matplotlib 如何对齐两个y轴刻度的网格线?

jljoyd4f  于 2023-05-18  发布在  其他
关注(0)|答案(8)|浏览(225)

我绘制了两个数据集,y轴上的单位不同。有没有办法使刻度线和网格线在两个y轴上对齐?
第一张图片显示了我得到的东西,第二张图片显示了我想要得到的东西。
这是我用来绘图的代码:

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

ltskdhd1

ltskdhd11#

我不确定这是否是最好的方法,但它确实用一行来解决它:

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

7ivaypg92#

我可以通过在网格轴中停用ax.grid(None)来解决这个问题:

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

fcipmucu

fcipmucu3#

我写了这个函数,它接受Matplotlib轴对象ax1,ax2,并浮动minresax1minresax2:

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

它计算并设置刻度,以便有七个刻度。最低刻度对应于当前最低刻度,并增加最高刻度,使得每个刻度之间的间隔是minrexax1或minrexax2的整数倍。
为了使其通用,您可以通过将您看到的任何7更改为滴答总数,并将6更改为滴答总数减1来设置所需的滴答总数。
我在matplotlib.ticer.LinearLocator中加入了一个pull请求:
https://github.com/matplotlib/matplotlib/issues/6142
在未来(Matplotlib 2.0也许?),试试:

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

这应该可以工作,并为两个y轴选择方便的刻度。

cwdobuhd

cwdobuhd4#

我创建了一个方法来对齐多个y轴的刻度线(可能超过2),在不同的轴上可能有不同的比例。
下面是示例图:

有3个y轴,左边一个蓝色,右边一个绿色和一个红色。将3条曲线绘制在y轴上,并绘制相应颜色。请注意,它们都具有非常不同的数量级。

*左图:没有对齐。
*中间图:在(近似)每个y轴的下限对齐。
*右图:以指定值对齐:0表示蓝色,2.2*1e8表示红色,44表示绿色。这些都是任意选择的。

我所做的是将每个y数组缩放到1-100的范围内,然后将所有缩放的y值合并到一个数组中,使用MaxNLocator从这个数组中创建一组新的刻度。然后,使用相应的缩放因子对这组新的刻度进行缩放,以获得每个轴的新刻度。如果需要一些特定的对齐,则y阵列在缩放之前移位,并且在缩放之后向后移位。
完整的代码在这里(关键函数是alignYaxes()):

import matplotlib.pyplot as plt
import numpy as np

def make_patch_spines_invisible(ax):
    '''Used for creating a 2nd twin-x axis on the right/left

    E.g.
        fig, ax=plt.subplots()
        ax.plot(x, y)
        tax1=ax.twinx()
        tax1.plot(x, y1)
        tax2=ax.twinx()
        tax2.spines['right'].set_position(('axes',1.09))
        make_patch_spines_invisible(tax2)
        tax2.spines['right'].set_visible(True)
        tax2.plot(x, y2)
    '''

    ax.set_frame_on(True)
    ax.patch.set_visible(False)
    for sp in ax.spines.values():
        sp.set_visible(False)

def alignYaxes(axes, align_values=None):
    '''Align the ticks of multiple y axes

    Args:
        axes (list): list of axes objects whose yaxis ticks are to be aligned.
    Keyword Args:
        align_values (None or list/tuple): if not None, should be a list/tuple
            of floats with same length as <axes>. Values in <align_values>
            define where the corresponding axes should be aligned up. E.g.
            [0, 100, -22.5] means the 0 in axes[0], 100 in axes[1] and -22.5
            in axes[2] would be aligned up. If None, align (approximately)
            the lowest ticks in all axes.
    Returns:
        new_ticks (list): a list of new ticks for each axis in <axes>.

        A new sets of ticks are computed for each axis in <axes> but with equal
        length.
    '''
    from matplotlib.pyplot import MaxNLocator

    nax=len(axes)
    ticks=[aii.get_yticks() for aii in axes]
    if align_values is None:
        aligns=[ticks[ii][0] for ii in range(nax)]
    else:
        if len(align_values) != nax:
            raise Exception("Length of <axes> doesn't equal that of <align_values>.")
        aligns=align_values

    bounds=[aii.get_ylim() for aii in axes]

    # align at some points
    ticks_align=[ticks[ii]-aligns[ii] for ii in range(nax)]

    # scale the range to 1-100
    ranges=[tii[-1]-tii[0] for tii in ticks]
    lgs=[-np.log10(rii)+2. for rii in ranges]
    igs=[np.floor(ii) for ii in lgs]
    log_ticks=[ticks_align[ii]*(10.**igs[ii]) for ii in range(nax)]

    # put all axes ticks into a single array, then compute new ticks for all
    comb_ticks=np.concatenate(log_ticks)
    comb_ticks.sort()
    locator=MaxNLocator(nbins='auto', steps=[1, 2, 2.5, 3, 4, 5, 8, 10])
    new_ticks=locator.tick_values(comb_ticks[0], comb_ticks[-1])
    new_ticks=[new_ticks/10.**igs[ii] for ii in range(nax)]
    new_ticks=[new_ticks[ii]+aligns[ii] for ii in range(nax)]

    # find the lower bound
    idx_l=0
    for i in range(len(new_ticks[0])):
        if any([new_ticks[jj][i] > bounds[jj][0] for jj in range(nax)]):
            idx_l=i-1
            break

    # find the upper bound
    idx_r=0
    for i in range(len(new_ticks[0])):
        if all([new_ticks[jj][i] > bounds[jj][1] for jj in range(nax)]):
            idx_r=i
            break

    # trim tick lists by bounds
    new_ticks=[tii[idx_l:idx_r+1] for tii in new_ticks]

    # set ticks for each axis
    for axii, tii in zip(axes, new_ticks):
        axii.set_yticks(tii)

    return new_ticks

def plotLines(x, y1, y2, y3, ax):

    ax.plot(x, y1, 'b-')
    ax.tick_params('y',colors='b')

    tax1=ax.twinx()
    tax1.plot(x, y2, 'r-')
    tax1.tick_params('y',colors='r')

    tax2=ax.twinx()
    tax2.spines['right'].set_position(('axes',1.2))
    make_patch_spines_invisible(tax2)
    tax2.spines['right'].set_visible(True)
    tax2.plot(x, y3, 'g-')
    tax2.tick_params('y',colors='g')

    ax.grid(True, axis='both')

    return ax, tax1, tax2

#-------------Main---------------------------------
if __name__=='__main__':

    # craft some data to plot
    x=np.arange(20)
    y1=np.sin(x)
    y2=x/1000+np.exp(x)
    y3=x+x**2/3.14

    figure=plt.figure(figsize=(12,4),dpi=100)

    ax1=figure.add_subplot(1, 3, 1)
    axes1=plotLines(x, y1, y2, y3, ax1)
    ax1.set_title('No alignment')

    ax2=figure.add_subplot(1, 3, 2)
    axes2=plotLines(x, y1, y2, y3, ax2)
    alignYaxes(axes2)
    ax2.set_title('Default alignment')

    ax3=figure.add_subplot(1, 3, 3)
    axes3=plotLines(x, y1, y2, y3, ax3)
    alignYaxes(axes3, [0, 2.2*1e8, 44])
    ax3.set_title('Specified alignment')

    figure.tight_layout()
    figure.show()
yc0p9oo0

yc0p9oo05#

此代码将确保两个轴的网格彼此对齐,而不必隐藏任何一个轴的网格线。在本例中,它允许您匹配具有更细网格线的任何一条。这是基于@Leo的想法。希望有帮助!

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

mefy6pfw6#

如果使用轴标签,Leo的解决方案可以push them off the side,这是由于刻度中数字的精确性。
所以除了类似Leo的解决方案(这里重复)之外,

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

您可以使用autolayout设置,如this answer中所述;例如,在脚本的前面,您可以更新rcParams

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

在一些测试用例中,这似乎产生了预期的结果,输出中完全包含了排列的刻度和标签。

rdrgkggo

rdrgkggo7#

我有同样的问题,除了这是一个次要的x轴。我通过设置第二个x轴等于主轴的极限来解决。下面的例子没有设置第二个x轴等于第一个x轴的极限:ax2 = ax.twiny()

一旦我将第二个轴的极限设置为等于第一个ax2.set_xlim(ax.get_xlim()),这里是我的结果:

k4aesqcs

k4aesqcs8#

1.固定两个轴的限制(从任意数字到任意数字)
1.将两个轴分成相同的n个部分

ax1.set_ylim(a,b)
ax1.set_yticks(np.linspace(a,b, n))

ax2.set_ylim(c,d)
ax2.set_yticks(np.linspace(c,d, n))

相关问题