知识点思维导图
29 个知识节点
参考资料
Python(21) - Pandas 数据分析
读完后,你应能完成以下任务:
- 绘制“Python(21) - Pandas 数据分析 / 先给锚点:DataFrame ≈ 你后端拿到的「对象数组」”的关键对象与数据流,解释“边界一:取出一列得到的不是 Python list,而是 Series。”,并用源码位置、日志或 Trace 标注证据。
- 为“Python(21) - Pandas 数据分析 / 读数据:CSV / Excel 一行搞定”设计正常与异常输入,验证“边界二:df.shape 这种是属性(无括号),df.head() 这种是方法(有括号)。”,输出首个偏差位置与回归测试结果。
- 实现“Python(21) - Pandas 数据分析 / 选列与选行:loc / iloc 是新坑”的最小代码或配置,检验“边界三:iloc 切片左闭右开(同 JS),loc 切片左闭右闭(多取一个)。”,输出命令、结果与 Diff,并说明不适用边界。
你在前端天天和「一坨数据」打交道:后端返回的
[{name, age}, {name, age}, ...],你用map/filter/reduce一通处理,再渲染成表格。Pandas 干的就是这件事——只不过它把这个「对象数组」升级成了一张真正的二维表,并内置了 Excel 的「整列运算」和 SQL 的「筛选 / 分组 / 聚合」能力。本篇先用array of objects给你建立直觉,再立刻划清三处关键边界:列不是普通数组(是 Series)、取行有 loc / iloc 两套语法、别用 for 循环遍历(要向量化)。
一、零、它在生态里的位置
Pandas 建在 NumPy(详见第 20 篇,NumPy ≈ 批量运算的「超级 Array」)之上。一句话分工:
| 层 | 类比 | 干什么 |
|---|---|---|
| NumPy | 超级 Array(纯数字) | 底层数值计算引擎 |
| Pandas | 加强版 Excel / SQL | 带列名、带索引的二维表,做数据分析 |
| matplotlib | 后端版 ECharts / D3(详见第 22 篇) | 把表画成图 |
安装与导入(约定俗成 import pandas as pd,几乎所有教程都这么写,照做即可):
pip install pandas # pandas 会自动把 numpy 一起装上
二、先给锚点:DataFrame ≈ 你后端拿到的「对象数组」
前端最熟悉的数据形态,就是后端返回的一个对象数组:
// JavaScript:一个「对象数组」,每个对象是一行
const users = [
{ name: "Tom", age: 18, city: "上海" },
{ name: "Amy", age: 25, city: "北京" },
{ name: "Bob", age: 30, city: "上海" },
]
Pandas 的 DataFrame 就是这个东西的「表格化升级版」——同样是一行行记录,但它知道自己有哪些列、每行有个索引(index):
核心概念就两个,先记牢:
| 概念 | 类比 | 说明 |
|---|---|---|
DataFrame |
整张 Excel 表 / SQL 表 | 二维:有行有列 |
Series |
表里的一列 / 一个带标签的数组 | 一维:df["age"] 取出来就是它 |
index |
Excel 最左边的行号 | 默认 0,1,2…,也可设成日期、ID 等 |
边界一:取出一列得到的不是 Python list,而是
Series。df["age"]不是[18, 25, 30]这种普通列表,而是一个带索引、能直接做整列运算的Series(下一节就用到)。别下意识把它当 list 来for遍历。
三、读数据:CSV / Excel 一行搞定
前端读数据靠 fetch,Pandas 读数据靠一组 read_* 函数,最常用的是 CSV 和 Excel:
读进来后,第一件事永远是先「体检」,这几个方法你会天天用:
边界二:
df.shape这种是属性(无括号),df.head()这种是方法(有括号)。这俩很容易混。判断标准:要不要「执行一个动作」——取已有的形状信息是属性,让它去算/去取前几行是方法。
四、选列与选行:loc / iloc 是新坑
4.1 选列:像取对象属性
4.2 选行:用 loc / iloc(这里和 JS 思路不同,重点)
JS 里取第 0 行你写 users[0]。Pandas 里 df[0] 会报错或被当成取列,取行必须用 loc / iloc:
| 需求 | JS | Pandas |
|---|---|---|
| 取一列 | users.map(u => u.age) |
df["age"] |
| 取第 N 行(按位置) | users[n] |
df.iloc[n] |
| 取前 3 行 | users.slice(0, 3) |
df.iloc[0:3](左闭右开) |
| 按标签取行 | (无直接对应) | df.loc[label] |
边界三:
iloc切片左闭右开(同 JS),loc切片左闭右闭(多取一个)。这是 Pandas 最反直觉的点之一。记法:iloc是「位置」走数组那套,loc是「标签」,标签区间天然包含两端。
五、筛选:布尔索引 ≈ filter(但写法是新的)
前端筛选用 arr.filter(条件)。Pandas 用一种叫布尔索引的写法——先算出一列「True/False」,再用它去框选行:
// JavaScript
const adults = users.filter(u => u.age >= 18) // 单条件
const shAdults = users.filter(u => u.age >= 18 && u.city === "上海") // 多条件
两个高频坑,前端 100% 会踩:
常用的还有 isin(≈ JS 的 includes 判断)和 str 系列(针对字符串列):
六、整列运算:向量化,别写 for(性能关键边界)
这是 Pandas 最该改掉的 JS 习惯。前端给每个对象加字段,你会 for 一遍。Pandas 里直接对整列运算,底层走 NumPy 批量处理,又快又短:
// JavaScript:循环逐个处理
users.forEach(u => { u.ageNextYear = u.age + 1 })
const names = users.map(u => u.name.toUpperCase())
边界四(性能):能向量化就别 for。
for i in range(len(df)): df.loc[i, ...]这种逐行循环在 Pandas 里又慢又难读,几万行就明显卡。心智转变:从「遍历每个元素」改成「对整列下达一条指令」——这正是 NumPy/Pandas 的灵魂。真要逐行处理逻辑时用df.apply(函数),但优先找向量化写法。
七、分组聚合:groupby ≈ SQL 的 GROUP BY
这是 DataFrame「像 SQL」的部分。需求:按城市分组,算每个城市的平均年龄、人数。SQL 你会写,对照看 Pandas:
-- SQL
SELECT city, AVG(age) AS avg_age, COUNT(*) AS cnt
FROM users
GROUP BY city;
常用聚合函数和 SQL 一一对应:mean() / sum() / count() / max() / min() / median()。
value_counts() 是个超高频快捷方式——统计某列每个值出现多少次(≈ GROUP BY x COUNT(*) 再排序):
八、缺失值处理:真实数据一定有 NaN
读进来的真实数据,几乎必然有空值。Pandas 用 NaN(Not a Number)表示缺失,约等于 JS 的 null / undefined,但判空方式不同:
边界五:判断空值不能用
== None或== NaN。NaN == NaN结果竟然是False(IEEE 浮点规定,NaN 不等于任何值,包括它自己)。所以必须用df["x"].isnull()/.notnull()来判空,不要手写等号比较。
九、串起来:一个最小数据分析流程
把前面的拼成你真实会写的样子——读数据 → 清洗 → 派生 → 分组 → 导出:
这套流程就是阶段四的核心肌肉记忆:读 → 看 → 洗 → 算 → 出。出图的部分交给下一篇 matplotlib(第 22 篇,后端版 ECharts/D3)。
十、和前端思维的速查对照
| 操作 | JavaScript(对象数组) | Pandas |
|---|---|---|
| 数据形态 | [{...}, {...}] |
DataFrame |
| 一列数据 | arr.map(o => o.x) |
df["x"](是 Series) |
| 行数 | arr.length |
df.shape[0] / len(df) |
| 取第 N 行 | arr[n] |
df.iloc[n] |
| 筛选 | arr.filter(o => o.x > 1) |
df[df["x"] > 1] |
| 多条件筛选 | && / || |
& / |(每段加圆括号) |
| 新增/派生列 | forEach 里赋值 |
df["new"] = df["x"] + 1(整列) |
| 映射转换 | arr.map(...) |
整列运算 / df["x"].apply(fn) |
| 包含判断 | [...].includes(o.x) |
df["x"].isin([...]) |
| 分组统计 | reduce 手撸 |
df.groupby("x").agg(...) |
| 计数 | 手动累加 | df["x"].value_counts() |
| 判空 | o.x == null |
df["x"].isnull()(不能用 ==) |
| 排序 | arr.sort((a,b)=>...) |
df.sort_values("x") |
十一、总结
- 先给锚点:DataFrame ≈ 你后端拿到的「对象数组」:| Series | 表里的一列 / 一个带标签的数组 | 一维:df["age"] 取出来就是它 |
- 读数据:CSV / Excel 一行搞定:边界二:df.shape 这种是属性(无括号),df.head() 这种是方法(有括号)。
- 选列与选行:loc / iloc 是新坑:边界三:iloc 切片左闭右开(同 JS),loc 切片左闭右闭(多取一个)。
- 整列运算:向量化,别写 for(性能关键边界):这是 Pandas 最该改掉的 JS 习惯。
- 分组聚合:groupby ≈ SQL 的 GROUP BY:这是 DataFrame「像 SQL」的部分。
- 缺失值处理:真实数据一定有 NaN:边界五:判断空值不能用 == None 或 == NaN。
学完自测
选择所有正确答案;提交后逐项核对判断依据。