gridspec比subplot更适合精确控制子图大小,因其通过width_ratios和height_ratios定义相对权重的非均匀网格,支持行列合并与像素级面积分配,而subplot仅支持均分矩形网格。

gridspec 为什么比 subplot 更适合精确控制子图大小
因为 plt.subplot() 或 plt.subplots() 默认把画布均分为矩形网格,每个子图占据整行整列;而实际需求常是「左侧大图占 70% 宽度,右侧两个小图上下堆叠各占 30% 宽度的 50% 高度」——这种非均匀划分必须用 GridSpec。它不生成子图,只定义网格结构,后续用 fig.add_subplot(gs[...]) 指定每个子图落在哪几行哪几列,从而实现像素级面积分配。
用 width_ratios 和 height_ratios 控制列宽/行高比例
这是最常用也最容易出错的地方:比例值不是百分比,而是相对权重。比如想让三列宽度为 2:1:1,就设 width_ratios=[2, 1, 1];若写成 [70, 30],实际是 70:30 = 7:3,不是你想要的 70% 和 30%。同样,height_ratios 控制每行高度占比。
- 比例数组长度必须等于
nrows或ncols - 数值支持小数(如
[1.5, 0.5]),但不能为 0 - 若只调一行/一列比例,另一方向默认均分(无需显式写
[1, 1])
import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec <p>fig = plt.figure(figsize=(10, 4)) gs = gridspec.GridSpec(nrows=1, ncols=3, width_ratios=[2, 1, 1])</p><p>ax1 = fig.add_subplot(gs[0, 0]) # 第 0 行,第 0 列(宽占比 2) ax2 = fig.add_subplot(gs[0, 1]) # 第 0 行,第 1 列(宽占比 1) ax3 = fig.add_subplot(gs[0, 2]) # 第 0 行,第 2 列(宽占比 1)</p>
合并行列时避免索引越界或重叠
gs[r_start:r_end, c_start:c_end] 是切片语法,注意 Python 切片「左闭右开」:gs[0:2, 0] 表示第 0、1 行 + 第 0 列,共两行一列;gs[0, 0:2] 是第 0 行 + 第 0、1 列。常见错误是写 gs[0:1, 0](只占 1 行)却误以为占了「前两行」。
- 合并多行时,
r_end必须 ≤nrows;同理列索引不能超ncols - 两个子图若用了同一网格区域(如都用
gs[0, 0]),后画的会覆盖先画的 - 想让一个图横跨全部列?直接写
gs[0, :],冒号表示全选
# 两行两列,第一行合并为一个宽图,第二行左右两个小图 gs = gridspec.GridSpec(nrows=2, ncols=2, height_ratios=[2, 1]) ax_top = fig.add_subplot(gs[0, :]) # 第 0 行,所有列 ax_bot_left = fig.add_subplot(gs[1, 0]) # 第 1 行,第 0 列 ax_bot_right = fig.add_subplot(gs[1, 1])# 第 1 行,第 1 列
和 constrained_layout=True 共存时的冲突与绕过方式
启用 constrained_layout(如 plt.figure(constrained_layout=True))会自动调整子图间距,但它可能无视 GridSpec 的手动合并或比例设置,导致布局错乱或报错 Warning: Tight layout not applied...。这不是 bug,而是机制冲突——一个要自动算,一个要你手算。
- 最稳妥做法:关掉
constrained_layout,改用plt.tight_layout()手动收尾 - 若必须开启,可在创建
GridSpec时传参hspace/wspace微调间隙,但效果不稳定 - 注意:
plt.subplots(constrained_layout=True)生成的fig无法再安全套用自定义GridSpec
真正难的是在复杂嵌套(比如某个子图内部还要再分 gridspec)时保持各层比例不打架,这时候建议先画框架、再逐层填充,别试图一步到位。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











