如何使用pyplot让一组坐标轴占用多个子图

71

我想在一个图中绘制三个子图。该图应该具有2x2的子图布局,其中第一个子图应该占据前两个子图单元格(即整个第一行的子图单元格),其他子图应该位于第一个子图下面的单元格3和4中。

我知道MATLAB可以通过使用subplot命令来实现:

subplot(2,2,[1,2]) % the plot will span subplots 1 and 2

在pyplot中,是否也可以使单个坐标轴跨越多个子图?pyplot.subplot的文档没有提到这一点。

有人有简单的解决方案吗?


可能是 https://dev59.com/cnM_5IYBdhLWcg3wfTI0 的重复问题。 - cyborg
7个回答

75

你可以简单地这样做:

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(0, 7, 0.01)
    
plt.subplot(2, 1, 1)
plt.plot(x, np.sin(x))
    
plt.subplot(2, 2, 3)
plt.plot(x, np.cos(x))
    
plt.subplot(2, 2, 4)
plt.plot(x, np.sin(x)*np.cos(x))

即,第一个图是在上半部分绘制的(该图仅分为2x1 = 2个单元格),后面的两个较小的图是在2x2=4个单元格网格中完成的。 subplot() 的第三个参数是图形在该网格中的位置(以英语阅读方向为准,第一个单元格位于左上角): 例如在第二个子图中 (subplot(2, 2, 3)),轴将进入2x2矩阵的第三部分,即位于左下角。

enter image description here


11
虽然这回答了问题中给出的特定示例,但不清楚如何应用于大小不是2的幂次方的布局。对于我的代码,我想将布局分为三个部分。一个绘图填充右边的2/3,另一个绘图填充左边的1/3。 - AnnanFay
4
这个答案适用于常规布局(没有“2的幂”限制):例如,subplot(3, 1, 1)将其分为三等份。现在,你想要的可以通过axes_grid 工具包处理。 - Eric O. Lebigot
2
@EOL,我在考虑2/3,而不是1/3。例如,我有两个子图,只有1列,第一个跨越2/3,第二个跨越1/3。这该怎么做?谢谢。 - StayFoolish
我不是完全确定,但看起来适当的方法在特定的Matplotlib工具包(axes_grid1)中有描述:https://matplotlib.org/2.0.2/mpl_toolkits/axes_grid/users/overview.html#axes-grid1。 - Eric O. Lebigot
我刚才意识到我一直在查看matplotlib子图文档而不是subplot。无论如何,再次感谢您的精彩文章。 - sucksatnetworking
3
这个答案相当老了,而且大多数人都不鼓励使用。请参见下面的“gridspec”和“subplot_mosaic”响应。 - Jody Klymak

62

使用GridSpec制作多列/行子图布局展示了使用GridSpec实现此目的的方法。一个包含三个子图的简化版本如下所示:

import matplotlib.pyplot as plt

fig = plt.figure()

gs = fig.add_gridspec(2,2)
ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1])
ax3 = fig.add_subplot(gs[1, :])

plt.show()

在此输入图片描述


非常、非常、非常棒!非常感谢。如果您有6行,例如,您想在第1行上绘制一个图,而在第2、3、4行上绘制另一个图,则这是必要的。您不能使用通常的subplot技巧(即subplot(611)subplot(612)...因为subplot没有任何关于“跨度”的概念)。 - Trevor Boyd Smith
这对我来说是最直观的,如果你正在处理不同的坐标轴,则非常有用。 - GeoMonkey
为了完整起见,您能展示如何用实际图形填充空白的图形吗?谢谢 :) - Tom

39

要在一个坐标轴中拥有多个子图,您可以简单地这样做:

from matplotlib import pyplot as plt
import numpy as np

b=np.linspace(-np.pi, np.pi, 100)

a1=np.sin(b)

a2=np.cos(b)

a3=a1*a2

plt.subplot(221)
plt.plot(b, a1)
plt.title('sin(x)')

plt.subplot(222)
plt.plot(b, a2)
plt.title('cos(x)')

plt.subplot(212)
plt.plot(b, a3)
plt.title('sin(x)*cos(x)')

plt.show()

enter image description here

另一种方法是

plt.subplot(222)
plt.plot(b, a1)
plt.title('sin(x)')

plt.subplot(224)
plt.plot(b, a2)
plt.title('cos(x)')

plt.subplot(121)
plt.plot(b, a3)
plt.title('sin(x)*cos(x)')

plt.show()

这里输入图片描述


17
要更精细地控制,您可能希望使用 matplotlib.pyplotsubplot2grid 模块。

https://matplotlib.org/stable/api/gridspec_api.html

一个GridSpec的实例:使用Gridspec制作多列/行子图布局的示例。
import matplotlib.pyplot as plt

from matplotlib.gridspec import GridSpec


def format_axes(fig):
    for i, ax in enumerate(fig.axes):
        ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center")
        ax.tick_params(labelbottom=False, labelleft=False)

fig = plt.figure(layout="constrained")

gs = GridSpec(3, 3, figure=fig)
ax1 = fig.add_subplot(gs[0, :])
# identical to ax1 = plt.subplot(gs.new_subplotspec((0, 0), colspan=3))
ax2 = fig.add_subplot(gs[1, :-1])
ax3 = fig.add_subplot(gs[1:, -1])
ax4 = fig.add_subplot(gs[-1, 0])
ax5 = fig.add_subplot(gs[-1, -2])

fig.suptitle("GridSpec")
format_axes(fig)

plt.show()

Using Gridspec to make multi-column/row subplot layouts


2
非常酷,不知道这个,正是我所需要的,特别是colspan和rowspan功能。 - mxmlnkn

15

更现代的答案是:最简单的方法可能是使用 subplots_mosaic: https://matplotlib.org/stable/tutorials/provisional/mosaic.html

import matplotlib.pyplot as plt
import numpy as np

# Some example data to display
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)

fig, axd = plt.subplot_mosaic([['left', 'right'],['bottom', 'bottom']],
                              constrained_layout=True)
axd['left'].plot(x, y, 'C0')
axd['right'].plot(x, y, 'C1')
axd['bottom'].plot(x, y, 'C2')
plt.show()

使用subplot_mosaic的示例


7

在matplotlib中,有三种主要选项可以在一个图中制作单独的图:

  1. subplot:访问轴数组并添加子图
  2. gridspec:控制底层图形的几何属性(演示
  3. subplots:将前两个包装在方便的api中(演示
到目前为止,这些帖子已经讨论了前两个选项,但它们并没有提到第三个选项,这是更现代的方法,基于前两个选项。请参见具体文档 使用subplots和GridSpec组合两个子图

更新

一个更好的改进可能是在@Jody Klymak的帖子中提到的临时subplot_mosaic 方法。它使用一种结构化、视觉化的方法来映射子图,而不是混淆的数组索引。但它仍然基于上述后选项。


6

我可以想到另外两种更加灵活的解决方案。

  1. 最灵活的方式:使用 subplot_mosaic
f, axes = plt.subplot_mosaic('AAB;CDD;EEE')
# axes = {'A': ..., 'B': ..., ...}

作用:

subplot_mosaic picture

  1. 使用subplotsgridspec_kw。但是当不同行需要不同的宽度比例时,这种方式也不方便。
f, (a0, a1) = plt.subplots(1, 2, gridspec_kw={'width_ratios': [2, 1]})

效果:

子图图片

我认为其他答案中的subplot方法有点死板。例如,你不能轻松地创建两行宽度比为1:2和2:1的子图。然而,当你需要覆盖一些subplots的布局时,它可以帮助你。


2
我之前不知道有subplot_mosaic这个函数,太棒了! :) 非常感谢! - shredEngineer

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