2017-07-30 20 views
8

Osie Matplotlib mają tyknięcia Major i Minor. Jak dodać trzeci poziom zaznaczenia poniżej mniejszego?Jak dodać trzeci poziom tyknięć w python matplotlib

Na przykład

import matplotlib.pyplot as plt 
import numpy as np 
import matplotlib.ticker 

t = np.arange(0.0, 100.0, 0.1) 
s = np.sin(0.1*np.pi*t)*np.exp(-t*0.01) 

fig, ax = plt.subplots() 
plt.plot(t, s) 

ax1 = ax.twiny() 
ax1.plot(t, s) 

ax1.xaxis.set_ticks_position('bottom') 

majors = np.linspace(0, 100, 6) 
minors = np.linspace(0, 100, 11) 
thirds = np.linspace(0, 100, 101) 

ax.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(majors)) 
ax.xaxis.set_minor_locator(matplotlib.ticker.FixedLocator(minors)) 
ax1.xaxis.set_major_locator(matplotlib.ticker.FixedLocator([])) 
ax1.xaxis.set_minor_locator(matplotlib.ticker.FixedLocator(thirds)) 
ax1.tick_params(which='minor', length=2) 
ax.tick_params(which='minor', length=4) 
ax.tick_params(which='major', length=6) 
ax.grid(which='both',axis='x',linestyle='--') 

plt.axhline(color='gray') 

plt.show() 

daje efekt chcę używając partnerskich x-osie.

Third level ticks

Czy istnieje lepszy sposób?

+1

Ponieważ matplotlib dostarcza tylko duże i małe znaczniki, rozwiązanie, które uznałeś za dobre. (Nie musisz jednak nanieść danych na osie bliźniacze). Nie sądzę, że istnieje "lepszy sposób". – ImportanceOfBeingErnest

+0

Możesz napisać nową klasę osi wywodzącą się z 'Osi', aby zautomatyzować to, co chcesz, ale widząc, jak mało linii kodu wymaga twoje rozwiązanie, prawdopodobnie nie jest to warte problemów. –

+0

Nie dostaję tyknięć trzeciego poziomu, jeśli nie dodaję "ax1.plot (t, s)". Czy istnieje inny sposób koordynacji dwóch osi? – pheon

Odpowiedz

1

Ponieważ stwierdziłem, że możesz osiągnąć to, co chcesz, wywodząc się z niektórych kluczowych klas, zdecydowałem się to zrobić (ale jak powiedziałem, prawdopodobnie nie jest to warte wysiłku). Tak czy inaczej, oto, co mam:

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

Nie jest idealny, ale w przypadku użycia pod warunkiem, że działa dobrze. Narysowałem kilka pomysłów od this matplotlib example i bezpośrednio przechodząc przez kody źródłowe. Wynik wygląda tak:

sub-minor ticks on x-axis

Przetestowałem kod zarówno Python 2.7 i Python 3.5.

EDIT:

Zauważyłem, że subminor linie siatki zawsze należy wyciągnąć jeśli siatka jest włączony (podczas gdy ja przeznaczony dla nie to należy wyciągnąć w ogóle). Poprawiłem to w powyższym kodzie, tzn. Tyknięcia subminor nigdy nie powinny tworzyć linii siatki. Jeśli linie siatki powinny być właściwie implementowane, potrzeba więcej pracy.

Powiązane problemy