How do I align gridlines for two y-axis scales using Matplotlib?

前端 未结 7 1511
遇见更好的自我
遇见更好的自我 2020-11-29 00:16

I\'m plotting two datasets with different units on the y-axis. Is there a way to make the ticks and gridlines aligned on both y-axes?

The first image shows what I ge

相关标签:
7条回答
  • 2020-11-29 00:50

    I am not sure if this is the prettiest way to do it, but it does fix it with one line:

    import matplotlib.pyplot as plt
    import seaborn as sns
    import numpy as np
    import pandas as pd
    
    np.random.seed(0)
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    ax1.plot(pd.Series(np.random.uniform(0, 1, size=10)))
    ax2 = ax1.twinx()
    ax2.plot(pd.Series(np.random.uniform(10, 20, size=10)), color='r')
    
    # ADD THIS LINE
    ax2.set_yticks(np.linspace(ax2.get_yticks()[0], ax2.get_yticks()[-1], len(ax1.get_yticks())))
    
    plt.show()
    
    0 讨论(0)
  • 2020-11-29 00:51

    This code will ensure that grids from both axes align to each other, without having to hide gridlines from either set. In this example, it allows you to match whichever has the finer grid lines. This builds off of the idea from @Leo. Hope it helps!

    import matplotlib.pyplot as plt
    import seaborn as sns
    import numpy as np
    import pandas as pd
    
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    ax1.plot(pd.Series(np.random.uniform(0,1,size=10)))
    ax2 = ax1.twinx()
    ax2.plot(pd.Series(np.random.uniform(10,20,size=10)),color='r')
    ax2.grid(None)
    
    # Determine which plot has finer grid. Set pointers accordingly
    l1 = len(ax1.get_yticks())
    l2 = len(ax2.get_yticks())
    if l1 > l2:
      a = ax1
      b = ax2
      l = l1
    else:
      a = ax2
      b = ax1
      l = l2
    
    # Respace grid of 'b' axis to match 'a' axis
    b_ticks = np.linspace(b.get_yticks()[0],b.get_yticks()[-1],l)
    b.set_yticks(b_ticks)
    
    plt.show()
    
    0 讨论(0)
  • 2020-11-29 00:53

    If you're using axis labels, Leo's solution can push them off the side, due to the precision of the numbers in the ticks.

    So in addition to something like Leo's solution (repeated here),

    ax2.set_yticks(np.linspace(ax2.get_yticks()[0],ax2.get_yticks()[-1],len(ax1.get_yticks())))
    

    you can use the autolayout setting, as mentioned in this answer; e.g., earlier in your script you can update rcParams:

    from matplotlib import rcParams
    rcParams.update({'figure.autolayout': True})
    

    In a few test cases, this appears to produce the expected result, with both lined-up ticks and labels fully contained in the output.

    0 讨论(0)
  • 2020-11-29 00:53

    I had the same issue except this was for a secondary x axis. I solved by setting my secondary x axis equal to the limit of my primary axis.The example below is without setting the limit of the second axis equal to the first:ax2 = ax.twiny()

    Once I set the limit of the second axis equal to the first ax2.set_xlim(ax.get_xlim()) here is my result:

    0 讨论(0)
  • 2020-11-29 00:54

    I wrote this function that takes Matplotlib axes objects ax1, ax2, and floats minresax1 minresax2:

    def align_y_axis(ax1, ax2, minresax1, minresax2):
        """ Sets tick marks of twinx axes to line up with 7 total tick marks
    
        ax1 and ax2 are matplotlib axes
        Spacing between tick marks will be a factor of minresax1 and minresax2"""
    
        ax1ylims = ax1.get_ybound()
        ax2ylims = ax2.get_ybound()
        ax1factor = minresax1 * 6
        ax2factor = minresax2 * 6
        ax1.set_yticks(np.linspace(ax1ylims[0],
                                   ax1ylims[1]+(ax1factor -
                                   (ax1ylims[1]-ax1ylims[0]) % ax1factor) %
                                   ax1factor,
                                   7))
        ax2.set_yticks(np.linspace(ax2ylims[0],
                                   ax2ylims[1]+(ax2factor -
                                   (ax2ylims[1]-ax2ylims[0]) % ax2factor) %
                                   ax2factor,
                                   7))
    

    It calculates and sets the ticks such that there are seven ticks. The lowest tick corresponds to the current lowest tick and increases the highest tick such that the separation between each tick is integer multiples of minrexax1 or minrexax2.

    To make it general, you can set the total number of ticks you want by changing ever 7 you see to the total number of ticks, and change 6 to the total number of ticks minus 1.

    I put a pull request in to incorporate some this into matplotlib.ticker.LinearLocator:

    https://github.com/matplotlib/matplotlib/issues/6142

    In the future (Matplotlib 2.0 perhaps?), try:

    import matplotlib.ticker
    nticks = 11
    ax1.yaxis.set_major_locator(matplotlib.ticker.LinearLocator(nticks))
    ax2.yaxis.set_major_locator(matplotlib.ticker.LinearLocator(nticks))
    

    That should just work and choose convenient ticks for both y-axes.

    0 讨论(0)
  • 2020-11-29 00:55

    I could solve it by deactivating ax.grid(None) in one of the grid`s axes:

    import matplotlib.pyplot as plt
    import seaborn as sns
    import numpy as np
    import pandas as pd
    
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    ax1.plot(pd.Series(np.random.uniform(0, 1, size=10)))
    ax2 = ax1.twinx()
    ax2.plot(pd.Series(np.random.uniform(10, 20, size=10)), color='r')
    ax2.grid(None)
    
    plt.show()
    

    Figure Result

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