Matplotlib 绘制多图(Subplots)完全指南
在数据分析、科研论文、报告展示中,多图组合 是最常见的可视化需求。Matplotlib 提供了强大的 subplots 系统,支持 任意布局、共享坐标轴、嵌套、复杂组合。本教程从入门到专业,带你一文掌握所有技巧。
一、核心函数对比
| 函数 | 用途 | 推荐场景 |
|---|---|---|
plt.subplot() | 快速网格布局 | 简单 2×2 图 |
plt.subplots() | 返回 fig, axes 数组 | 推荐,复杂图 |
plt.subplot2grid() | 不规则布局 | 仪表盘 |
GridSpec | 高级灵活布局 | 跨行/跨列 |
二、方法 1:plt.subplots()(推荐!面向对象)
import matplotlib.pyplot as plt
import numpy as np
# 创建 2行2列子图
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
# 填充数据
x = np.linspace(0, 10, 100)
axes[0, 0].plot(x, np.sin(x), 'b-')
axes[0, 0].set_title('sin(x)')
axes[0, 1].plot(x, np.cos(x), 'r--')
axes[0, 1].set_title('cos(x)')
axes[1, 0].scatter(x[::5], np.sin(x[::5]))
axes[1, 0].set_title('散点图')
axes[1, 1].hist(np.random.randn(1000), bins=30, color='purple', alpha=0.7)
axes[1, 1].set_title('直方图')
plt.tight_layout() # 自动调整间距
plt.show()
优势:
axes是数组,可循环、可索引,适合批量操作
三、方法 2:plt.subplot()(快速网格)
plt.figure(figsize=(10, 8))
plt.subplot(2, 2, 1) # 第1个子图
plt.plot(x, np.sin(x))
plt.title('sin(x)')
plt.subplot(2, 2, 2)
plt.plot(x, np.cos(x), 'r--')
plt.title('cos(x)')
plt.subplot(2, 2, 3)
plt.bar(['A','B','C'], [3,7,5])
plt.title('柱状图')
plt.subplot(2, 2, 4)
plt.pie([30,20,25,25], labels=['A','B','C','D'], autopct='%1.1f%%')
plt.title('饼图')
plt.tight_layout()
plt.show()
适合快速原型,不推荐复杂项目
四、共享坐标轴(sharex, sharey)
fig, axes = plt.subplots(2, 1, sharex=True, sharey=True, figsize=(8, 6))
axes[0].plot(x, np.sin(x), 'b-')
axes[0].set_title('共享 X 和 Y 轴')
axes[1].plot(x, np.cos(x), 'r-')
axes[1].set_xlabel('x') # 只有底部显示标签
plt.tight_layout()
plt.show()
节省空间,适合对比趋势
五、不规则布局:GridSpec
from matplotlib.gridspec import GridSpec
fig = plt.figure(figsize=(12, 8))
gs = GridSpec(3, 3, figure=fig)
# 左边大图(跨2行2列)
ax1 = fig.add_subplot(gs[0:2, 0:2])
ax1.plot(x, np.sin(x**2), 'purple')
ax1.set_title('主图:sin(x²)')
# 右上小图
ax2 = fig.add_subplot(gs[0, 2])
ax2.bar(['A','B','C'], [3,7,5])
ax2.set_title('柱状图')
# 右中
ax3 = fig.add_subplot(gs[1, 2])
ax3.pie([30,20,25,25], autopct='%1.1f%%')
ax3.set_title('饼图')
# 底部整行
ax4 = fig.add_subplot(gs[2, :])
ax4.hist(np.random.randn(1000), bins=50, color='skyblue', alpha=0.7)
ax4.set_title('底部直方图')
plt.tight_layout()
plt.show()
六、嵌套子图(子图中的子图)
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(x, np.sin(x), 'b-', linewidth=2)
# 在主图上添加小图(inset)
ax_inset = fig.add_axes([0.6, 0.6, 0.25, 0.25]) # [左, 下, 宽, 高]
ax_inset.plot(x[:20], np.cos(x[:20]), 'r-')
ax_inset.set_title('局部放大', fontsize=10)
plt.show()
七、批量操作所有子图
fig, axes = plt.subplots(2, 3, figsize=(12, 6))
axes = axes.flatten() # 转为1D数组
data_funcs = [np.sin, np.cos, np.tan, np.exp, np.log, lambda x: x**2]
for i, (ax, func) in enumerate(zip(axes, data_funcs)):
try:
ax.plot(x, func(x))
ax.set_title(func.__name__ if hasattr(func, '__name__') else f'f{i}')
except:
ax.text(0.5, 0.5, 'Error', ha='center', va='center', transform=ax.transAxes)
plt.tight_layout()
plt.show()
八、总标题与子标题
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
for i, ax in enumerate(axes.flatten()):
ax.plot(x, np.sin(x + i))
ax.set_title(f'子图 {i+1}')
fig.suptitle('多子图总标题示例', fontsize=18, fontweight='bold', y=1.02)
plt.tight_layout(rect=[0, 0, 1, 0.96])
plt.show()
九、完整专业示例(论文级多图)
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.gridspec import GridSpec
plt.style.use('seaborn-v0_8')
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
x = np.linspace(0, 2*np.pi, 200)
y1, y2 = np.sin(x), np.cos(x)
# 创建复杂布局
fig = plt.figure(figsize=(14, 10))
gs = GridSpec(3, 3, figure=fig, hspace=0.4, wspace=0.4)
# 主图:双曲线
ax_main = fig.add_subplot(gs[0:2, 0:2])
ax_main.plot(x, y1, '#1f77b4', linewidth=3, label=r'$\sin(x)$')
ax_main.plot(x, y2, '#ff7f0e', linewidth=3, linestyle='--', label=r'$\cos(x)$')
ax_main.set_title('三角函数波形', fontsize=14, fontweight='bold')
ax_main.set_xlabel('角度 (rad)')
ax_main.set_ylabel('函数值')
ax_main.legend(fontsize=11)
ax_main.grid(True, alpha=0.3)
# 右上:散点
ax_scatter = fig.add_subplot(gs[0, 2])
ax_scatter.scatter(x[::10], y1[::10], c=y2[::10], cmap='plasma', s=60, edgecolors='k')
ax_scatter.set_title('散点图\n(颜色表示 cos)')
plt.colorbar(ax_scatter.collections[0], ax=ax_scatter, shrink=0.8)
# 右中:柱状
ax_bar = fig.add_subplot(gs[1, 2])
categories = ['Q1', 'Q2', 'Q3', 'Q4']
values = [85, 92, 78, 88]
bars = ax_bar.bar(categories, values, color=['#2ca02c','#d62728','#9467bd','#8c564b'])
ax_bar.set_title('季度销售额')
ax_bar.set_ylim(0, 100)
for bar in bars:
h = bar.get_height()
ax_bar.text(bar.get_x() + bar.get_width()/2, h+1, f'{h}',
ha='center', va='bottom', fontsize=10)
# 底部:直方图 + 箱线图
ax_hist = fig.add_subplot(gs[2, 0])
data = np.random.normal(100, 15, 1000)
ax_hist.hist(data, bins=30, color='skyblue', edgecolor='black', alpha=0.7)
ax_hist.set_title('正态分布直方图')
ax_hist.set_xlabel('值')
ax_hist.set_ylabel('频数')
ax_box = fig.add_subplot(gs[2, 1:3])
ax_box.boxplot([np.random.normal(0,1,100), np.random.normal(1,1.5,100)],
labels=['组A', '组B'], patch_artist=True,
boxprops=dict(facecolor='lightblue', color='black'),
medianprops=dict(color='red'))
ax_box.set_title('箱线图对比分析')
# 总标题
fig.suptitle('Matplotlib 多图布局专业示例', fontsize=20, fontweight='bold', y=0.98)
plt.savefig('multiplot_pro.png', dpi=300, bbox_inches='tight', facecolor='white')
plt.show()
十、多图速查表(收藏用)
# 1. 标准网格
fig, axes = plt.subplots(2, 3, figsize=(12, 6))
# 2. 共享轴
fig, axes = plt.subplots(2, 1, sharex=True)
# 3. 不规则布局
gs = GridSpec(3, 3)
ax1 = fig.add_subplot(gs[0:2, 0:2])
# 4. 嵌套图
ax_inset = fig.add_axes([0.7, 0.7, 0.2, 0.2])
# 5. 总标题
fig.suptitle('总标题', fontsize=16, y=1.02)
# 6. 自动间距
plt.tight_layout()
十一、常见问题解决
| 问题 | 解决方案 |
|---|---|
| 子图重叠 | plt.tight_layout() 或 fig.subplots_adjust() |
| 标签被裁剪 | plt.savefig(..., bbox_inches='tight') |
| 颜色重复 | 用 axes.flatten() 循环设置 |
| 布局太挤 | 增大 figsize 或用 GridSpec |
| 坐标轴标签重复 | 用 sharex=True |
十二、推荐布局模板
| 用途 | 布局 |
|---|---|
| 论文插图 | 2×2 或 1×3 + 共享轴 |
| 仪表盘 | GridSpec + 跨行跨列 |
| 时序对比 | sharex=True 多行 |
| 相关性分析 | plt.subplots(3,3) + 对角线 |
官方文档
- Subplots:https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplots.html
- GridSpec:https://matplotlib.org/stable/api/_as_gen/matplotlib.gridspec.GridSpec.html
- 示例图库:https://matplotlib.org/stable/gallery/subplots_axes_and_figures/index.html
总结:三步打造专业多图
# 1. 规划布局
fig, axes = plt.subplots(2, 2, figsize=(12, 8), sharex=True)
# 2. 填充内容
for ax in axes.flatten():
ax.plot(...)
# 3. 美化输出
fig.suptitle('标题')
plt.tight_layout()
plt.savefig('fig.png', dpi=300, bbox_inches='tight')
一键生成 2×2 多图:
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
axes[0,0].plot(x, np.sin(x)); axes[0,0].set_title('sin')
axes[0,1].plot(x, np.cos(x)); axes[0,1].set_title('cos')
axes[1,0].hist(np.random.randn(1000)); axes[1,0].set_title('hist')
axes[1,1].scatter(x[::5], np.sin(x[::5])); axes[1,1].set_title('scatter')
plt.tight_layout(); plt.show()
需要我为你:
- 生成 6种经典多图布局模板?
- 制作 Jupyter 多图交互小工具?
- 输出 LaTeX/PPT 可用多图代码?
告诉我你的需求!