注意
转到末尾 下载完整示例代码。
在图中排列多个轴#
通常,一个图中需要同时显示多个轴,它们通常组织成规则网格。Matplotlib 提供了多种工具来处理轴网格,这些工具随着库的历史发展而演变。我们将在这里讨论我们认为用户应该最常使用的工具、支撑轴组织方式的工具,并提及一些旧工具。
注意
Matplotlib 使用 轴 来指代包含数据、x 轴和 y 轴、刻度、标签、标题等的绘图区域。有关更多详细信息,请参阅 图的组成部分。另一个常用术语是“子图”("subplot"),它指的是与其他轴对象一起排列在网格中的一个轴。
概述#
创建网格状轴组合#
subplots
用于创建图形和轴网格的主要函数。它一次性创建并将所有轴放置在图中,并返回一个包含网格中轴句柄的对象数组。请参阅
Figure.subplots
。
或
subplot_mosaic
一种创建图形和轴网格的简单方法,额外增加了轴可以跨越行或列的灵活性。轴以带标签的字典形式返回,而不是数组。另请参阅
Figure.subplot_mosaic
和 复杂且语义化的图组合(subplot_mosaic)。
有时,自然会存在多个独立的轴网格组,在这种情况下,Matplotlib 引入了 SubFigure
的概念
SubFigure
图中的虚拟图。
底层工具#
这些概念的底层是 GridSpec
和 SubplotSpec
GridSpec
指定子图将被放置的网格几何结构。需要设置网格的行数和列数。可选地,可以调整子图布局参数(例如,left、right 等)。
SubplotSpec
指定给定
GridSpec
中子图的位置。
一次添加单个轴#
上述函数在一次函数调用中创建所有轴。也可以一次添加一个轴,这最初是 Matplotlib 的工作方式。这样做通常不够优雅和灵活,但有时对于交互式工作或将轴放置在自定义位置很有用
add_axes
在以图形宽度或高度的比例表示的
[left, bottom, width, height]
指定的位置添加单个轴。subplot
或Figure.add_subplot
在图中添加单个子图,使用 1-based 索引(继承自 Matlab)。通过指定网格单元范围,可以跨越列和行。
subplot2grid
类似于
pyplot.subplot
,但使用 0-based 索引和二维 Python 切片来选择单元格。
作为手动添加轴 ax 的一个简单示例,我们将在一个 4 英寸 x 3 英寸的图中添加一个 3 英寸 x 2 英寸的轴。请注意,子图的位置是使用图归一化单位定义为 [left, bottom, width, height] 的

创建网格的高级方法#
基本的 2x2 网格#
我们可以使用 subplots
创建一个基本的 2x2 轴网格。它返回一个 Figure
实例和一个 Axes
对象数组。轴对象可用于访问在轴上放置艺术家的方法;这里我们使用 annotate
,但其他示例可以是 plot
、pcolormesh
等。
fig, axs = plt.subplots(ncols=2, nrows=2, figsize=(5.5, 3.5),
layout="constrained")
# add an artist, in this case a nice label in the middle...
for row in range(2):
for col in range(2):
axs[row, col].annotate(f'axs[{row}, {col}]', (0.5, 0.5),
transform=axs[row, col].transAxes,
ha='center', va='center', fontsize=18,
color='darkgrey')
fig.suptitle('plt.subplots()')

我们将注释许多轴,因此让我们将注释封装起来,而不是每次需要时都有一大段注释代码
def annotate_axes(ax, text, fontsize=18):
ax.text(0.5, 0.5, text, transform=ax.transAxes,
ha="center", va="center", fontsize=fontsize, color="darkgrey")
同样的效果可以通过 subplot_mosaic
实现,但返回类型是字典而不是数组,用户可以为键赋予有用的含义。这里我们提供了两个列表,每个列表代表一行,列表中每个元素代表一列的键。
fig, axd = plt.subplot_mosaic([['upper left', 'upper right'],
['lower left', 'lower right']],
figsize=(5.5, 3.5), layout="constrained")
for k, ax in axd.items():
annotate_axes(ax, f'axd[{k!r}]', fontsize=14)
fig.suptitle('plt.subplot_mosaic()')

固定纵横比轴的网格#
固定纵横比轴常用于图像或地图。然而,它们在布局上带来了挑战,因为对轴的大小施加了两组约束——它们必须适应图中,并且必须具有设定的纵横比。这导致默认情况下轴之间存在大间隙。
fig, axs = plt.subplots(2, 2, layout="constrained",
figsize=(5.5, 3.5), facecolor='lightblue')
for ax in axs.flat:
ax.set_aspect(1)
fig.suptitle('Fixed aspect Axes')

解决这个问题的一种方法是改变图的纵横比使其接近轴的纵横比,但这需要反复试验。Matplotlib 还提供了 layout="compressed"
,它适用于简单网格以减少轴之间的间隙。(mpl_toolkits
也提供了 ImageGrid
来实现类似的效果,但使用的是非标准轴类)。
fig, axs = plt.subplots(2, 2, layout="compressed", figsize=(5.5, 3.5),
facecolor='lightblue')
for ax in axs.flat:
ax.set_aspect(1)
fig.suptitle('Fixed aspect Axes: compressed')

网格中跨越行或列的轴#
有时我们希望轴跨越网格的行或列。实际上有多种方法可以实现这一点,但最方便的可能是在 subplot_mosaic
中通过重复其中一个键来使用。
fig, axd = plt.subplot_mosaic([['upper left', 'right'],
['lower left', 'right']],
figsize=(5.5, 3.5), layout="constrained")
for k, ax in axd.items():
annotate_axes(ax, f'axd[{k!r}]', fontsize=14)
fig.suptitle('plt.subplot_mosaic()')

有关如何使用 GridSpec
或 subplot2grid
实现相同效果的描述,请参见下文。
网格中可变宽度或高度#
subplots
和 subplot_mosaic
都允许通过 gridspec_kw 关键字参数设置网格中的行具有不同的高度,列具有不同的宽度。 GridSpec
接受的间距参数可以传递给 subplots
和 subplot_mosaic
。
gs_kw = dict(width_ratios=[1.4, 1], height_ratios=[1, 2])
fig, axd = plt.subplot_mosaic([['upper left', 'right'],
['lower left', 'right']],
gridspec_kw=gs_kw, figsize=(5.5, 3.5),
layout="constrained")
for k, ax in axd.items():
annotate_axes(ax, f'axd[{k!r}]', fontsize=14)
fig.suptitle('plt.subplot_mosaic()')

嵌套轴布局#
有时,拥有两个或更多彼此可能无需关联的轴网格会很有帮助。实现此目的最简单的方法是使用 Figure.subfigures
。请注意,子图布局是独立的,因此每个子图中的轴轴线不一定对齐。有关使用 GridSpecFromSubplotSpec
实现相同效果的更详细方法,请参见下文。
fig = plt.figure(layout="constrained")
subfigs = fig.subfigures(1, 2, wspace=0.07, width_ratios=[1.5, 1.])
axs0 = subfigs[0].subplots(2, 2)
subfigs[0].set_facecolor('lightblue')
subfigs[0].suptitle('subfigs[0]\nLeft side')
subfigs[0].supxlabel('xlabel for subfigs[0]')
axs1 = subfigs[1].subplots(3, 1)
subfigs[1].suptitle('subfigs[1]')
subfigs[1].supylabel('ylabel for subfigs[1]')

也可以使用嵌套列表通过 subplot_mosaic
嵌套轴。此方法不像上面那样使用子图,因此无法添加每个子图的 suptitle
和 supxlabel
等。它只是对下面描述的 subgridspec
方法的便捷封装。

底层和高级网格方法#
在内部,轴网格的排列通过创建 GridSpec
和 SubplotSpec
的实例来控制。GridSpec 定义了一个(可能不均匀的)单元格网格。对 GridSpec 进行索引会返回一个覆盖一个或多个网格单元格的 SubplotSpec,可用于指定轴的位置。
以下示例展示了如何使用底层方法来使用 GridSpec 对象排列轴。
基本的 2x2 网格#
我们可以以与 plt.subplots(2, 2)
相同的方式实现 2x2 网格。
fig = plt.figure(figsize=(5.5, 3.5), layout="constrained")
spec = fig.add_gridspec(ncols=2, nrows=2)
ax0 = fig.add_subplot(spec[0, 0])
annotate_axes(ax0, 'ax0')
ax1 = fig.add_subplot(spec[0, 1])
annotate_axes(ax1, 'ax1')
ax2 = fig.add_subplot(spec[1, 0])
annotate_axes(ax2, 'ax2')
ax3 = fig.add_subplot(spec[1, 1])
annotate_axes(ax3, 'ax3')
fig.suptitle('Manually added subplots using add_gridspec')

网格中跨越行或网格的轴#
我们可以使用 NumPy 切片语法 对 spec 数组进行索引,新的轴将跨越该切片。这与 fig, axd = plt.subplot_mosaic([['ax0', 'ax0'], ['ax1', 'ax2']], ...)
相同。
fig = plt.figure(figsize=(5.5, 3.5), layout="constrained")
spec = fig.add_gridspec(2, 2)
ax0 = fig.add_subplot(spec[0, :])
annotate_axes(ax0, 'ax0')
ax10 = fig.add_subplot(spec[1, 0])
annotate_axes(ax10, 'ax10')
ax11 = fig.add_subplot(spec[1, 1])
annotate_axes(ax11, 'ax11')
fig.suptitle('Manually added subplots, spanning a column')

手动调整 GridSpec 布局#
当显式使用 GridSpec 时,您可以调整从 GridSpec 创建的子图的布局参数。请注意,此选项与 constrained layout 或 Figure.tight_layout
不兼容,这两者都会忽略 left 和 right 并调整子图大小以填充图形。通常,这种手动放置需要多次迭代才能使轴刻度标签不与轴重叠。
这些间距参数也可以作为 gridspec_kw 参数传递给 subplots
和 subplot_mosaic
。
fig = plt.figure(layout=None, facecolor='lightblue')
gs = fig.add_gridspec(nrows=3, ncols=3, left=0.05, right=0.75,
hspace=0.1, wspace=0.05)
ax0 = fig.add_subplot(gs[:-1, :])
annotate_axes(ax0, 'ax0')
ax1 = fig.add_subplot(gs[-1, :-1])
annotate_axes(ax1, 'ax1')
ax2 = fig.add_subplot(gs[-1, -1])
annotate_axes(ax2, 'ax2')
fig.suptitle('Manual gridspec with right=0.75')

使用 SubplotSpec 的嵌套布局#
您可以使用 subgridspec
创建类似于 subfigures
的嵌套布局。在这里,轴轴线是 对齐的。
请注意,这也可以从更详细的 gridspec.GridSpecFromSubplotSpec
获得。
fig = plt.figure(layout="constrained")
gs0 = fig.add_gridspec(1, 2)
gs00 = gs0[0].subgridspec(2, 2)
gs01 = gs0[1].subgridspec(3, 1)
for a in range(2):
for b in range(2):
ax = fig.add_subplot(gs00[a, b])
annotate_axes(ax, f'axLeft[{a}, {b}]', fontsize=10)
if a == 1 and b == 1:
ax.set_xlabel('xlabel')
for a in range(3):
ax = fig.add_subplot(gs01[a])
annotate_axes(ax, f'axRight[{a}, {b}]')
if a == 2:
ax.set_ylabel('ylabel')
fig.suptitle('nested gridspecs')

这是一个更复杂的嵌套 GridSpec 示例:我们创建一个外层 4x4 网格,每个单元格包含一个内层 3x3 的轴网格。我们通过隐藏每个内层 3x3 网格中适当的轴线来勾勒外层 4x4 网格。
def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)):
return np.sin(i*a)*np.cos(i*b), np.sin(i*c)*np.cos(i*d)
fig = plt.figure(figsize=(8, 8), layout='constrained')
outer_grid = fig.add_gridspec(4, 4, wspace=0, hspace=0)
for a in range(4):
for b in range(4):
# gridspec inside gridspec
inner_grid = outer_grid[a, b].subgridspec(3, 3, wspace=0, hspace=0)
axs = inner_grid.subplots() # Create all subplots for the inner grid.
for (c, d), ax in np.ndenumerate(axs):
ax.plot(*squiggle_xy(a + 1, b + 1, c + 1, d + 1))
ax.set(xticks=[], yticks=[])
# show only the outside spines
for ax in fig.get_axes():
ss = ax.get_subplotspec()
ax.spines.top.set_visible(ss.is_first_row())
ax.spines.bottom.set_visible(ss.is_last_row())
ax.spines.left.set_visible(ss.is_first_col())
ax.spines.right.set_visible(ss.is_last_col())
plt.show()

更多阅读#
参考
本示例展示了以下函数、方法、类和模块的使用
脚本总运行时间: (0 分 14.470 秒)