将两个y轴对齐到零点附近

13
我有一个带有共享x轴的双y轴图,我想让它们在y=0处对齐,这样我就可以画一条水平线来突出显示零刻度。目前这两个轴没有对齐,我不得不画两条线,这太糟糕了。我该如何实现这个目标?

enter image description here

4个回答

9

假设您使用共享轴创建了这些图表,只需修改 y 轴的范围以使其居中于零点或在两个图表中具有相似的偏移倍增器(即为两个图表设置 ax.set_ylim(-6,6))。以下代码为示例。

from matplotlib import pyplot as plt
import numpy as np

#Create some Fake Data
x =np.arange(-10,10)
y = x+np.random.rand(20)
y2 = 0.5*x-3.*np.random.rand(20)


#Figure
fig = plt.figure(figsize=(12,6))

#First subplot with zero line not even
ax1 = plt.subplot(121)
ax2 = ax1.twinx()

ax1.plot(x,y,c='r')
ax2.plot(x,y2,c='b')

ax1.axhline(0)

#Second Subplot with zero line the same on both axes
ax3 = plt.subplot(122)
ax4 = ax3.twinx()

ax3.plot(x,y,c='r')
ax4.plot(x,y2,c='b')

ax3.axhline(0)

#If you set your limits on both sides to have the same interval you will get the same zero line
ax3.set_ylim(-10,10)
ax4.set_ylim(-6,6)

plt.show()

enter image description here


所以我们需要手动定义轴范围,以确保它们成比例。没有可能保持自动比例尺,但将零点链接起来吗? - mins
肯定有自动化的方法,但是当你在matplotlib中创建子图轴时,边界是根据值的范围设置的。因此,自动化将在数据上完成,并找到保持零线相同的最佳边缘。 - BenT

7

我曾经遇到过同样的问题,我所做的就是根据最小和最大限制之间的比率来改变y轴的范围。如果你设置y轴的比率相同,那么零点应该也会相同。

fig, ax1 = plt.subplots()

ax1.plot(...)     # Plot first data set
ax2 = ax1.twinx()
ax2.plot(...)     # Plot second data set

ax1_ylims = ax1.axes.get_ylim()           # Find y-axis limits set by the plotter
ax1_yratio = ax1_ylims[0] / ax1_ylims[1]  # Calculate ratio of lowest limit to highest limit

ax2_ylims = ax2.axes.get_ylim()           # Find y-axis limits set by the plotter
ax2_yratio = ax2_ylims[0] / ax2_ylims[1]  # Calculate ratio of lowest limit to highest limit


# If the plot limits ratio of plot 1 is smaller than plot 2, the first data set has
# a wider range range than the second data set. Calculate a new low limit for the
# second data set to obtain a similar ratio to the first data set.
# Else, do it the other way around

if ax1_yratio < ax2_yratio: 
    ax2.set_ylim(bottom = ax2_ylims[1]*ax1_yratio)
else:
    ax1.set_ylim(bottom = ax1_ylims[1]*ax2_yratio)

plt.tight_layout()
plt.show()

这是我的第一个回答,希望它足够并且没问题。


这只适用于正y轴值,对吗? - KobeJohn
1
我认为需要一些逻辑,是的。我需要测试和审查。有机会时,我会尝试更新。我认为以上内容仍应该给出相同的零点,但我认为可能存在数据显示不正确的情况,是的。 - KobusNell
谢谢。我在传统搜索中找不到这个答案,但是phind指向了它作为答案的来源。你的代码现在成为了人类知识的广阔体系的一部分 :) - IanS

5

我发现了这个优秀的库,可以对齐轴并保持自动缩放:https://github.com/ryutok/mpl_axes_aligner

安装

pip install mpl-axes-aligner

用法

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

x = np.arange(0.0, 30, 0.1)
y1 = 0.1 * x * np.sin(x)
y2 = 0.001*x**3 - 0.03*x**2 + 0.12*x

fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()

ax1.plot(x, y1, color='blue', label='Plot 1')
ax2.plot(x, y2, color='red', label='Plot 2')

# Align y = 0 of ax1 and ax2 with the center of figure.
mpl_axes_aligner.align.yaxes(ax1, 0, ax2, 0, 0.5)

plt.show()

输出

输出图

制作者

该软件包由ryotuk开发,上面的使用示例来自他软件包的文档。


我尝试使用这个,但出现了错误 AttributeError: module 'mpl_axes_aligner' has no attribute 'align' - Dean
根据这个问题,您应该更新mpl_axes_aligner - abrac
1
谢谢@abrac,现在可以工作了。 - Dean

1

这里有另一种方式,我认为更加灵活。

import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt

def align_zeros(axes):

    ylims_current = {}   #  Current ylims
    ylims_mod     = {}   #  Modified ylims
    deltas        = {}   #  ymax - ymin for ylims_current
    ratios        = {}   #  ratio of the zero point within deltas

    for ax in axes:
        ylims_current[ax] = list(ax.get_ylim())
                        # Need to convert a tuple to a list to manipulate elements.
        deltas[ax]        = ylims_current[ax][1] - ylims_current[ax][0]
        ratios[ax]        = -ylims_current[ax][0]/deltas[ax]
    
    for ax in axes:      # Loop through all axes to ensure each ax fits in others.
        ylims_mod[ax]     = [np.nan,np.nan]   # Construct a blank list
        ylims_mod[ax][1]  = max(deltas[ax] * (1-np.array(list(ratios.values()))))
                        # Choose the max value among (delta for ax)*(1-ratios),
                        # and apply it to ymax for ax
        ylims_mod[ax][0]  = min(-deltas[ax] * np.array(list(ratios.values())))
                        # Do the same for ymin
        ax.set_ylim(tuple(ylims_mod[ax]))

x = np.array(range(1,11))
y1 = 5*x-10
y2 = -10*x+5

fig = plt.figure()
ax1 = fig.add_subplot(111)

ax1.plot(x,y1,'r',label='y1')
ax1.set_ylabel('y1')

ax2 = ax1.twinx()
ax2.plot(x,y2,'g',label='y2')
ax2.set_ylabel('y2')

align_zeros([ax1,ax2])

ax1.legend(loc='upper left')
ax2.legend(loc='upper right')

plt.show()

在此输入图片描述


是的,这很有道理,应该比我的更耐用。 - KobusNell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接