第 02 章 · 学习路线与知识地图
这一章要解决什么:在动手写代码之前,先看清楚"我接下来要学的 37 章之间是什么关系"。 这一章没有代码,但值得花 20 分钟读完 —— 它能帮你判断"现在卡住了,应该回头看哪一章"。
2.1 一句话说清这本书在教什么
把一堆 CSV 变成"我敢下判断的证据"。
这句话拆开,就是全书的五个部分:
一堆 CSV ──[读进来]──> 一张表 ──[清洗]──> 可信的数据 ──[统计/画图]──> 证据 ──[组织]──> 报告
│ │ │ │ │
│ 第 12 章 第 30 章 第 32~38 章 第 39 章
│ 第 23 章 第 32~34 章
│
└─ 你在真实工作中拿到的东西(可能是 Excel 导出的、传感器记的、数据库导的)每一章都在推进这条链路的一小段。所以你随时可以问自己:我现在在链路的哪一步?
2.2 知识地图
点开看完整地图(38 章的分组与依赖关系)
┌─────────────────────────────────────────────────────────────────────┐
│ 起步(第 01–02 章) │
│ 01 环境安装 ← 必须最先完成 │
│ 02 知识地图 ← 你正在读 │
└─────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────┐
│ A · Python 基础(第 03–16 章)—— 学"怎么指挥计算机" │
│ │
│ 数据从哪来 怎么处理它 怎么组织代码 │
│ ──────────── ────────────── ────────────── │
│ 03 变量与类型 06 列表 10 函数 │
│ 04 数值与算术 07 循环与切片 11 推导式与解包 │
│ 05 字符串 08 条件判断 13 异常处理 │
│ 09 字典与集合 14 模块与导入 │
│ 15 类与对象 ★补充 │
│ 16 类型注解 ★补充 │
│ │
│ 12 文件读写与 csv ← A 组的收尾,也是转向数据处理的桥梁 │
└─────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────┐
│ B · NumPy(第 17–22 章)—— 学"怎么让计算机一次算一整列" │
│ │
│ 17 第一个数组 20 向量化运算(告别 for) │
│ 18 创建与形状 21 广播机制 ★ 标准化就是它 │
│ 19 索引与切片 22 聚合与布尔索引 │
│ ★ 视图 vs 拷贝 │
└─────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────┐
│ C · Pandas(第 23–31 章)—— 学"怎么处理真实的表" │
│ │
│ 23 第一个 DataFrame 27 派生列 │
│ 24 数据速览三件套 28 分组聚合 ★ 切-算-合 │
│ 25 选行选列 loc/iloc 29 时间序列与重采样 │
│ 26 筛选与排序 30 缺失值处理 ★★ 全书最重要的坑 │
│ 31 保存结果 │
└─────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────┴─────────────────────┐
↓ ↓
┌───────────────────────────┐ ┌───────────────────────────┐
│ D · 数据清洗(32–34) │ │ E · 可视化(35–38) │
│ │ │ │
│ 32 Z 分数 │ │ 35 折线图(趋势) │
│ 33 IQR ★ 比 Z 稳健 │ │ 36 散点图(关系) │
│ 34 异常处置策略 │ │ 37 热力图(冗余/泄漏) │
│ ★ 删/截/标/单 │ │ 38 箱线图(分布对比) │
└───────────────────────────┘ └───────────────────────────┘
└─────────────────────┬─────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────┐
│ F · 阶段目标(第 39 章) │
│ 把上面全部拼起来 → 一条命令产出《电池数据体检报告》+ 4 张图 │
└─────────────────────────────────────────────────────────────────────┘图里带 ★ 的是这一章最关键的知识点,★★ 表示"坑"的严重程度 —— 自测时优先看它。第 15、16 章(类与对象、类型注解)是整章补充内容,别跳过。
2.3 五个部分分别在解决什么问题
理解"每一部分为什么存在",比记住语法更有用 —— 因为它决定了你什么时候该用它。
A 组 · Python 基础(03–16):把想法写成指令
它解决的问题:计算机不会读心术。你要用它能懂的方式,把你的处理逻辑一步步说清楚。
核心转折点:第 06~10 章。学完这几章,你就能写出"能跑"的程序了:
caps = [2.50, 2.46, 2.42, 2.38] # 列表:存一串数据
for c in caps: # 循环:对每一个做点什么
soh = c / 2.5 # 算术
if soh < 0.85: # 条件:分情况处理
print(f"SOH {soh:.4f} → 预警") # 输出这一段最常见的卡点:下标从 0 开始、切片不含右端、缩进就是语法。 这三个想通了,A 组就通了。
为什么要有第 12 章(读文件):到这一步你终于能处理"真实存在的数据",而不是自己手写的小列表。 这也是你第一次遇到"路径""编码""读进来全是字符串"这三个真实世界的麻烦。
B 组 · NumPy(17–22):从"能算"到"算得快"
它解决的问题:Python 的 for 循环在处理上百万个数字时太慢了。
# Python 列表:每个元素都要单独跑一次解释器
result = []
for c in caps: # 100 万个元素 → 几秒钟
result.append(c / 2.5)
# NumPy 数组:一行,底层是 C 循环,快几十倍
result = caps / 2.5 # 100 万个元素 → 几毫秒三个最关键的知识点:
| 概念 | 在哪一章 | 为什么重要 |
|---|---|---|
| 视图 vs 拷贝 | 19 | 改一个切片会连带改原数组 —— 这是 NumPy 最难查的 bug |
| 向量化 | 20 | "整列一次算完"的思维,是后面所有数据处理的基础 |
| 广播 | 21 | 它同时就是 Z-score 标准化(StandardScaler)的数学本体 |
为什么 AI 开发必须先学 NumPy:PyTorch 的 Tensor、TensorFlow 的 EagerTensor、 JAX 的 Array —— 它们的 API 都是照着 NumPy 设计的。 学会 NumPy 的"整体运算"思维,再学这些框架会轻松得多。
C 组 · Pandas(23–31):处理"真实的表"
它解决的问题:真实数据不是干净的数值矩阵,而是混合类型、有缺失、有索引、有日期的表格。
df = pd.read_csv("battery.csv") # 一行读入,类型自动推断
df[df["soh"] < 0.85] # 按条件筛行
df.groupby("cell_id")["soh"].mean() # 按电芯分组算均值
df["soh"].resample("100D").mean() # 按时间重采样
df["x"].fillna(df["x"].median()) # 填缺失这一段的信息量最大,也最容易"看懂了但写不出来"。建议:
- 第 25、26 章(选取与筛选)反复练,因为它们出现在后面每一段代码里;
- 第 28 章(
groupby)是分水岭,理解"切-算-合"这个心智模型; - 第 30 章(缺失值)里有全书最重要的一个坑:先排序、再按组插值。请务必亲手跑一遍那个例子。
D 组 · 数据清洗(32–34):判断"哪些数字不可信"
它解决的问题:数据里总是有错的。但"发现异常"和"处理异常"是两件独立的事。
| 阶段 | 方法 | 关键认知 |
|---|---|---|
| 发现 | 物理规则(值为负、超上限) | 不需要统计,最可靠 |
| 发现 | Z 分数(32) | 均值和标准差会被异常值自己带偏 |
| 发现 | IQR(33) | 比 Z 分数稳健得多 |
| 处理 | 四种策略(34) | 没有"正确"策略,只有"和目的匹配"的策略 |
最重要的两个结论:
- 物理规则先于统计规则 —— 容量为负这种事,不该等统计方法来抓;
- 工业项目的默认答案是"标记 + 单独分析",而不是直接删 —— 因为删数据不可逆。
E 组 · 可视化(35–38):把数字变成"别人能看懂的东西"
它解决的问题:0.9071 这个数字没人有感觉;一条曲线配上一条 0.80 的参考线,所有人都懂了。
四种图各自回答什么问题(这个对应关系比语法重要):
| 图 | 回答的问题 | 章节 |
|---|---|---|
| 折线图 | "随时间/循环怎么变化?" | 35 |
| 散点图 | "这两个量有关系吗?" | 36 |
| 热力图 | "这十几个字段里,哪些是重复的?" | 37 |
| 箱线图 | "几组的分布有什么差别?" | 38 |
最容易犯的错:把相关系数当因果。第 36 章会用一个真实例子拆穿"共同趋势造成的假相关"。
F 组 · 阶段目标(39):把前面全部拼起来
第 39 章没有新知识,它做的是工程组织:
def load_and_clean(path): ... # 读 + 清洗,返回 (df, 日志)
def fig_fade(df, out_path): ... # 一张图 = 一个函数
def build_findings(df, log, ...): ... # 报告要的数字集中算一次
def write_report(f, out_path): ... # 只负责拼 Markdown,不做计算
def main(): # 只做编排,没有计算
...三条纪律:main() 里只有调用没有计算;一个图一个函数;报告里每个数字都来自 build_findings()。
为什么这很重要:这样"图、数字、结论"永远不会互相矛盾。数据一换,重跑即可。
2.4 三条学习路线
路线 A:零基础系统学习(约 30 小时)
这是本书设计的默认路线。
| 阶段 | 章节 | 时间 | 重点 |
|---|---|---|---|
| 起步 | 01–02 | 1 小时 | 环境一定要装通 |
| A 组 | 03–16 | 10 小时 | 每章都跑代码、做练习,不要跳 |
| B 组 | 17–22 | 3 小时 | 重点:视图 vs 拷贝、广播 |
| C 组 | 23–31 | 7 小时 | 重点:groupby、缺失值那个坑 |
| D 组 | 32–34 | 2 小时 | 重点:为什么 IQR 比 Z 分数稳健 |
| E 组 | 35–38 | 3 小时 | 重点:四种图各回答什么问题 |
| F 组 | 39 | 2 小时 | 亲手跑出报告 |
节奏建议:每天 2 小时、每周 5 天,三周走完。 卡住了怎么办:先回头看上一章的"常见坑",90% 的问题在那里。
路线 B:有编程基础,快速上手(约 12 小时)
| 阶段 | 章节 | 说明 |
|---|---|---|
| 起步 | 01 | 环境必须装通(这一步和有没有基础无关) |
| 快扫 | 03–10 | 只看每章的「常见坑」和练习,遇到 Python 特有的写法(切片、enumerate、默认参数陷阱)再细读 |
| 必读 | 11–12 | 推导式、with open、路径处理 —— 这几个是 Python 的标志性写法 |
| 必读 | 13–16 | 异常、模块、类、类型注解 —— 决定代码能否维护 |
| 正常学 | 17–31 | NumPy + Pandas,这是新东西,按顺序读 |
| 正常学 | 32–39 | 清洗、可视化、报告 |
可以跳的:第 04 章(算术)、第 05 章(字符串基础)如果你已熟悉; 不能跳的:第 19 章(视图 vs 拷贝)、第 21 章(广播)、第 30 章(插值的坑)—— 这几个和语言基础无关,是新知识。
路线 C:只想做数据分析(约 15 小时)
| 阶段 | 章节 | 说明 |
|---|---|---|
| 起步 | 01 | 环境 |
| 最小基础 | 06、09、10、12 | 列表、字典、函数、读文件 —— 够用就行,别陷进去 |
| 可选 | 11、15、16 | 想代码写得好一点就读 |
| 主体 | 17–31 | NumPy + Pandas,按顺序 |
| 主体 | 32–39 | 清洗、可视化、报告 |
| 跳过 | 03–05、07–08、13–14 | 遇到不懂的语法再回来查 |
注意:即使"只想做数据分析",第 06/09/10/12 章也不能跳 —— 否则你会在第 26 章看到 df[df["a"] > 1] 时完全不知道发生了什么。
2.5 五类常见的"卡住"
提前知道这些,就不会在遇到时怀疑自己。
卡点 1:环境装不上,反复报模块找不到
这不是你的问题,是所有人的必经之路。 环境问题占了新手卡壳原因的 80%。
怎么办:
- 严格按第 01 章的检查清单逐条确认;
- 关键诊断命令:
python -c "import sys; print(sys.executable)"—— 看路径里有没有.venv; - 实在修不好,删掉
.venv重建(这是虚拟环境最大的好处:随时可以推倒重来)。
卡点 2:A 组后期开始"看得懂但写不出来"
原因:从第 12 章起,代码开始变长(读文件 → 处理 → 输出),而你还在一个个语法点地记。
怎么办:
- 不要背语法,背结构。任何数据处理脚本都是"读 → 处理 → 写"三段式;
- 把第 12 章的示例关掉书自己默写一遍,卡在哪就翻哪一章;
- 抄代码不丢人,但抄完要能说出每一行在干什么。说不出来的行,就是你要回去补的章。
卡点 3:Pandas 链式操作看不懂
长这样的代码:
df[df["soh"] < 0.85].groupby("cell_id")["resistance_mohm"].mean().sort_values(ascending=False).head(5)怎么办:从左往右读,每一步都想"现在手上是什么东西":
df 一张表
df[df["soh"] < 0.85] 还是表,只是行少了(筛选)
.groupby("cell_id") 一个"分组对象",还没算
["resistance_mohm"] 指定要算哪一列
.mean() 现在才真的算 —— 变成 Series(每支电芯一个数)
.sort_values(ascending=False) 排序
.head(5) 取前 5练习方法:把上面这一行拆成 6 行、每行 print 一次中间结果。 看得见就懂了。第 25 章的练习里就有这个训练。
卡点 4:报错看不懂
记住:Python 的报错信息是从下往上看。
Traceback (most recent call last): ← 上面是"调用栈",先别看
File "demo.py", line 12, in <module> ← 出错位置:文件 + 行号、哪个函数
df["soh"] / df["cap_rated"]
~~~~~~~~~~^~~~~~~~~~~~~~~~~~
TypeError: unsupported operand type(s) for /: 'str' and 'float' ← ★ 最后一行最重要
└── 类型错误:字符串和浮点数不能相除读法三步:
- 最后一行:什么错(异常类型 + 消息);
- 倒数第二段
File:错在哪(文件 + 行号); ^箭头:指向具体哪个字符。
然后问自己:"我需要的东西,现在是什么类型?" 九成的问题用 print(type(x)) 就能定位。
第 13 章会专门讲这个,并把常见异常类型列成表。
卡点 5:学会语法了,但拿到自己的数据还是不知道从哪下手
这是"没有形成流程"的表现。 解决办法:把下面这套流程用成肌肉记忆。
点开:拿到任何一张表的标准流程
# ① 先看清楚它是什么(第 23、24 章)
df = pd.read_csv("your_data.csv")
print(df.shape) # 多大规模?
print(df.head()) # 长什么样?
df.info() # 每列什么类型、有没有缺失?
df.describe() # 数值范围合不合理?
# ② 逐列问"它是什么"(第 24 章)
# 标识列(每行都不同)→ 不进模型,但可以当分组键
# 类别列(取值很少) → 要编码
# 时间列 → 可以派生成"距开始多少天"
# 标签列 → 是预测目标,绝不能当特征
# ③ 查数据质量(第 30、32、33 章)
df.isna().sum() # 缺多少、缺在哪
(df["数值列"] < 0).sum() # 物理规则
# 再用 IQR 或 Z 分数找统计意义上的异常
# ④ 开始回答你的业务问题(第 26、28、35~38 章)
df.groupby("分组键")[...].mean() # 分组统计
# 挑一张合适的图(趋势→折线、关系→散点、分布→箱线、多字段→热力图)这四步走完,再谈建模。 跳过任何一步,后面都会付出更大的代价。
2.6 本章检查清单
已勾选 0 / 0 项
本章练习
本章没有编程练习,但有三个值得花 10 分钟想清楚的问题:
思考题 1:为什么先学 NumPy,再学 Pandas?
提示:想一下 Pandas 的 DataFrame 底层是用什么存的。
参考答案
因为 Pandas 的底层就是 NumPy 数组。
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3]})
print(type(df["a"].to_numpy())) # <class 'numpy.ndarray'>推论(这些都会在后面反复出现):
- Pandas 的整列运算就是 NumPy 的向量化:
df["soh"] / 2.5之所以能一行算完,是因为底层走了 NumPy。 - NumPy 的坑会原样继承到 Pandas:第 19 章的"视图 vs 拷贝"在 Pandas 里表现为
SettingWithCopyWarning(第 25 章)。 - 报错信息会混着出现:你可能看到
ValueError: operands could not be broadcast together, 这是 NumPy 的报错,但触发它的是 Pandas 的代码。 - 缺了 NumPy 就学不懂"为什么":知道
axis=0是什么,才知道df.mean(axis=0)在干什么。
反过来说,为什么不能只学 NumPy? 因为:
- NumPy 数组只能存同一种类型(第 17 章),而真实的表是数值+类别+时间混在一起;
- NumPy 没有列名、没有索引标签、没有缺失值标记(
NaN处理很难用)、没有日期类型。
所以顺序是:先学 NumPy 打地基(第 17–22 章),再用 Pandas 盖楼(第 23–31 章)。
思考题 2:为什么第 30 章(缺失值)被称为"全书最重要的坑"?
提示:想一想"这份数据是怎么组织的"。
参考答案
因为数据是"多支电芯竖着摞在一起"的,而插值需要"前后邻居"。
行号 cell_id cycle capacity_ah
861 C001 862 1.9945 ← C001 的最后一圈
862 C002 1 NaN ← 这一格缺失了
863 C002 2 2.5046 ← C002 的第二圈如果你写 df[["capacity_ah"]].interpolate(),Pandas 会把整列当成一条长序列, 于是用第 861 行的 1.9945(C001 的末圈)和第 863 行的 2.5046(C002 第 2 圈)去插第 862 行:
整表插值 → 2.2496 ❌ 被上一支电芯拖低了
组内插值 → 2.5046 ✅ 与 C002 第 2 圈接近,物理上合理差异 0.2550 Ah,相当于额定容量的 10.2%。这个错误不会报错、不会报警, 只是让你的数据悄悄偏了 10% —— 后面所有结论都建立在错误的数据上。
正确写法(两个动作缺一不可):
df = df.sort_values(["cell_id", "cycle"]) # ① 先排序
df[NUM] = (df.groupby("cell_id")[NUM] # ② 再按电芯分组
.transform(lambda g: g.interpolate(limit_direction="both")))更重要的那条认知(这才是"最重要的坑"的真正含义):
数据处理的正确写法,不由"这次跑出来一样不一样"决定,而由"物理上什么才是合理的邻居"决定。
在这份数据里,随机撒的缺失碰巧很少落在电芯边界,所以两种做法只差 1 格。 如果你用"结果一不一样"来判断对错,就会得出"两种都行"的错误结论 —— 然后在真实数据上翻车。
思考题 3:为什么报告里的数字不能手写?
提示:想想"数据更新一次会发生什么"。
参考答案
因为手写的数字一定会过时,而过时的数字比没有数字更危险。
场景:你手写了一份报告说"平均 SOH 0.9071,r = -0.94"。 三个月后数据更新了,你重跑脚本,图画出来了(fig.savefig 是代码自动跑的), 但报告里的文字还是旧的 —— 图上显示 0.8912,报告里写着 0.9071。
读者看到的是一份自相矛盾的报告。更糟的是:没人会发现,因为两边看起来都很正常。
第 39 章的解法:把所有要写进报告的数字,集中在一个函数里算一次:
def build_findings(df, log, fade, scat, corr, box) -> dict:
"""所有要写进报告的数字,集中算一次。"""
return {
"n_rows": len(df),
"r_soh_r": scat["r_soh_resistance"],
"worst_cell": per_cell["soh_mean"].idxmin(),
...
}
def write_report(f, out_path):
"""只负责拼 Markdown,不做任何计算。"""
text = f"...全批平均 SOH 从 {f['fade']['soh_first']:.4f} 降到 ..."
out_path.write_text(text, encoding="utf-8")这样图、数字、结论三者永远一致:数据一换,重跑即可,不会出现"图变了但结论没变"。
这条纪律在真实项目里的名字叫"单一数据源(single source of truth)": 任何一个数字,在整个系统里只应该有一个地方产生它。
面向 AI 开发:为什么要"先看图,再建模"
这一章讲的知识地图,在真实的 AI 项目里对应一条重要的工程经验:
模型的上限,由数据决定;而数据的上限,由你"看懂它"的程度决定。
一个典型的时间分配对比:
| 阶段 | 新手的时间 | 有经验的人的时间 |
|---|---|---|
| 看数据、查质量、画图 | 10% | 50~60% |
| 特征工程 | 10% | 20~30% |
| 选模型、调参 | 80% | 10~20% |
新手把大部分时间花在调模型上,而有经验的人知道:在脏数据上调参,是在优化一个错误的答案。
本书刻意把 "数据处理 + 可视化" 放在 "模型" 之前,也正是这个原因 —— 第 39 章的"阶段目标"是一份报告,而不是一个模型。
下一章:第 03 章 · 变量、对象与数据类型 —— 正式开始写代码。