Matplotlib-多图布局与网格显示
- 一、多图布局的核心组件
- 二、基础布局:plt.subplots()快速创建网格
- 1. 均等分网格
- 2. 不等分网格(指定比例)
- 三、进阶布局:GridSpec实现复杂嵌套
- 1. 跨行列布局
- 2. 嵌套GridSpec
- 四、实用技巧:布局美化与一致性
- 1. 统一坐标轴范围与标签
- 2. 全局样式统一
- 3. 动态调整子图数量
- 五、常见问题与解决方案
- 1. 子图重叠或标签被截断
- 2. 中文显示乱码
- 3. 保存图像时布局错乱
在数据分析与可视化中,单一图表往往难以全面呈现复杂信息,将多个相关图表按逻辑布局组合,既能对比数据特征,又能揭示潜在关联。Matplotlib提供了灵活的多图布局工具,从简单的行列排列到复杂的嵌套网格,满足从基础分析到专业报告的多样化需求。
一、多图布局的核心组件
Matplotlib的多图布局基于画布(Figure) 和子图(Axes) 两大组件:
- 画布(Figure):所有图表的容器,相当于绘图的“纸张”,通过
plt.figure()
创建。 - 子图(Axes):画布上的单个图表区域,包含坐标轴、标题等元素,通过
plt.subplot()
或plt.subplots()
创建。
多图布局的本质是在画布上规划子图的位置与尺寸,关键参数包括:
- 行数(nrows) 与列数(ncols):定义子图的网格结构。
- 间距(hspace/wspace):控制子图之间的垂直/水平距离。
- 跨度(colspan/rowspan):允许子图跨越多行或多列(复杂布局必备)。
二、基础布局:plt.subplots()快速创建网格
plt.subplots(nrows, ncols)
是创建规则网格布局的首选方法,一次性生成所有子图并返回子图数组,适合行列整齐的布局。
1. 均等分网格
创建2行2列的均等分网格,展示4个相关图表:
import numpy as np
import matplotlib.pyplot as plt# 创建2x2网格的画布和子图数组
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10, 8)) # 画布大小10x8英寸# 生成示例数据
x = np.linspace(0, 2*np.pi, 100)
data = [np.sin(x), np.cos(x), np.sin(2*x), np.cos(2*x)]
titles = ['sin(x)', 'cos(x)', 'sin(2x)', 'cos(2x)']# 遍历子图数组并绘图
for i, ax in enumerate(axes.flat): # axes.flat将二维数组转为一维迭代器ax.plot(x, data[i])ax.set_title(titles[i], fontsize=10)ax.grid(alpha=0.3)# 调整子图间距(hspace垂直间距,wspace水平间距)
plt.subplots_adjust(hspace=0.3, wspace=0.2)
plt.suptitle("Trigonometric Functions", fontsize=14, y=0.95) # 总标题
plt.show()
axes.flat
简化了子图的批量处理,避免嵌套循环。plt.subplots_adjust()
用于微调间距,确保图表不重叠。
2. 不等分网格(指定比例)
通过gridspec_kw
参数设置行列比例,实现非均等分布局:
# 创建1行2列网格,列宽比例为3:1
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(10, 4),gridspec_kw={'width_ratios': [3, 1]})# 左侧绘制曲线
ax1.plot(x, np.sin(x), color='blue')
ax1.set_title("sin(x) Curve")
ax1.grid(alpha=0.3)# 右侧绘制直方图
ax2.hist(np.sin(x), bins=20, orientation='horizontal', color='orange')
ax2.set_title("Distribution")
ax2.grid(alpha=0.3)plt.tight_layout() # 自动调整布局,替代subplots_adjust
plt.show()
width_ratios
控制列宽比例,height_ratios
控制行高比例(适用于多行布局)。plt.tight_layout()
自动优化间距,是快速布局的实用工具。
三、进阶布局:GridSpec实现复杂嵌套
当需要跨行列的子图(如合并单元格的Excel表格),matplotlib.gridspec.GridSpec
是更强大的工具,支持子图跨度与精细尺寸控制。
1. 跨行列布局
创建包含跨列子图的复杂网格:
from matplotlib.gridspec import GridSpec# 创建3行3列的GridSpec,设置行高和列宽比例
gs = GridSpec(3, 3, figure=plt.figure(figsize=(10, 8)),height_ratios=[1, 1, 1],width_ratios=[1, 1, 1],hspace=0.3, wspace=0.2)# 分配子图位置(行索引start:end, 列索引start:end)
ax1 = plt.subplot(gs[0, :]) # 第0行,横跨所有列(0-2)
ax2 = plt.subplot(gs[1, 0:2]) # 第1行,列0-1
ax3 = plt.subplot(gs[1, 2]) # 第1行,列2
ax4 = plt.subplot(gs[2, 0]) # 第2行,列0
ax5 = plt.subplot(gs[2, 1:3]) # 第2行,列1-2# 绘图示例
ax1.plot(x, np.sin(x), color='red')
ax1.set_title("Full Width Plot (Row 0)")ax2.scatter(np.random.rand(50), np.random.rand(50), alpha=0.6)
ax3.bar(['A', 'B', 'C'], [3, 5, 2], color='green')
ax4.hist(np.random.normal(0, 1, 100), bins=15)
ax5.plot(x, np.cos(x), color='purple')# 隐藏冗余坐标轴标签
for ax in [ax2, ax3, ax4, ax5]:ax.tick_params(axis='x', labelsize=8)ax.tick_params(axis='y', labelsize=8)plt.tight_layout()
plt.show()
gs[i, j]
中,i
和j
支持切片(start:end
),表示跨多个索引。- 适合创建“标题图+细节图”的层级布局,突出核心信息。
2. 嵌套GridSpec
在子图中嵌套另一个GridSpec,实现更复杂的层级结构:
fig = plt.figure(figsize=(12, 8))# 外层GridSpec:2行1列
outer_gs = GridSpec(2, 1, figure=fig, height_ratios=[1, 2], hspace=0.4)# 上层子图:简单曲线
ax_top = fig.add_subplot(outer_gs[0])
ax_top.plot(x, np.sin(x), color='blue')
ax_top.set_title("Main Trend")# 下层嵌套GridSpec:1行2列
inner_gs = GridSpecFromSubplotSpec(1, 2, subplot_spec=outer_gs[1], wspace=0.3)
ax_bottom_left = fig.add_subplot(inner_gs[0])
ax_bottom_right = fig.add_subplot(inner_gs[1])ax_bottom_left.plot(x, np.sin(x)*0.5, color='orange')
ax_bottom_left.set_title("Zoomed In")
ax_bottom_right.plot(x, np.sin(x)+1, color='green')
ax_bottom_right.set_title("Shifted Up")plt.tight_layout()
plt.show()
GridSpecFromSubplotSpec
用于在内层子图中创建新网格,继承外层布局的位置。- 适合展示“总览+分面”的数据分析场景(如地图的全局+局部放大)。
四、实用技巧:布局美化与一致性
1. 统一坐标轴范围与标签
多图对比时,保持坐标轴范围一致可避免视觉误导:
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
data = [np.sin(x), np.sin(x)+1, np.sin(x)*2, np.sin(x)-1]for i, ax in enumerate(axes.flat):ax.plot(x, data[i])ax.set_ylim(-2.5, 2.5) # 统一y轴范围ax.set_xlim(0, 2*np.pi) # 统一x轴范围# 共享x轴(仅显示底部子图的x标签)
for ax in axes[:-1, :].flat:ax.set_xticklabels([])# 共享y轴(仅显示左侧子图的y标签)
for ax in axes[:, 1:].flat:ax.set_yticklabels([])plt.tight_layout()
plt.show()
sharex=True
和sharey=True
在plt.subplots()
中可快速实现轴共享:fig, axes = plt.subplots(2, 2, figsize=(10, 8), sharex=True, sharey=True)
2. 全局样式统一
通过plt.rcParams
设置全局样式,确保多图风格一致:
# 设置全局字体和线条样式
plt.rcParams.update({'font.family': 'SimHei', # 支持中文'axes.labelsize': 10,'axes.titlesize': 12,'lines.linewidth': 1.5,'grid.linestyle': ':'
})# 绘图时自动应用全局样式
fig, ax = plt.subplots()
ax.plot(x, np.sin(x))
ax.set_title("中文标题测试") # 正常显示中文
plt.show()
3. 动态调整子图数量
根据数据量动态生成子图(如批量展示10个样本的图像):
n_samples = 6 # 动态数据量
n_cols = 3 # 固定列数
n_rows = (n_samples + n_cols - 1) // n_cols # 计算行数(向上取整)fig, axes = plt.subplots(n_rows, n_cols, figsize=(n_cols*4, n_rows*3))for i in range(n_samples):row = i // n_colscol = i % n_colsaxes[row, col].plot(x, np.sin(x + i*0.5))axes[row, col].set_title(f"Sample {i+1}")# 隐藏多余子图(若样本数不足n_rows*n_cols)
for i in range(n_samples, n_rows*n_cols):row = i // n_colscol = i % n_colsfig.delaxes(axes[row, col])plt.tight_layout()
plt.show()
五、常见问题与解决方案
1. 子图重叠或标签被截断
- 解决方案:使用
plt.tight_layout()
或plt.subplots_adjust(top=0.9)
调整顶部留白。 - 极端情况:减小
figsize
或字体大小(plt.rcParams['font.size'] = 8
)。
2. 中文显示乱码
- 解决方案:设置全局字体为支持中文的字体(如
SimHei
、WenQuanYi Micro Hei
):plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
3. 保存图像时布局错乱
- 解决方案:保存时添加
bbox_inches='tight'
参数:plt.savefig("multi_plot.png", dpi=300, bbox_inches='tight')
总结:Matplotlib多图布局的核心原则
- 简洁优先:规则网格用
plt.subplots()
,复杂跨列用GridSpec
。- 逻辑清晰:按数据关联度排列,同类图表保持样式一致。
- 视觉平衡:避免子图大小差异过大,关键图表可占据更大空间。
- 自动化适配:利用
tight_layout()
和动态计算行数,减少手动调整。
That’s all, thanks for reading~~
觉得有用就点个赞
、收进收藏
夹吧!关注
我,获取更多干货~