-
Notifications
You must be signed in to change notification settings - Fork 981
Description
@at2software recently asked about support for logarithmic scaling in ScottPlot. While much of ScottPlot is hard-coded to assume linear axes, it may be possible to create convincing scatter plots of log-scaled data. This issue tracks progress toward exploring these options now that the library has matured since it was last considered. Related: #29, #41, #207, #412
Present Capabilities
ScottPlot is really close to being able to create convincing log-scaled scatter plots already. The only giveaway is that the connecting lines between scatter points are straight and not curved. EDIT: this is pretty close to what the same code looks like in Python
double[] xs = { 1, 2, 3, 4, 5 };
double[] ys = { 10, 2_000, 50_000, 1_000_000, 1_500_000 };
var plt = new ScottPlot.Plot(500, 300);
// Plot the log of the Ys
double[] logYs = ys.Select(y => Math.Log10(y)).ToArray();
plt.AddScatter(xs, logYs);
// Use a custom tick formatter to label tick marks as the antilog of their position
Func<double, string> tickLabeler = (y) => Math.Pow(10, y).ToString("N0");
plt.YAxis.TickLabelFormat(tickLabeler);
// Use log-spaced tick marks and grid lines to make it more convincing
plt.YAxis.MinorGrid(enable: true);
plt.YAxis.MinorLogScale(true);
// Set the axis limits manually to ensure edges terminate at a nice locations in log space
plt.SetAxisLimits(.5, 5.5, 0, Math.Log10(10_000_000));
plt.SaveFig("log.png");Python/Matplotlib Example
import matplotlib.pyplot as plt
xs = [ 1, 2, 3, 4, 5 ]
ys = [ 10, 2_000, 50_000, 1_000_000, 1_500_000 ]
plt.semilogy()
plt.plot(xs, ys, '.-', ms=10)
plt.grid(True, 'major', alpha=1)
plt.grid(True, 'minor', alpha=.2)
plt.show()outdated details about connecting lines
## Special Log-Scaled Scatter Plot Type?Could a custom plot type be made that replaces these straight lines with a function that dynamically calculates the log-scaled Y pixel for each X position of a connecting line between each pair of points in a scatter plot? We could borrow some logic from function plots:
ScottPlot/src/ScottPlot/Plottable/FunctionPlot.cs
Lines 54 to 81 in 9b4aaa2
| public void Render(PlotDimensions dims, Bitmap bmp, bool lowQuality = false) | |
| { | |
| List<double> xList = new List<double>(); | |
| List<double> yList = new List<double>(); | |
| PointCount = (int)dims.DataWidth; | |
| for (int columnIndex = 0; columnIndex < dims.DataWidth; columnIndex++) | |
| { | |
| double x = columnIndex * dims.UnitsPerPxX + dims.XMin; | |
| try | |
| { | |
| double? y = Function(x); | |
| if (y is null) | |
| throw new NoNullAllowedException(); | |
| if (double.IsNaN(y.Value) || double.IsInfinity(y.Value)) | |
| throw new ArithmeticException("not a real number"); | |
| xList.Add(x); | |
| yList.Add(y.Value); | |
| } | |
| catch (Exception e) //Domain error, such log(-1) or 1/0 | |
| { | |
| Debug.WriteLine($"Y({x}) failed because {e}"); | |
| continue; | |
| } | |
| } |
I'm inclined to experiment in a new plot type to allow free experimentation (with breaking API changes along the way) until this idea is fleshed-out, then we could consider merging it into the existing ScatterPlot type.

