Skip to content

Ticks: improve positions of log-scaled minor ticks #1386

@swharden

Description

@swharden

@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");

image

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()

log

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:

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions