How to add third level of ticks in python matplotlib

后端 未结 1 582
不思量自难忘°
不思量自难忘° 2021-02-20 09:38

Matplotlib axes have Major and Minor ticks. How do I add a third level of tick below Minor?

For example

import matplotlib.pyplot as plt
import numpy as n         


        
1条回答
  •  别那么骄傲
    2021-02-20 10:10

    As I stated that you can achieve what you want by deriving from some key classes, I decided to do so (but as I said, it's probably not worth the effort). Anyway, here is what I've got:

    from matplotlib import pyplot as plt
    from matplotlib import axes as maxes
    from matplotlib import axis as maxis
    import matplotlib.ticker as mticker
    import matplotlib.cbook as cbook
    from matplotlib.projections import register_projection
    
    
    from matplotlib import ticker
    import numpy as np
    
    class SubMinorXAxis(maxis.XAxis):
        def __init__(self,*args,**kwargs):
            self.subminor = maxis.Ticker()
            self.subminorTicks = []
            self._subminor_tick_kw = dict()
    
            super(SubMinorXAxis,self).__init__(*args,**kwargs)
    
    
        def reset_ticks(self):
            cbook.popall(self.subminorTicks)
            ##self.subminorTicks.extend([self._get_tick(major=False)])
            self.subminorTicks.extend([maxis.XTick(self.axes, 0, '', major=False, **self._subminor_tick_kw)])
            self._lastNumSubminorTicks = 1
            super(SubMinorXAxis,self).reset_ticks()
    
    
        def set_subminor_locator(self, locator):
            """
            Set the locator of the subminor ticker
    
            ACCEPTS: a :class:`~matplotlib.ticker.Locator` instance
            """
            self.isDefault_minloc = False
            self.subminor.locator = locator
            locator.set_axis(self)
            self.stale = True
    
    
        def set_subminor_formatter(self, formatter):
            """
            Set the formatter of the subminor ticker
    
            ACCEPTS: A :class:`~matplotlib.ticker.Formatter` instance
            """
            self.isDefault_minfmt = False
            self.subminor.formatter = formatter
            formatter.set_axis(self)
            self.stale = True
    
    
        def get_subminor_ticks(self, numticks=None):
            'get the subminor tick instances; grow as necessary'
            if numticks is None:
                numticks = len(self.get_subminor_locator()())
    
            if len(self.subminorTicks) < numticks:
                # update the new tick label properties from the old
                for i in range(numticks - len(self.subminorTicks)):
                    ##tick = self._get_tick(major=False)
                    tick = maxis.XTick(self.axes, 0, '', major=False, **self._subminor_tick_kw)
                    self.subminorTicks.append(tick)
    
            if self._lastNumSubminorTicks < numticks:
                protoTick = self.subminorTicks[0]
                for i in range(self._lastNumSubminorTicks, len(self.subminorTicks)):
                    tick = self.subminorTicks[i]
                    tick.gridOn = False
                    self._copy_tick_props(protoTick, tick)
    
            self._lastNumSubminorTicks = numticks
            ticks = self.subminorTicks[:numticks]
    
            return ticks
    
        def set_tick_params(self, which='major', reset=False, **kwargs):
            if which == 'subminor':
                kwtrans = self._translate_tick_kw(kwargs, to_init_kw=True)
                if reset:
                    self.reset_ticks()
                    self._subminor_tick_kw.clear()
                self._subminor_tick_kw.update(kwtrans)
    
                for tick in self.subminorTicks:
                    tick._apply_params(**self._subminor_tick_kw)
            else:
                super(SubMinorXAxis, self).set_tick_params(which=which, reset=reset, **kwargs)
    
        def cla(self):
            'clear the current axis'
            self.set_subminor_locator(mticker.NullLocator())
            self.set_subminor_formatter(mticker.NullFormatter())
    
            super(SubMinorXAxis,self).cla()
    
    
        def iter_ticks(self):
            """
            Iterate through all of the major and minor ticks.
            ...and through the subminors
            """
            majorLocs = self.major.locator()
            majorTicks = self.get_major_ticks(len(majorLocs))
            self.major.formatter.set_locs(majorLocs)
            majorLabels = [self.major.formatter(val, i)
                           for i, val in enumerate(majorLocs)]
    
            minorLocs = self.minor.locator()
            minorTicks = self.get_minor_ticks(len(minorLocs))
            self.minor.formatter.set_locs(minorLocs)
            minorLabels = [self.minor.formatter(val, i)
                           for i, val in enumerate(minorLocs)]
    
            subminorLocs = self.subminor.locator()
            subminorTicks = self.get_subminor_ticks(len(subminorLocs))
            self.subminor.formatter.set_locs(subminorLocs)
            subminorLabels = [self.subminor.formatter(val, i)
                           for i, val in enumerate(subminorLocs)]
    
            major_minor = [
                (majorTicks, majorLocs, majorLabels),
                (minorTicks, minorLocs, minorLabels),
                (subminorTicks, subminorLocs, subminorLabels),
            ]
    
            for group in major_minor:
                for tick in zip(*group):
                    yield tick
    
    
    class SubMinorAxes(maxes.Axes):
        name = 'subminor'
    
        def _init_axis(self):
            self.xaxis = SubMinorXAxis(self)
            self.spines['top'].register_axis(self.xaxis)
            self.spines['bottom'].register_axis(self.xaxis)
            self.yaxis = maxis.YAxis(self)
            self.spines['left'].register_axis(self.yaxis)
            self.spines['right'].register_axis(self.yaxis)
    
    register_projection(SubMinorAxes)
    
    
    
    
    if __name__ == '__main__':
        fig = plt.figure()
        ax = fig.add_subplot(111,projection = 'subminor')
    
        t = np.arange(0.0, 100.0, 0.1)
        s = np.sin(0.1*np.pi*t)*np.exp(-t*0.01)
    
        majors = np.linspace(0, 100, 6)
        minors = np.linspace(0, 100, 11)
        thirds = np.linspace(0, 100, 101)
    
        ax.plot(t, s)
    
        ax.xaxis.set_ticks_position('bottom')
    
        ax.xaxis.set_major_locator(ticker.FixedLocator(majors))
        ax.xaxis.set_minor_locator(ticker.FixedLocator(minors))
        ax.xaxis.set_subminor_locator(ticker.FixedLocator(thirds))
    
        ##some things in set_tick_params are not being set correctly
        ##by default. For instance 'top=False' must be stated
        ##explicitly
        ax.tick_params(which='subminor', length=2, top=False)
        ax.tick_params(which='minor', length=4)
        ax.tick_params(which='major', length=6)
        ax.grid(which='both',axis='x',linestyle='--')
    
        plt.show()
    

    It's not perfect, but for the use case you provided it's working fine. I drew some ideas from this matplotlib example and by going through the source codes directly. The result looks like this:

    I tested the code on both Python 2.7 and Python 3.5.

    EDIT:

    I noticed that the subminor gridlines would always be drawn if the grid is turned on (while I had intended for it not to be drawn at all). I rectified this in the code above, i.e. the subminor ticks should never produce grid lines. If gridlines should be implemented properly, some more work will be needed.

    0 讨论(0)
提交回复
热议问题