Matplotlib:获取子图以填充图形

Posted

技术标签:

【中文标题】Matplotlib:获取子图以填充图形【英文标题】:Matplotlib: Getting subplots to fill figure 【发布时间】:2014-08-23 11:55:12 【问题描述】:

我希望获得有关在将图像绘制为子图时如何覆盖默认 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 值的比率,所以我想知道是否有一个好的通用方法来设置填充空间的子图宽度。可以以某种方式指定子图的物理大小吗?几个小时以来我一直在寻找解决方案,因此提前感谢您提供的任何帮助。

【问题讨论】:

【参考方案1】:

您可以使用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 的坐标是相对坐标[左、下、宽、高],其中每个方向都按页面大小缩放。

正如其他答案中指出的那样,imshowmatshow 有时会尝试将像素保持在图片正方形中。轴比和imshow 之间存在相当特殊的相互作用。

如果在没有 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() 时无效,这是我正在使用的。我用它来绘制二维直方图,图中的每个正方形对应一个 bin,它的颜色对应于它包含的值的数量。您是否知道调整 imshow() 对象大小的方法,或者在您的方法适用的地方呈现 2D 直方图的更好方法? @user3798292,您可以尝试按照此处指定的方式设置 set_position,但随后不要使用 pyplot 命令,而是使用直接对象接口(@98​​7654338@ 而不是 plt.imshow(...) @user3798292:您已经向您指出了一些解决方案,但我在此添加了一些 cmets。 imshow 在这个意义上有点奇怪。 plt.gca().set_position([0, 0, 1, 1]) 正是我需要使用 .imshow() 和 Cartopy 来获取卫星图像图来填充整个图。对于我的用例,plt.axis('tight') 让它显示了地球的整个投影,这不是我想要的。感谢您提供非常有帮助的答案! 我喜欢使用“晦涩的单位”来指代英寸。也很对。【参考方案2】:

首先,当您有 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)

在我的系统上,它看起来像这样:

【讨论】:

【参考方案3】:

一位朋友提出了一个似乎运作良好的解决方案,我想我会为遇到类似问题的任何人发布该解决方案 - 在 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()

【讨论】:

以上是关于Matplotlib:获取子图以填充图形的主要内容,如果未能解决你的问题,请参考以下文章

matplotlib子图与多种图形绘制

Python机器学习(六十八)Matplotlib 多个图形

在 matplotlib 中设置子图的大小

Matplotlib 不同大小的子图

尝试将 3d 子图添加到 matplotlib 图

不能使用 matplotlib.use('Agg'),图形总是显示在屏幕上