【Python系列课程】24 Matplotlib(三):子图布局、图像保存与综合实战
> Matplotlib三部曲的最后一篇,我们解决两个核心问题:如何在一个画布上放多个子图(subplot布局),如何把画好的图保存为图片文件(savefig)。最后用一个综合可视化项目,把三篇所学全部串联起来。
一、子图布局:plt.subplot()
subplot() 让你在一个画布上排列多个子图,格式为 subplot(行数, 列数, 编号)。
1.1 基础用法:2x2 四宫格
`python
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
x = np.linspace(-3, 3, 50)
y1 = 2*x + 1
y2 = x**2
y3 = np.sin(x)
y4 = np.tan(x)
plt.figure(figsize=(10, 8))
2行2列,第1个子图
plt.subplot(2, 2, 1)
plt.plot(x, y1, color='blue')
plt.title('直线 y=2x+1')
2行2列,第2个子图
plt.subplot(2, 2, 2)
plt.plot(x, y2, color='red')
plt.title('抛物线 y=x²')
2行2列,第3个子图
plt.subplot(2, 2, 3)
plt.plot(x, y3, color='green')
plt.title('正弦曲线 y=sin(x)')
2行2列,第4个子图
plt.subplot(2, 2, 4)
plt.plot(x, y4, color='purple')
plt.title('正切曲线 y=tan(x)')
plt.tight_layout() # 自动调整子图间距
plt.show()`
> [图1:2×2四宫格子图——四张图排列在同一个画布上,分别展示四种函数]
1.2 编号规则:按行从上到下,从左到右
`
subplot(2, 2, 1) subplot(2, 2, 2)
┌──────────────┐ ┌──────────────┐
│ #1 │ │ #2 │
└──────────────┘ └──────────────┘
subplot(2, 2, 3) subplot(2, 2, 4)
┌──────────────┐ ┌──────────────┐
│ #3 │ │ #4 │
└──────────────┘ └──────────────┘`
1.3 不规则布局:跨行跨列
subplot() 的一个强大特性是:可以跨越多个格子。
`python
plt.figure(figsize=(12, 8))
第1行,占2列(即2行3列布局中的第1、2、3个格子合成一个大图)
plt.subplot(2, 1, 1) # 2行1列,第1个 → 占上半部分
plt.plot(x, y1, color='blue')
plt.title('上半部分大图')
第2行,占3列中的第1列
plt.subplot(2, 3, 4) # 2行3列,第4个(即第2行第1列)
plt.plot(x, y2, color='red')
plt.title('左下')
第2行,第2列
plt.subplot(2, 3, 5)
plt.plot(x, y3, color='green')
plt.title('中下')
第2行,第3列
plt.subplot(2, 3, 6)
plt.plot(x, y4, color='purple')
plt.title('右下')
plt.tight_layout()
plt.show()`
> [图2:不规则子图布局——上方一张大图占满整行,下方三张小图并排]
关键理解:subplot(行数, 列数, 编号) 中的行数列数定义了网格系统,编号表示你当前要填充第几个格子。你可以在不同位置定义不同的网格系统(比如上半部分用2行1列,下半部分用2行3列)。
1.4 tight_layout() —— 子图间距的救星
不加 tight_layout() 时,子图之间可能会重叠:
`python
没有 tight_layout → 标签可能重叠
plt.subplot(2, 2, 1)
plt.plot(x, y1)
plt.xlabel('这个标签可能会和上方的图重叠')
有 tight_layout → 自动调整间距,不重叠
plt.tight_layout()
`二、保存图像:plt.savefig()
画好的图不只是用来在屏幕上看的——保存为图片文件才能写进报告、发邮件、做PPT。
2.1 基础用法
`python
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3, 3, 50)
y = np.sin(x)
plt.plot(x, y, color='blue')
plt.title('sin曲线')
保存为PNG图片
plt.savefig('sin_curve.png')
print("图片已保存为 sin_curve.png")
`> [图3:savefig保存的sin_curve.png文件在Finder/资源管理器中的预览]
2.2 保存前先清理画布:plt.clf()
如果你在循环中画多张图,一定要在每次画新图前清理画布:
`python
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3, 3, 50)
循环画图并保存
for i, func in enumerate([np.sin, np.cos, lambda x: x**2]):
plt.clf() # 清除当前画布!否则图会叠加
y = func(x)
plt.plot(x, y)
plt.title(f'图{i+1}')
plt.savefig(f'figure_{i+1}.png')
print(f'figure_{i+1}.png 已保存')
plt.clf() 只清除数据,不关闭画布窗口
如果想关闭整个画布(释放内存),用 plt.close()
`2.3 savefig() 的关键参数
| 参数 | 默认值 | 说明 |
|
|
--|
|
| fname | — | 文件名(支持 .png .jpg .svg .pdf 等) |
| dpi | 100 | 分辨率,数字越大图片越清晰(打印用300) |
| bbox_inches | None | 'tight' 表示裁掉多余白边 |
| transparent | False | True 表示透明背景 |
| facecolor | 'white' | 背景颜色 |
`python
高清导出(适合打印/出版)
plt.savefig('high_quality.png',
dpi=300, # 300dpi = 打印级别
bbox_inches='tight', # 裁掉多余白边
facecolor='white', # 白色背景
edgecolor='none') # 无边框
`2.4 保存为矢量格式(SVG/PDF)
矢量格式可以无限放大不失真,适合论文和出版:
`python
plt.savefig('chart.svg') # SVG矢量图
plt.savefig('chart.pdf') # PDF矢量图`
> [图4:同一张图的PNG(放大后模糊)和SVG(放大后清晰)对比]
三、综合实战:数据分析全流程可视化
把 Matplotlib 三篇所学的全部内容串起来,做一个完整的项目:
需求:分析某公司三个部门(技术、销售、市场)四个季度的业绩数据,生成一份包含以下内容的可视化报告:
1. 各部门各季度业绩的折线趋势图
2. 各部门总业绩的对比条形图
3. 各部门的月度业绩分布散点图
4. 将以上三张图放在一个画布上,保存为高清图片
`python
import matplotlib.pyplot as plt
import numpy as np
========== 准备工作 ==========
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
np.random.seed(42)
========== 模拟数据 ==========
departments = ['技术', '销售', '市场']
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
各部门各季度业绩数据(百万)
performance = {
'技术': [120, 135, 128, 156],
'销售': [200, 185, 210, 230],
'市场': [80, 90, 95, 110]
}
各月随机波动数据(用于散点图)
monthly = {}
for dept in departments:
base = np.mean(performance[dept]) / 3 # 季度均值 ÷ 3 ≈ 月均值
monthly[dept] = base + np.random.randn(12) * 10 # 12个月 + 噪声
========== 创建画布 ==========
plt.figure(figsize=(14, 10))
========== 子图1:折线趋势图(2行2列,第1个) ==========
plt.subplot(2, 2, 1)
colors = ['blue', 'green', 'orange']
markers = ['o', 's', '^']
for i, dept in enumerate(departments):
plt.plot(quarters, performance[dept],
color=colors[i], marker=markers[i],
linewidth=2.5, markersize=8,
label=dept)
plt.xlabel('季度', fontsize=12)
plt.ylabel('业绩 (万元)', fontsize=12)
plt.title('各部门季度业绩趋势', fontsize=14, fontweight='bold')
plt.legend(fontsize=10)
plt.grid(True, alpha=0.3, linestyle='--')
在每个数据点上标注数值
for i, dept in enumerate(departments):
for j, (q, val) in enumerate(zip(quarters, performance[dept])):
plt.text(j, val + 3, str(val), ha='center', fontsize=8, color=colors[i])
========== 子图2:总业绩对比条形图(2行2列,第2个) ==========
plt.subplot(2, 2, 2)
totals = [sum(performance[d]) for d in departments]
bars = plt.bar(departments, totals,
color=['skyblue', 'lightgreen', 'orange'],
edgecolor='black', linewidth=1.5, width=0.5)
plt.xlabel('部门', fontsize=12)
plt.ylabel('年度总业绩 (万元)', fontsize=12)
plt.title('各部门年度总业绩对比', fontsize=14, fontweight='bold')
在条形上方标注数值
for bar, total in zip(bars, totals):
plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 5,
str(total), ha='center', fontsize=11, fontweight='bold')
========== 子图3:月度分布散点图(2行2列,第3个) ==========
plt.subplot(2, 2, 3)
months = np.arange(1, 13)
for i, dept in enumerate(departments):
plt.scatter(months, monthly[dept],
s=80, c=colors[i], marker=markers[i],
alpha=0.7, edgecolors='black', linewidth=0.5,
label=dept)
plt.xlabel('月份', fontsize=12)
plt.ylabel('月业绩 (万元)', fontsize=12)
plt.title('各部门月度业绩分布', fontsize=14, fontweight='bold')
plt.xticks(months)
plt.legend(fontsize=10)
plt.grid(True, alpha=0.3, linestyle='--')
========== 子图4:年度汇总表(2行2列,第4个) ==========
plt.subplot(2, 2, 4)
plt.axis('off') # 关闭坐标轴,做成"表格"效果
构建表格数据
table_data = []
table_data.append(['部门', 'Q1', 'Q2', 'Q3', 'Q4', '总计', '平均'])
for dept in departments:
row = [dept] + performance[dept] + [sum(performance[dept]),
f"{np.mean(performance[dept]):.0f}"]
table_data.append(row)
创建表格
table = plt.table(cellText=table_data,
cellLoc='center',
loc='center',
colWidths=[0.12]*7)
table.auto_set_font_size(False)
table.set_fontsize(11)
table.scale(1, 2) # 调整行高
plt.title('年度业绩汇总表', fontsize=14, fontweight='bold', y=1.02)
========== 全局调整 ==========
plt.suptitle('XX公司2024年度业绩分析报告', fontsize=18, fontweight='bold', y=1.01)
plt.tight_layout()
保存高清图片
plt.savefig('业绩分析报告.png',
dpi=300,
bbox_inches='tight',
facecolor='white')
print("✅ 业绩分析报告.png 已生成!")
plt.show()`
> [图5:综合实战最终输出——一张画布上四张子图 + 表格 + 公司标题的完整报告]
输出效果:
`
✅ 业绩分析报告.png 已生成!`
这张图包含了:
- 折线图:看趋势变化
- 条形图:比总量大小
- 散点图:看分布离散度
- 表格:展示精确数字
四、Matplotlib 进阶路线图
三篇Matplotlib的内容足以应对80%的日常可视化需求。如果你想继续深入,以下是进阶方向:
| 方向 | 说明 | 推荐学习 |
|
|
|
|
| Seaborn | 统计图表(箱线图、热力图、小提琴图) | Seaborn官方教程 |
| Pandas绘图 | df.plot() 直接画图,语法更简洁 | Pandas文档 |
| 交互式图表 | Plotly、Bokeh,鼠标悬停显示数据 | Plotly Express |
| 地图可视化 | Folium、GeoPandas | GeoPandas文档 |
| 动画 | Matplotlib的 FuncAnimation | Matplotlib Animation指南 |
五、本文总结
| 知识点 | 核心要点 |
|
--|
|
| 子图布局 | plt.subplot(行, 列, 编号),编号按行从左到右 |
| 不规则布局 | 同一画布内定义不同网格系统,灵活排列 |
| 防止重叠 | plt.tight_layout() |
| 清除画布 | plt.clf() 清除数据,plt.close() 关闭窗口 |
| 保存图片 | plt.savefig('文件名.png', dpi=300, bbox_inches='tight') |
| 矢量格式 | .svg 和 .pdf 无限缩放不失真 |
六、Matplotlib三篇回顾 + 24篇系列总结
Matplotlib三篇回顾
| 篇目 | 核心内容 | 关键函数 |
|
|
|
|
| 第22篇 | 绘图基础 | plot(), scatter(), bar(), imshow(), figure() |
| 第23篇 | 坐标轴与标注 | xlabel(), ylabel(), xticks(), yticks(), legend(), text(), axes(), gca(), spines |
| 第24篇(本篇) | 子图与导出 | subplot(), tight_layout(), clf(), close(), savefig() |
24篇系列完整回顾
| 阶段 | 篇目 | 主题 |
|
|
|
|
| Python基础 | 01~03 | 入门、环境搭建、六大数据类型 |
| 流程控制 | 04~06 | 运算符、条件语句、循环语句 |
| 函数 | 07~08 | def基础、lambda、作用域、闭包 |
| 面向对象 | 09~10 | 类与对象、封装继承多态 |
| 工程能力 | 11~13 | 异常处理、模块与包、文件操作 |
| 正则表达式 | 14~15 | 元字符、量词、环视、日志实战 |
| NumPy | 16~17 | 数组创建、索引切片、向量化、广播、聚合 |
| Pandas | 18~21 | Series/DataFrame、数据清洗、分组聚合、文件读写 |
| Matplotlib | 22~24 | 基础图表、坐标轴定制、子图与导出 |
恭喜你!如果你一路跟着代码敲下来,现在已经具备了用Python做数据分析和可视化的完整能力。下一步可以去Kaggle找真实数据集练手,或者用Pandas + Matplotlib 分析自己的工作数据——学以致用,才是最好的学习方式。
动手练习
1. 创建一个 2×3 的子图布局,用 np.random.randn(100) 生成6组不同的正态分布数据,分别画出6张直方图(plt.hist()),用 tight_layout() 调整间距。
2. 画一张折线图,分别保存为 .png(dpi=300)和 .svg 两种格式,对比文件大小。
3. 在循环中画3张图,每张画完用 plt.savefig() 保存,画下一张前用 plt.clf() 清空画布。
4. 设计一个你自己的综合可视化项目:至少包含2个子图(一个折线图 + 一个条形图),添加标题、图例、坐标轴标签,最后保存为高清PNG。
系列完结 感谢你的阅读,祝你在Python和数据分析的道路上越走越远!

浙公网安备 33010602011771号