让子图填充整个图形

10
我想请教如何在绘制图像子图时覆盖默认的matplotlib行为,因为子图的大小似乎与图形大小不匹配。我希望设置我的图形大小(例如与A4纸的宽度相匹配),并且子图可以自动拉伸以填充可用空间。在下面的示例中,下面的代码生成了一个在面板之间有很多空白空间的图形。
import numpy as np
import matplotlib.pyplot as plt

data=np.random.rand(10,4)

#creating a wide figure with 2 subplots in 1 row
fig,ax=plt.subplots(1,2, figsize=(9,3))  
ax=ax.reshape(1,len(ax))

for i in [0,1]:
    plt.sca(ax[0,i])
    plt.imshow(data,interpolation='nearest')
    plt.colorbar()

我希望子图在水平方向上拉伸,以填满图形空间。我将制作许多类似的图表,每个轴上的值数量不同,而图表之间的间距似乎取决于x值与y值的比例,所以我想知道是否有一种好的通用方法来设置子图的宽度以填充空间。是否可以以某种方式指定子图的物理尺寸?我已经搜索了几个小时的解决方案,所以提前感谢您能提供的任何帮助。
3个回答

15

您可以使用ax.set_position方法来调整轴区域。它使用相对坐标,因此如果您想制作A4图片,则:

import numpy as np
import matplotlib.pyplot as plt

# figsize keyword talks some obscure units which need a conversion from standard units
plt.figure(figsize=np.array([210,297]) / 25.4)

x = np.linspace(0,2*np.pi, 100)
plt.plot(x, np.sin(x))

plt.gca().set_position([0, 0, 1, 1])

plt.show()

现在轴区域(绘图区)填满整个页面。

set_position 中给出的坐标是相对坐标[left, lower, width, height],其中每个方向都按页面大小进行缩放。

正如其他答案中指出的那样,imshowmatshow有时会尝试保持图片中的像素为正方形。它们与轴比例之间存在着一种特殊的相互作用。

  • 如果没有使用extent=[...]aspect='auto'关键字参数调用imshow,则它会执行本地默认设置,通常会尝试保持像素正方形
  • 如果发生这种情况(或者设置了aspect='equal'),则轴的行为就好像调用了plt.axis('scaled')一样,即保持X和Y坐标的长度相等(每个单位的像素),并更改轴的大小以匹配范围
  • 可以通过设置plt.axis('tight')来覆盖此行为(使x和y限制精确适合图像)

旧的技巧是使用axis('auto')axis('normal'),但这些已经被弃用了(使用scaledequaltight)。

是的,这有点混乱。


非常感谢您的回复。在使用plt.plot()时,该解决方案似乎有效,但在使用我所使用的plt.imshow()时却不行。我正在使用它来绘制2D直方图,每个正方形对应一个bin,并且其颜色对应它包含的值的数量。您知道调整imshow()对象大小的方法吗?或者有更好的方式呈现2D直方图,使您的方法可以奏效吗? - PeterW
@user3798292,您可以尝试按照此处指定的方式进行set_position,但是不要使用pyplot命令,而是使用直接对象接口(ax.imshow(...)而不是plt.imshow(...))。 - Ajean
@user3798292:你已经有一些解决方案指向了你,但我对此添加了一些评论。imshow在这个意义上有点奇怪。 - DrV
plt.gca().set_position([0, 0, 1, 1]) 正是我需要的,使用 Cartopy 的 .imshow() 来填充整个图形,以获得卫星图像的绘制。对于我的用例,plt.axis('tight') 显示了整个地球的投影,这不是我想要的。感谢非常有帮助的答案! - kevinmicke
3
我喜欢使用“模糊单位”来表示英寸。非常正确。 - Archimaredes

7

首先,当您拥有Axes对象时,使用plt调用会导致问题。其次,imshow会将轴比例的纵横比设置为1。这就是为什么轴如此狭窄的原因。了解所有这些后,您的示例变成了:

import numpy as np
import matplotlib.pyplot as plt

data = np.random.rand(10,4)

#creating a wide figure with 2 subplots in 1 row
fig, axes = plt.subplots(1, 2, figsize=(9,3))  

for ax in axes.flatten():  # flatten in case you have a second row at some point
    img = ax.imshow(data, interpolation='nearest')
    ax.set_aspect('auto')

plt.colorbar(img)

在我的系统上,它看起来像这样:输入图像描述

0
一个朋友提出了一个看起来相当不错的解决方案,我想将其发布给那些遇到类似问题的人 - 在imshow中设置aspect='auto'似乎是个好办法,无论下面的nx、figsize、nplots_hori和nplots_vert选择什么值。
import numpy as np
import matplotlib.pyplot as plt

nx=5
data=np.random.rand(10,nx)

figsize=[10,8]
nplots_hori=2
nplots_vert=2

fig,ax=plt.subplots(nplots_vert, nplots_hori, figsize=figsize)
if nplots_vert==1: ax=ax.reshape(1,len(ax))

plt.tight_layout()
for i in range(nplots_hori):
    for j in range(nplots_vert):
        plt.sca(ax[j,i])
        plt.imshow(data, aspect='auto')

plt.tight_layout()
plt.show()

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