Matplotlib — 画图、直方图与曲线拟合¶
这一章讲 Python 的画图库 Matplotlib。 作者给它定的调子值得放在最前面: 光看一堆数字,你根本看不到有用的信息;画出来,直到看出有趣的模式为止。 所以这一章不只是 API——它是「用图思考」的入门。
1. 这一章讲什么¶
Matplotlib 是 Python 的画图库,功能多到「几乎什么都能画」;原书只教基本款, 给的学习路径也很务实:「去 Matplotlib 画廊(gallery,官方示例集锦)找长得像你想要的效果, 抄下来改」——画图代码不必优雅,能用就行。1
本章四块:
折线图(plot + show) → 标注(轴标签、单位、图例)
→ 直方图(hist:频率分布) → 曲线拟合(curve_fit)与「怎么判断拟合好不好」
图说:前两块是「画得出」,后两块是「画完之后,图反过来检查你」。
2. 核心原理一:折线图——plot 攒数据,show 一口气显示¶
解决什么问题: 手上一串 (x, y) 数据点,想看清它的形状。
怎么做(主线例子:正弦曲线):
import matplotlib.pyplot as plt # 行业惯例缩写 plt
import numpy as np
x = np.linspace(0, 10, 100) # 0 到 10 均匀取 100 个点
y = np.sin(x) # 每个点算正弦
plt.plot(x, y) # 画线:点按顺序连起来
plt.show() # 显示
走一遍:plt.plot 接受普通列表或 NumPy 数组,每次调用画一条线;
plt.show() 把此前所有 plot 的结果一次性弹出来。想画两条线?plot 两次,show 一次:
x1 = np.linspace(0, 5, 20); y1 = x1**2
x2 = np.linspace(0, 4, 5); y2 = 16 - x2**2
plt.plot(x1, y1)
plt.plot(x2, y2)
plt.show() # 一张图里两条曲线
为什么有效: 「攒完再 show」的模型让你可以自由组合多条曲线、多张子图于一个程序;
边界:plt.rcParams.update({'font.size': 12}) 那类全局设置(书里的示例用来调字号)是可选的,与画图逻辑无关。2
3. 核心原理二:标注——没有轴标签的图一文不值¶
解决什么问题: 一条曲线画出来,读者不知道横轴是什么、纵轴是什么、线代表什么。
怎么做:
plt.plot(t, y, label="y=sin(t)") # 给线起名
plt.xlabel("t [s]") # 横轴:时间,单位秒
plt.ylabel("y [m]") # 纵轴:位移,单位米
plt.legend() # 把线的名字画进图里
plt.show()
加框的原话:「永远给坐标轴加标签。没标签的图毫无价值。别忘了单位!」
曲线名(label + legend())同样不是装饰:多条线同图时,它是唯一的说书人。3
顺带一个书里的细节:图例默认放右上,若和曲线撞了,Matplotlib 会自动挪到左下——小事,但说明它在意可读性。
3.1 标注三件套,各管什么¶
把三个标注动作的分工说透:3
| 动作 | 回答的问题 | 不做的后果 |
|---|---|---|
plt.xlabel("t [s]") |
横轴是什么、单位是什么 | 读者自己猜,十有八九猜错 |
plt.ylabel("y [m]") |
纵轴是什么、单位是什么 | 同上;sin 曲线没有单位就是一条波浪 |
plt.legend() |
图里每条线各代表什么 | 两条线以上时,读者无法引用你的结论 |
书里的原话是命令句:「永远给坐标轴加标签。没标签的图毫无价值。别忘了单位!」
单位写在方括号里是物理学的惯例(t [s] = t,单位秒),照抄即可;你的领域有自己的惯例就跟领域的。3
4. 核心原理三:直方图——看「频率分布」的形状¶
解决什么问题: 一千个测量值逐个看毫无意义;你想看「值落在各区间的有多少」。
怎么做: 用第 09 章的高斯随机数造 1000 个样本,hist 分 10 个桶(bin)统计:
import random
random.seed(1)
nums = [random.gauss(100, 50) for _ in range(1000)] # 均值100、标准差50 的钟形分布
plt.hist(nums, bins=10)
plt.xlabel("Value"); plt.ylabel("Number of occurrences")
plt.show()
直方图还会把数据「吐回来」——这是本章最容易错过的实用点:
n, bins, _ = plt.hist(data, bins=30) # n:每个桶的计数;bins:桶的边界
bin_average = (bins[1:] + bins[:-1]) / 2 # 用第 04 章的切片技巧取每个桶的中点
plt.clf() # 清掉直方图
plt.plot(bin_average, n) # 改画成曲线:直方图的平滑版
plt.clf()(clear figure)把直方图抹掉、只留曲线——「不牺牲直方图的便利,又能得到曲线」。4
4.1 直方图的桶,怎么数¶
plt.hist(nums, bins=10) 的输出分两部分理解:4
书里的高斯例子:1000 个样本(均值 100、标准差 50)分 10 桶——中间几桶(100 附近)柱子最高, 两边递减,钟形的轮廓直接从柱子的起伏里长出来。 这就是为什么直方图是分布的「第一眼检查」:不用算任何统计量,形状已经说明问题。4
4.2 直方图转曲线的切片技巧¶
n, bins, _ = plt.hist(data, bins=30) # 画图的同时拿到数据
bin_average = (bins[1:] + bins[:-1]) / 2 # 相邻边界取中点 → 每个桶的中心位置
plt.clf() # 清掉直方图,画布空了
plt.plot(bin_average, n) # 用桶中心当横轴、计数当纵轴 → 平滑曲线
bins[1:] + bins[:-1] 是第 04 章的切片错位加法,在这里直接复用;
plt.clf()(clear figure)把直方图抹掉,只留曲线——
「既享直方图的自动分桶,又得曲线的干净形状」,书里这句总结就是这两行的价值。4
5. 核心原理四:曲线拟合——以及比参数更重要的「拟合质量」¶
5.1 主线例子:抛物线拟合¶
解决什么问题: 实验测了一组 (t, 位置) 数据点,想反推出物理参数 (初始位置 s₀、初速度 v₀、重力加速度 a)。这是「从数据反推参数」的标准动作——曲线拟合(curve fitting)。
怎么做: 假装我们做了一次抛体实验:真值 s₀=10、v₀=20、a=−9.81,
按公式算每个时刻的位置,再叠加高斯噪声模拟测量误差(random.gauss(0, 2),零均值、标准差 2 的抖动),
然后用 scipy.optimize.curve_fit 反拟合:5
from scipy.optimize import curve_fit
def f(t, s_0, v_0, a): # 物理模型:抛物线
return s_0 + v_0*t + 1/2*a*t**2
popt, pcov = curve_fit(f, t, y, p0=[10, 20, -10]) # p0:给参数的初始猜测
plt.plot(t, y, "o") # 数据点画成圆点
plt.plot(t, f(t, *popt)) # 拟合曲线;*popt 把三个参数拆给 f
走一遍:f 是「带未知参数的模型」;curve_fit 反复调整 (s₀, v₀, a) 使曲线离数据点总偏差最小,
返回两样东西——popt(拟合出的最优参数,书里把它直接织进图例:y(t)=10.01m+19.98m/s·t+…),
和 pcov(协方差矩阵,下面讲)。5
为什么有效: 「最小化总偏差」是最小二乘的思想(书里没点名);模型对、噪声适中时, 拟合值会收敛到真值附近——书里的演示结果确实贴回 10、20、−9.81 附近。
5.2 怎么判断拟合靠不靠谱(本章的灵魂)¶
书里说这一步「非常深刻」,给两条互补的判据:6
- 数学判据:看协方差矩阵
pcov。 「数值高说明拟合不稳,要再仔细看。」 (直觉:pcov 对角元大 ⟺ 参数的方向很「平」——数据对那个参数几乎不敏感,换个噪声样本拟合值就乱跑。) - 工程判据(作者更推的):把数据和拟合曲线画在同一张图里,亲眼看。 「你立刻会看出拟合的质量。一张图胜过一千个数。」
5.3 局部最小值:拟合会悄悄失败¶
curve_fit 的搜索不是全局的——初始猜测 p0 给得不好,算法会卡在一个局部最小值,
返回一条明显对不上数据的曲线(书里图 11.6 的样子)。
作者的坦白很有意思:「我没能故意制造一次坏拟合,只好画了条随机曲线来演示—— 但别以为坏拟合是例外:初始值选得差,它会无缘无故地发生。所以永远要检查拟合参数和协方差矩阵, 或者干脆画出来看。」7
顺带一句作者的吐槽(加框):「有人做曲线拟合,然后宣称自己在做机器学习——我认为不对,这里没有『学习』发生; 当然,机器学习也是在数据里找模式,两者确实有相似。」8
6. 作者的判断与证据¶
书里给了证据的: 正弦图、双曲线图的完整可运行代码;直方图取回数据再画曲线; 拟合例子的参数确实拟回真值附近。25
作者的判断(立场):
- 「画图代码不必优雅」——务实,正确;
- 「一张图胜过一千个数」——本章金句,也是数据工作的通用纪律;
- 「拟合不是机器学习」——他的口径;广义上拟合是最基础的「从数据学参数」,算不算 ML 取决于定义,不必纠结。
「高维」指参数多到无法逐对画图检查(几百个以上)——真到那一步,「画出来看」会失灵。
判断(我们的,不是书里的): 「数据+拟合曲线同图」这一条,等价于机器学习里「先建近似、再验证效果」的那套可视化做法。
作者 2026 年还在手动 curve_fit,而他的读者大概率会走向 sklearn/scipy 全家桶—— 但「永远把模型输出和真实数据画在一起」这个习惯,在任何工具链里都保值。
如果错,会错在: 那时协方差矩阵、残差(数据点离拟合线的距离)分析等数学判据升为主要手段——本章的排序应倒过来。
6.1 本章的最小可运行骨架¶
把全章压成一个能直接跑的最小脚本,收藏备用:2
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y, label="y=sin(x)") # 画线并命名
plt.xlabel("x") # 横轴标签
plt.ylabel("y") # 纵轴标签
plt.legend() # 显示图例
plt.show() # 弹窗显示
七行,含标签和图例——比书里第一个例子多两行,但可直接当模板。
把 np.sin 换成你的数据、把标签换成你的单位,就能用;直方图版把 plot 换成 hist 即可。2
6.2 「图先于数」的工作法¶
书里那句「plot them until you see some interesting patterns」值得单独成章。 科学数据的工作顺序,作者默认是:先画图,再决定算什么——
两条路径的差别在「离群点」上最明显:均值对离群点敏感,图对离群点诚实。 算之前画一下,是成本最低的质检;「一张图胜过一千个数」的下半句是: 一千个数可能合谋骗你,一张图很难。1
7. 边界与局限¶
- 子图(subplot)、散点图
scatter、误差棒 errorbar、(刻度按倍数而不是按等差排的)对数坐标等常用件没讲; curve_fit的数学(最小二乘、优化算法)完全黑箱;- 直方图
bins怎么选没有指导(太少糊、太多噪); - 图的保存(
savefig)没提——本章所有图都靠show()弹窗。
8. 可带走的¶
本章主线例子,一行写完: 正弦:linspace 取点 → plot(x, y) → show();
拟合:curve_fit(f, t, y, p0=…) 得 popt/pcov → 数据点画 "o"、拟合线画实线同图核对。25
- 数字本身没用,画出来看模式——Matplotlib 就是干这个的;画图代码抄画廊、改到能用即可。
plot攒线、show显示;多条线 plot 多次。- 轴标签 + 单位 + 图例,一个不能少;没标签的图毫无价值。
- 直方图看分布;
hist会返回计数和桶边界,能二次利用(直方图转曲线)。 - 拟合 = 模型 + curve_fit + 初始猜测;结果参数织进图例,一眼可读。
- 拟合质量两条判据:协方差矩阵(数学)+ 数据与曲线同图(亲眼);后者优先。
- 初始值差会陷局部最小值——坏拟合不是例外,永远检查。
- 「一张图胜过一千个数」——全数据工作的通用纪律。
9. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 「画出来看模式」的立场 | 11 Matplotlib | text/27-ch11-11-matplotlib.txt:7(搜「plot them」) |
| 画廊学习法 | 11 Matplotlib | text/27-ch11-11-matplotlib.txt:12(搜「gallery」) |
| 正弦主线例子 | 11 Matplotlib | text/27-ch11-11-matplotlib.txt:29(搜「np.sin」) |
| 多线一图、show 的语义 | 11 Matplotlib | text/27-ch11-11-matplotlib.txt:50(搜「several times」) |
| 标注三件套与「没标签一文不值」 | 11 Matplotlib | text/27-ch11-11-matplotlib.txt:71(搜「Labeling」) · text/27-ch11-11-matplotlib.txt:90(搜「worthless」) |
| 直方图与取回数据画曲线 | 11 Matplotlib | text/27-ch11-11-matplotlib.txt:93(搜「Histograms」) · text/27-ch11-11-matplotlib.txt:116(搜「n, bins」) |
| 曲线拟合主线例子 | 11 Matplotlib | text/27-ch11-11-matplotlib.txt:130(搜「Curve Fitting」) · text/27-ch11-11-matplotlib.txt:147(搜「curve_fit」) |
| 协方差矩阵与「画出来看」 | 11 Matplotlib | text/27-ch11-11-matplotlib.txt:174(搜「covariance matrix」) · text/27-ch11-11-matplotlib.txt:182(搜「more than a thousand numbers」) |
| 「拟合不是机器学习」框 | 11 Matplotlib | text/27-ch11-11-matplotlib.txt:184(搜「machine learning」) |
| 局部最小值警告 | 11 Matplotlib | text/27-ch11-11-matplotlib.txt:192(搜「local minimum」) · text/27-ch11-11-matplotlib.txt:197(搜「without any apparent reason」) |
-
出处:「11 Matplotlib」第 5–14 段(
text/27-ch11-11-matplotlib.txt:7,搜「plot them」;画廊建议见第 12 段,搜「gallery」)。 ↩↩ -
出处:「11 Matplotlib」第 16–66 段(
text/27-ch11-11-matplotlib.txt:26,搜「rcParams」;多线示例见第 55–64 段)。 ↩↩↩↩↩ -
出处:「11 Matplotlib」第 71–92 段(
text/27-ch11-11-matplotlib.txt:80,搜「label=」;「别忘单位」见第 91 段,搜「units」)。 ↩↩↩ -
出处:「11 Matplotlib」第 93–129 段(
text/27-ch11-11-matplotlib.txt:110,搜「plt.hist」;取回数据见第 115–128 段,搜「clf」)。 ↩↩↩↩ -
出处:「11 Matplotlib」第 130–176 段(
text/27-ch11-11-matplotlib.txt:147,搜「curve_fit」;参数织进图例见第 165–167 段)。 ↩↩↩↩ -
出处:「11 Matplotlib」第 172–182 段(
text/27-ch11-11-matplotlib.txt:174,搜「High” values」;「一张图胜过一千个数」见第 181 段,搜「more than a thousand numbers」)。 ↩ -
出处:「11 Matplotlib」第 189–197 段(
text/27-ch11-11-matplotlib.txt:192,搜「stuck in a local minimum」;「无缘无故地发生」见第 196 段,搜「without any apparent reason」)。 ↩ -
出处:「11 Matplotlib」第 184–187 段(加框「Curve Fitting and Machine Learning」,
text/27-ch11-11-matplotlib.txt:186,搜「false」)。 ↩