How to place minor ticks on symlog scale?

Viewed 6761

I use the symlog scale of matplotlib to cover a large ranges of parameters extending both into positive and negative direction. Unfortunately, the symlog-scale is not very intuitive and probably also not very commonly used. Therefore, I'd like to make the used scaling more obvious by placing minor ticks between the major ticks. On the log part of the scale, I want to place ticks at [2,3,…,9]*10^e where e is the nearby major tick. Additionally, the range between 0 and 0.1 should be covered with evenly placed minor ticks, which would be 0.01 apart. I tried using the matplotlib.ticker API to arrive at such ticks using the following code:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import LogLocator, AutoLocator

x = np.linspace(-5, 5, 100)
y = x

plt.plot(x, y)
plt.yscale('symlog', linthreshy=1e-1)

yaxis = plt.gca().yaxis
yaxis.set_minor_locator(LogLocator(subs=np.arange(2, 10)))

plt.show()

Unfortunately, this does not produce what I want:

enter image description here

Note that there are way to many minor ticks around 0, which are probably due to the LogLocator. Furthermore, there are no minor ticks on the negative axis.

No minor ticks appear if I use and AutoLocator instead. The AutoMinorLocator does only support evenly scaled axes. My question thus is how to achieve the desired tick placement?

4 Answers

Just some additions to David's answer, addressing what also Matt highlighted. Note that class matplotlib.scale.SymmetricalLogScale now also has a sub argument for this, although doesn't really do outside and within the threshold. Please comment if you have suggestions and corrections.


        majorlocs = self.axis.get_majorticklocs()
        # my changes to previous solution
        # this adds one majortickloc below and above the axis range
        # to extend the minor ticks outside the range of majorticklocs
        # bottom of the axis (low values)
        first_major = majorlocs[0]
        if first_major == 0:
            outrange_first = -self.linthresh
        else:
            outrange_first = first_major * float(10) ** (- np.sign(first_major))
        # top of the axis (high values)
        last_major = majorlocs[-1]
        if last_major == 0:
            outrange_last = self.linthresh
        else:
            outrange_last = last_major * float(10) ** (np.sign(last_major))
        majorlocs = np.concatenate(([outrange_first], majorlocs, [outrange_last]))

then countinue with David'answer...

Related