在图中排列多个轴#

通常一次需要多个轴在一个图形上,通常组织成一个规则的网格。Matplotlib 有多种工具用于处理随着图书馆历史发展而演变的轴网格。在这里,我们将讨论我们认为用户应该最常使用的工具,支持 Axes 组织方式的工具,并提及一些较旧的工具。

笔记

Matplotlib 使用Axes来指代包含数据、x 轴和 y 轴、刻度、标签、标题等的绘图区域。有关 详细信息,请参阅图的部分。另一个经常使用的术语是“子图”,它指的是与其他 Axes 对象位于网格中的 Axes。

概述#

创建轴的网格形状组合#

subplots

用于创建图形和轴网格的主要功能。它一次创建并放置图形上的所有轴,并返回一个对象数组,其中包含网格中轴的句柄。见 Figure.subplots

或者

subplot_mosaic

一种创建图形和轴网格的简单方法,具有额外的灵活性,轴也可以跨越行或列。Axes 在带标签的字典而不是数组中返回。另请参见 复杂Figure.subplot_mosaic语义图形组合

有时很自然地会有一组以上不同的 Axes 网格,在这种情况下,Matplotlib 具有以下概念SubFigure

SubFigure

人物中的虚拟人物。

基础工具#

这些的基础是 aGridSpec和 a的概念SubplotSpec

GridSpec

指定将放置子图的网格的几何形状。需要设置网格的行数和列数。可选地,可以调整子图布局参数(例如,左、右等)。

SubplotSpec

指定给定的子图的位置GridSpec

一次添加单个轴#

上述函数在单个函数调用中创建所有轴。也可以一次添加一个轴,这最初是 Matplotlib 过去的工作方式。这样做通常不太优雅和灵活,但有时对交互式工作或将 Axes 放置在自定义位置很有用:

add_axes

在图形宽度或高度的分数中指定的位置添加单个轴 。[left, bottom, width, height]

subplot或者Figure.add_subplot

在图形上添加单个子图,具有基于 1 的索引(继承自 Matlab)。通过指定一系列网格单元可以跨越列和行。

subplot2grid

类似于pyplot.subplot,但使用基于 0 的索引和二维 python 切片来选择单元格。

制作网格的高级方法#

基本 2x2 网格#

我们可以使用创建一个基本的 2×2 轴网格 subplots。它返回一个Figure 实例和一个对象数组Axes。Axes 对象可用于访问将艺术家放置在 Axes 上的方法;这里我们使用annotate,但其他示例可能是plot, pcolormesh等。

import matplotlib.pyplot as plt
import numpy as np

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()')
plt.subplots()
Text(0.5, 0.9880942857142857, 'plt.subplots()')

我们会注解很多 Axes,所以让我们封装注解,而不是每次需要时都使用那一大段注解代码:

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 in axd:
    annotate_axes(axd[k], f'axd["{k}"]', fontsize=14)
fig.suptitle('plt.subplot_mosaic()')
plt.subplot_mosaic()
Text(0.5, 0.9880942857142857, 'plt.subplot_mosaic()')

固定纵横比轴的网格#

固定纵横比轴对于图像或地图很常见。但是,它们对布局提出了挑战,因为对轴的大小施加了两组约束——它们适合图形并且它们具有设定的纵横比。默认情况下,这会导致 Axes 之间的间隙很大:

fig, axs = plt.subplots(2, 2, layout="constrained", figsize=(5.5, 3.5))
for ax in axs.flat:
    ax.set_aspect(1)
fig.suptitle('Fixed aspect Axes')
固定纵横轴
Text(0.5, 0.9880942857142857, 'Fixed aspect Axes')

解决此问题的一种方法是将图形的纵横比更改为接近轴的纵横比,但这需要反复试验。Matplotlib 还提供layout="compressed",它将与简单的网格一起使用以减少轴之间的间隙。(mpl_toolkits也提供ImageGrid了实现类似效果,但使用非标准 Axes 类)。

fig, axs = plt.subplots(2, 2, layout="compressed", figsize=(5.5, 3.5))
for ax in axs.flat:
    ax.set_aspect(1)
fig.suptitle('Fixed aspect Axes: compressed')
固定纵横轴:压缩
Text(0.5, 0.9880942857142857, '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 in axd:
    annotate_axes(axd[k], f'axd["{k}"]', fontsize=14)
fig.suptitle('plt.subplot_mosaic()')
plt.subplot_mosaic()
Text(0.5, 0.9880942857142857, 'plt.subplot_mosaic()')

GridSpec有关如何使用or执行相同操作的说明,请参见下文 subplot2grid

网格中的可变宽度或高度

两者都subplots允许subplot_mosaic网格中的行具有不同的高度,并且使用gridspec_kw关键字参数使列具有不同的宽度。接受的间距参数GridSpec 可以传递给subplotsand 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 in axd:
    annotate_axes(axd[k], f'axd["{k}"]', fontsize=14)
fig.suptitle('plt.subplot_mosaic()')
plt.subplot_mosaic()
Text(0.5, 0.9880942857142857, '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('0.9')
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]')
排列轴
Text(0.016867713730569944, 0.5, 'ylabel for subfigs[1]')

也可以subplot_mosaic使用嵌套列表来嵌套 Axes。此方法不使用子图,如上所示,因此缺乏添加每个子图suptitlesupxlabel等的能力。相反,它是subgridspec 下面描述的方法的便利包装。

inner = [['innerA'],
         ['innerB']]
outer = [['upper left',  inner],
          ['lower left', 'lower right']]

fig, axd = plt.subplot_mosaic(outer, layout="constrained")
for k in axd:
    annotate_axes(axd[k], f'axd["{k}"]')
排列轴

低级和高级网格方法#

GridSpec在内部,通过创建和的实例来控制 Axes 网格的排列SubplotSpecGridSpec定义了一个(可能是不均匀的)单元格网格。对GridSpec进行索引会返回一个覆盖一个或多个网格单元的 SubplotSpec,并可用于指定 Axes 的位置。

以下示例展示了如何使用低级方法使用GridSpec对象来排列 Axes。

基本 2x2 网格#

我们可以以与以下相同的方式完成 2x2 网格 :plt.subplots(2, 2)

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')
使用 add_gridspec 手动添加子图
Text(0.5, 0.9880942857142857, 'Manually added subplots using add_gridspec')

跨越网格中行或网格的轴#

我们可以使用NumPy 切片语法对规范数组进行索引 ,新的 Axes 将跨越切片。这将与以下内容相同: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')
手动添加子图,跨越一列
Text(0.5, 0.9880942857142857, 'Manually added subplots, spanning a column')

手动调整GridSpec布局#

当明确使用GridSpec时,您可以调整从GridSpec创建的子图的布局参数。请注意,此选项不兼容constrained_layoutFigure.tight_layout两者都忽略左右调整子图大小以填充图形。通常这种手动放置需要迭代以使 Axes 刻度标签不与 Axes 重叠。

这些间距参数也可以作为subplotsgridspec_kw 参数传递subplot_mosaic

fig = plt.figure(layout=None, facecolor='0.9')
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')
手动 gridspec 与 right=0.75
Text(0.5, 0.98, 'Manual gridspec with right=0.75')

带有 SubplotSpec 的嵌套布局#

您可以创建类似于subfigures使用 的嵌套布局subgridspec。在这里,轴脊 对齐的。

请注意,这也可以从更详细的 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')
嵌套网格规范
Text(0.5, 0.99131875, '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), constrained_layout=False)
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分13.006秒)

由 Sphinx-Gallery 生成的画廊