不同y轴下的子图纵横比问题

9
我希望以下代码能够生成4个相同大小的子图,并且它们的x轴和y轴之间有一个由我设置的公共纵横比。参考下面的示例,我希望所有的子图看起来都像第一个子图(左上角)。目前的问题是y轴的大小与其最大值相关联。这是我想要避免的行为。
import matplotlib.pyplot as plt
import numpy as np

def main(): 

    fig = plt.figure(1, [5.5, 3])
    for i in range(1,5):
        fig.add_subplot(221+i-1, adjustable='box', aspect=1) 
        plt.plot(np.arange(0,(i)*4,i))

    plt.show()

if __name__ == "__main__": 
    main()

令人惊讶的是,matplotlib 默认情况下会生成正确的图像(如下图所示):
   import  matplotlib.pyplot as plt 
   import numpy as np 

   def main(): 
       fig = plt.figure(1, [5.5, 3]) 
       for i in range(1,5): 
           fig.add_subplot(221+i-1) 
            plt.plot(np.arange(0,(i)*4,i)) 
       plt.show() 

我只想补充一下,可以控制x轴和y轴长度之间的纵横比。以下是我所寻找的示例图:
3个回答

10

我无法从你的问题中完全理解你想要什么。

你是否希望所有的图表都具有相同的数据限制?

如果是这样,请使用共享轴(这里我使用了 subplots,但如果你想坚持使用 matlab 风格的代码,也可以避免使用它):

import matplotlib.pyplot as plt
import numpy as np

fig, axes = plt.subplots(nrows=2, ncols=2, sharey=True, sharex=True)
for i, ax in enumerate(axes.flat, start=1):
    ax.set(aspect=1)
    ax.plot(np.arange(0, i * 4, i))

plt.show()

在此输入图像描述

如果您想让它们共享坐标轴限制,但具有可调整的“box”(即非正方形坐标轴边界),请使用 adjustable = 'box-forced'

import matplotlib.pyplot as plt
import numpy as np

fig, axes = plt.subplots(nrows=2, ncols=2, sharey=True, sharex=True)
for i, ax in enumerate(axes.flat, start=1):
    ax.set(aspect=1, adjustable='box-forced', xticks=range(i))
    ax.plot(np.arange(0, i * 4, i))

plt.show()

在此输入图片描述


编辑:抱歉,我还有点困惑。您想要像这样的东西吗?

import matplotlib.pyplot as plt 
import numpy as np 

fig, axes = plt.subplots(nrows=2, ncols=2)
for i, ax in enumerate(axes.flat, start=1):
    ax.set(adjustable='datalim', aspect=1)
    ax.plot(np.arange(0, i * 4, i))

plt.show()

在此输入图片描述


好的,我终于明白了你的问题。我们两个人对“纵横比”这个词的理解完全不同。

在Matplotlib中,图的纵横比是指数据限制的相对比例尺。换句话说,如果图的纵横比为1,则斜率为1的线将呈现45度角。而你则认为纵横比适用于轴的轮廓而非轴上绘制的数据。

你只想要子图的轮廓是正方形的。(在这种情况下,它们都有由Matplotlib定义的不同的纵横比。)

在这种情况下,你需要一个正方形的图。还有其他方法,但制作一个正方形的图更简单。Matplotlib的坐标轴会填充与它们所在的图大小成比例的空间。

import matplotlib.pyplot as plt 
import numpy as np 

# The key here is the figsize (it needs to be square). The position and size of
# axes in matplotlib are defined relative to the size of the figure.
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(8,8))

for i, ax in enumerate(axes.flat, start=1):
    ax.plot(np.arange(0, i * 4, i))

# By default, subplots leave a bit of room for tick labels on the left.
# We'll remove it so that the axes are perfectly square.
fig.subplots_adjust(left=0.1)

plt.show()

在此输入图片描述


嗨,乔!感谢您对我的问题的关注。很抱歉造成了困惑。您提供的两个选项都没有完全符合我的要求。第一个选项最接近我的需求。如果每个子图都有自己的y轴限制,那就更完美了。这是Matplotlib自动完成的。例如,下面的代码可以实现我想要的效果,但我也想控制这些子图的纵横比(所有子图的一个纵横比)。 - user2077647
嗨,乔,快完成了!这很完美,只是我希望所有的子图的x轴限制都在0到3之间。我在原始问题中包含的那张图片就是我想要的一切,但是坐标轴之间的纵横比不正确。所以现在它大约是1/2,但我正在寻找1/1。 - user2077647
抱歉,我还是有些困惑...如果轴的限制或大小都没有改变,那么纵横比就不能保持不变。您似乎要求绘图具有完全相同的大小、相同的纵横比和相同的x轴限制,但具有不同的y轴限制。这是不可能的,根据定义。我想我可能误解了您的问题... - Joe Kington
如果你再看一下我附上的那张图片,它满足了我所描述的一切。唯一的问题是,我无法使所有这些子图像都成为正方形。希望我表达得更清楚了,再次感谢! - user2077647
啊,这就解释了!在matplotlib中,图的纵横比是指数据的x轴比例与y轴比例之比,而不涉及图的轮廓高度/宽度。你只想要正方形轮廓的子图吗?如果是这样,请参见编辑。 - Joe Kington
显示剩余5条评论

2
结合Joe Kington的答案和matplotlib中用于共享轴的正方形子图的新Python风格?以及另一个帖子(我不确定是否能够再次找到),我编写了一段代码,可精确设置盒子的比例为给定值。
让desired_box_ratioN表示盒子y和x边缘之间的期望比率。temp_inverse_axis_ratioN是当前绘图中x和y边缘之间的比率;由于'aspect'是y和x比例(而不是轴),我们需要将aspect设置为desired_box_ratioN * temp_inverse_axis_ratioN。
import matplotlib.pyplot as plt
import numpy as np

fig, axes = plt.subplots(nrows=2, ncols=2)

desired_box_ratioN = 1
for i, ax in enumerate(axes.flat, start=1):
    ax.plot(np.arange(0, i * 4, i))
    temp_inverse_axis_ratioN = abs( (ax.get_xlim()[1] - ax.get_xlim()[0])/(ax.get_ylim()[1] - ax.get_ylim()[0]) )
    ax.set(aspect = desired_box_ratioN * temp_inverse_axis_ratioN, adjustable='box-forced')

plt.show()

1

理论

Matplotlib中存在不同的坐标系。不同坐标系之间的差异可能会让很多人感到困惑。问题提出者希望在显示坐标系中设置纵横比,但是ax.set_aspect()会在数据坐标系中设置纵横比。它们之间可以用以下公式表示:

aspect = 1.0/dataRatio*dispRatio

其中,aspect是在set_aspect方法中使用的参数,dataRatio是数据坐标系中的宽高比,dispRatio您所需的显示坐标系中的宽高比。请保留HTML标签。

实践

我们可以使用get_data_ratio方法使代码更加简洁。以下是一个工作代码片段:

import matplotlib.pyplot as plt
import numpy as np

fig, axes = plt.subplots(nrows=2, ncols=2)

dispRatio = 0.5
for i, ax in enumerate(axes.flat, start=1):
    ax.plot(np.arange(0, i * 4, i))
    ax.set(aspect=1.0/ax.get_data_ratio()*dispRatio, adjustable='box-forced')

plt.show()

我还写了一篇详细的文章,关于所有这些东西 在这里


我尝试按照您的教程操作,非常清晰易懂,但是当y轴采用对数刻度时无法正常工作,有什么方法可以在保持y轴对数刻度的情况下扩大x轴? - seanysull
抱歉,我不确定。也许您可以在Stack Overflow上基于该帖子开一个新的问题。 - jdhao

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