Skip to content

[ENH]: minor ticks for symlog norms (with integer base) #25994

Open
@neutrinoceros

Description

@neutrinoceros

Problem

As of matplotlib 3.7.1, matplotlib.colors.SymLogNorm doesn't produce any minor tick (that doesn't overlap with major ticks). I understand that default minor tick locations are absolutely not obvious in the general case and, where the log base can be any real number, however, I think there's a case to be had regarding the special (and very useful) case of integer log bases (10 and 2 are probably among the most useful bases all domains considered), in which the interval between two major ticks n and n+1 can be divided by the interval between ticks n-1 and n (where n+1 is farthest from 0).

If fact matplotlib.colors.LogNorm uses log10 and produces minorticks by default. The difference in behaviour is perhaps best illustrated in a case where everything else is identical

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import SymLogNorm, LogNorm

prng = np.random.RandomState(0)
data = 50 * (prng.random_sample((10, 10)))

fig, axs = plt.subplots(ncols=2, figsize=(10,3))

for ax, norm in zip(axs, [LogNorm(), SymLogNorm(base=10, linthresh=1)]):
    im = ax.imshow(data, cmap="viridis_r", norm=norm)
    cb = fig.colorbar(im, ax=ax)
    cb.minorticks_on()

sfile = "/tmp/test.png"
print(f"saving to {sfile}")
fig.savefig(sfile)

test

Here's my attempt to reproduce what's happening internally (within cb.minorticks_on()) with a more minimal example

import matplotlib.ticker as mticker

log_t = mticker.LogLocator(base=10, subs=None)
symlog_t = mticker.SymmetricalLogLocator(linthresh=1, base=10, subs=None)

print(log_t.tick_values(vmin=1, vmax=100))
print(symlog_t.tick_values(vmin=1, vmax=100))

outputs

[2.e-01 3.e-01 4.e-01 5.e-01 6.e-01 7.e-01 8.e-01 9.e-01 2.e+00 3.e+00
 4.e+00 5.e+00 6.e+00 7.e+00 8.e+00 9.e+00 2.e+01 3.e+01 4.e+01 5.e+01
 6.e+01 7.e+01 8.e+01 9.e+01 2.e+02 3.e+02 4.e+02 5.e+02 6.e+02 7.e+02
 8.e+02 9.e+02 2.e+03 3.e+03 4.e+03 5.e+03 6.e+03 7.e+03 8.e+03 9.e+03]
[  1.  10. 100.]

I think that it is reasonable to expect SymmetricalLogLocator and LogLocator to produce identica ticks in this case (for values above linthresh), and this behaviour could be generalised to any integer base.

Proposed solution

At the moment I don't have a working patch for this. What I do not understand is how major ticks are sorted out from minor ticks in the case LogLocator. With a better understanding of the internals I'm willing to bet that the behaviour I expect should be easy to implement, and I'm more than willing to contribute it, but I may need some guidance.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions