pythonmatplotlibgraphfigure

Change number of ticks labeled using LogFormatter in Matplotlib


In Matplotlib's documentation for matplotlib.ticker.LogFormatter it is stated that with the parameter minor_thresholds one can control the labeling of minor ticks:

If labelOnlyBase is False, these two numbers control the labeling of ticks that are not at integer powers of base; normally these are the minor ticks. The controlling parameter is the log of the axis data range. In the typical case where base is 10 it is the number of decades spanned by the axis, so we can call it 'numdec'. If numdec <= all, all minor ticks will be labeled. If all < numdec <= subset, then only a subset of minor ticks will be labeled, so as to avoid crowding. If numdec > subset then no minor ticks will be labeled.

I would like to change the number of automatically labeled ticks when all < numdec <= subset, because sometimes it does not fully avoid crowding, for example this figure

Figure

was generated using LogFormatterSciNotation, which has LogFormatter as base:

import matplotlib.ticker as ticker

ax.set_minor_formatter(ticker.LogFormatterSciNotation(base=10, minor_thresholds=(1, 0.4)))

In this case, numdec = 0.7 and we are in the case all < numdec <= subset where only a subset of minor ticks are labeled, for instance three.

Is there a way to change this number of labeled ticks, without specifying their values?


Solution

  • This appears to be hardwired, to reduce the number of ticks by a factor of 2, here.

    Note that this method (.set_locs()) is essentially setting the ._sublabels attribute of the Formatter instance. This method is called automatically, so we can't just set ._sublabels ourselves to overwrite them, but we can rewrite the .set_locs method to modify its behaviour.

    In this minimal example, I have assumed that you will only do this when all < numdec <= subset is True, so I haven't accounted for the other cases. For a more complete solution you will probably want to do this more thoroughly.

    import matplotlib.pyplot as plt
    import matplotlib.ticker as ticker
    import numpy as np
    
    # Set up a simple plot to demonstrate the behaviour
    fig, ax = plt.subplots()
    ax.set_xscale('log')
    ax.set_xlim(.1, .8)
    fmt = ticker.LogFormatterSciNotation(base=10, minor_thresholds=(1, 0.4))
    
    def my_locs(self, locs=None):
        '''
        Redefine the set_locs method of the formatter
        Here I've used the "locs" parameter to control the number
        of tick labels; in the existing method, locs is not used. 
        '''
        b = self._base
        c = np.geomspace(1, b, int(b)//int(locs) + 1)
        self._sublabels = set(np.round(c))
    
    ## Tune the number of labels you want with the locs value.
    ## Higher numbers = fewer labels
    locs = 3
    fmt.set_locs = lambda x: my_locs(fmt, locs)
    
    ax.xaxis.set_minor_formatter(fmt)
    

    With locs=2 (equivalent to the default value)

    enter image description here

    With locs=3

    enter image description here

    With locs=4

    enter image description here