Python Matplotlib 多图与子图布局完全指南
参考文档:
https://matplotlib.org/3.5.3/tutorials/intermediate/arranging_axes.html
1. 创建多图的核心方法
在 Matplotlib 中,一个 axes 对象代表一个独立的绘图区域。官方文档将相关功能归类为 "arranging axes"(排列坐标轴)。
1.1 使用 subfigure 创建子图组
import matplotlib.pyplot as plt
# 启用 constrained_layout 自动处理边距和标签显示
fig = plt.figure(constrained_layout=True)
# 将图分割为 1 行 2 列的子图组,间距 0.07,宽度比例 1.5:1
subfigs = fig.subfigures(1, 2, wspace=0.07, width_ratios=[1.5, 1.])
1.2 通过 gridspec 精确控制网格
# 创建 3x3 的网格,并指定边距和间距
gs = fig.add_gridspec(nrows=3, ncols=3,
left=0.05, right=0.75,
hspace=0.1, wspace=0.05)
1.3 subplots 快速生成多个子图
# 在网格的指定区域创建子图(前两行,所有列)
ax0 = fig.add_subplot(gs[:-1, :])
1.4 add_axes 手动添加任意位置坐标系
通过指定 [left, bottom, width, height](单位为 relative figure fractions),可以在现有图中自由嵌入小图:
# 在相对位置 (0.2, 0.2) 处创建一个宽度 0.5、高度 0.3 的小坐标系
ax = fig.add_axes([0.2, 0.2, 0.5, 0.3])
2. 实用示例代码
以下代码改编自官方文档,保留了核心逻辑但重构了变量命名和结构。
2.1 组合 subfigure + subplots
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(constrained_layout=True, figsize=(10, 6))
# 创建左右两个子图组
subfigures = fig.subfigures(1, 2, wspace=0.08, width_ratios=[1.6, 1.0])
# 左侧子图组:2行2列,设置背景色和总标题
left_subfig = subfigures[0]
axs_left = left_subfig.subplots(2, 2)
left_subfig.set_facecolor('lightgray')
left_subfig.suptitle('Left Panel (2x2)')
left_subfig.supxlabel('Shared X Label')
# 右侧子图组:3行1列,设置标题
right_subfig = subfigures[1]
axs_right = right_subfig.subplots(3, 1)
right_subfig.suptitle('Right Panel (3x1)')
right_subfig.supylabel('Shared Y Label')
# 在左侧子图中填充示例数据
x = np.linspace(0, 2*np.pi, 100)
for i, ax in enumerate(axs_left.flat):
ax.plot(x, np.sin(x + i*np.pi/4))
ax.set_title(f'Sin(x + {i}π/4)')
plt.show()
2.2 通过 gridspec 灵活分配子图位置
import matplotlib.pyplot as plt
def annotate_axes(ax, text):
"""在坐标系上添加标注"""
ax.text(0.5, 0.5, text, transform=ax.transAxes,
ha='center', va='center', fontsize=14, fontweight='bold')
fig = plt.figure(constrained_layout=False, facecolor='lightyellow')
# 创建 3x3 网格
grid = fig.add_gridspec(nrows=3, ncols=3,
left=0.05, right=0.75,
hspace=0.1, wspace=0.05)
# ax0 占据前两行所有列
ax_main = fig.add_subplot(grid[:-1, :])
annotate_axes(ax_main, 'Main Plot (rows 0-1, all cols)')
# ax1 占据最后一行前两列
ax_bottom_left = fig.add_subplot(grid[-1, :-1])
annotate_axes(ax_bottom_left, 'Bottom Left')
# ax2 占据最后一行最后一列
ax_bottom_right = fig.add_subplot(grid[-1, -1])
annotate_axes(ax_bottom_right, 'Bottom Right')
fig.suptitle('Custom GridSpec Layout')
plt.show()
2.3 高级用法:子图嵌套与坐标轴控制
import matplotlib.pyplot as plt
import numpy as np
def waveform_generator(a, b, c, d, t=np.arange(0, 2*np.pi, 0.05)):
"""生成两个组合波形的 x,y 坐标"""
x = np.sin(t*a) * np.cos(t*b)
y = np.sin(t*c) * np.cos(t*d)
return x, y
fig = plt.figure(figsize=(8, 8), constrained_layout=False)
# 创建外层 4x4 网格
outer_grid = fig.add_gridspec(4, 4, wspace=0, hspace=0)
for row in range(4):
for col in range(4):
# 每个外层网格单元内部再嵌套 3x3 子网格
inner_grid = outer_grid[row, col].subgridspec(3, 3, wspace=0, hspace=0)
# 生成该内部网格的所有子图
axes_array = inner_grid.subplots()
# 在每一个子图中绘制专属波形
for (c, d), ax in np.ndenumerate(axes_array):
x_data, y_data = waveform_generator(row+1, col+1, c+1, d+1)
ax.plot(x_data, y_data)
ax.set(xticks=[], yticks=[]) # 隐藏刻度
# 只显示最外层的坐标轴线
for ax in fig.get_axes():
spec = ax.get_subplotspec()
ax.spines.top.set_visible(spec.is_first_row())
ax.spines.bottom.set_visible(spec.is_last_row())
ax.spines.left.set_visible(spec.is_first_col())
ax.spines.right.set_visible(spec.is_last_col())
plt.show()