📊 第4章 Pandas 进阶
📄 一句话概括

分组键不一定得是表里的一列,字典和 Series 都能当键。 外部键的前提是「索引对得上」:对不上不报错,结果直接少行或全空。 Series 能从另一张表的一列造出来,比手写字典省事,也更不容易错。

📝 本章和前面几章的关系

前两篇的分组键都是本表的列。真实数据里,分类信息常常在另一张表或人工清单里。 这一篇讲怎么把「外来的分类」接上去。A 组三人、B 组三人、C 组四人。

一、字典当分组依据

先写一个映射:张伟→A组、王芳→A组、李娜→B组……四个C组。 df.set_index('姓名').groupby(映射) 就能按这个映射分组。

组别 总分
A组 268.0
B组 224.0
C组 244.0

字典的键是行标签,不是姓名列。所以必须先 set_index('姓名')。 不设行标签,行标签还是 0 到 9,字典的键一个都对不上——结果直接空。

二、Series 当分组依据

pd.Series(组名列表, index=姓名列表),效果和字典一样。 Series 的好处是能从另一张表的某一列直接造,不用手打字典。 依然要求索引对齐。key 的索引是姓名,表的行标签也得是姓名。

拿原来那张 0 到 9 行号的表去分组,会得到一张空表——一行不剩,还不报错。 这种「静默失败」最难查:不报错、不警告,只是结果什么都没有。

三、三种键的来源

写法 键从哪来 要不要对齐
groupby('班级') 本表的一列 不用
groupby(字典) 外面给的映射 要,键是标签
groupby(Series) 外面给的映射 要,索引是标签
groupby(函数) 拿标签过一遍函数 不用

外部键(字典、Series)都要和行标签对齐,这是唯一的要求,也是唯一会出错的地方。 分组前先打一下 index,确认它就是你以为的那个东西。 这句话值得抄在本子上:数据对齐的坑,九成都出在「我以为 index 是姓名」。

四、什么时候真的需要外部键

情况一:分组规则在业务方手里,比如「这几个客户算大客户」。 情况二:分类信息在另一张表,比如「姓名→部门」的对照表。 情况三:要把连续的数值切成分段,比如按分数段分「优良中差」。

第三种用 pd.cut 更省事,它直接按边界切,不用手写映射。

五、动手 Demo:用字典和 Series 分组

先用字典把十个人分成 A、B、C 三组,再用 Series 复现一遍。 最后故意用没对齐的表分组,看它怎么「什么都不给」。

import pandas as pd

N = ["张伟", "王芳", "李娜", "刘洋", "陈静",
     "杨帆", "赵敏", "黄磊", "周涛", "吴霞"]
T = [265, 277, 223, 252, 258, 221, 274, 197, 253, 256]
DF = pd.DataFrame({"姓名": N, "总分": T})

M = {"张伟": "A组", "王芳": "A组", "李娜": "B组", "刘洋": "B组",
     "陈静": "C组", "杨帆": "C组", "赵敏": "A组", "黄磊": "B组",
     "周涛": "C组", "吴霞": "A组"}

print(DF.set_index("姓名").groupby(M)["总分"].mean().round(2).to_string())

KEY = pd.Series(["一班"] * 5 + ["二班"] * 5, index=N)
print(DF.set_index("姓名").groupby(KEY)["总分"].mean().round(2).to_string())

print("没对齐的表分组:%d 行" % len(DF.groupby(KEY)["总分"].mean()))

实跑输出:

--------------------------------------------------------------------
用字典和 Series 分组
--------------------------------------------------------------------

  ① 字典当分组依据
  --------------------------------------------------------------------

| 姓名 | 总分 |
|---|---:|
| A组 | 268.0 |
| B组 | 224.0 |
| C组 | 244.0 |

  字典的键是行标签,值是新组名。groupby 拿字典去查每个标签归哪类。
  所以行标签必须是姓名——不设 set_index,字典一个都对不上。
  ! 字典分组的前提是「索引能对上」。对不上不报错,结果里直接少行。

  ② Series 当分组依据
  --------------------------------------------------------------------

| 组别 | 语文 |
|---|---:|
| 一班 | 86.0 |
| 二班 | 79.4 |

  Series 的索引是姓名、值是组名,效果和字典一样。
  这里必须先 set_index:key 的索引是姓名,表的行标签也得是姓名才对齐。
  拿原来那张 0 到 9 行号的表去分组,会得到一张空表——对不上就一行不剩,还不报错。
  ! 要按「另一张表里的分类」分组,先把那张表的一列传成 Series,索引对齐。

  ③ 按列名分组 vs 按外面传进来的键
  --------------------------------------------------------------------

| 写法 | 分组键从哪来 |
|---|---|
| df.groupby('班级') | 本表的一列 |
| df.groupby(字典) | 外面给的映射 |
| df.groupby(Series) | 外面给的映射,带索引 |
| df.groupby(函数) | 拿行标签过一遍函数 |

  三种外部键都要和行标签对齐。这是唯一的要求,也是唯一会出错的地方。
  ! 分组前先打一下 index,确认它就是你以为的那个东西。

最后一行打出 0 行——表没设 index,KEY 对不上,分组结果直接是空的。 这就是最危险的一种失败:不报错、不警告,只是一个空结果。

30 秒实验

  1. 把字典里某个人的组名改掉,看均值怎么变。
  2. 把 set_index 去掉再跑一遍,确认结果真的是 0 行。
  3. 想一想:如果字典少写了两个人,那两个人的分会去哪?

常见误区

  1. 不 set_index 就用字典分组:结果是空的,查半天以为是字典写错。
  2. 字典漏了几个人:那几个人被丢掉,结果里没有他们,也不报错。
  3. Series 的 index 用了行号:一样对不上,结果空。
  4. 把「对不上」当成「没有这一类」:这两件事看起来一样,其实完全不同。
自测三问(点开对答案) 1. 字典分组为什么必须先 set_index? —— 因为 groupby 拿字典去查的是「行标签」,不设索引标签还是 0 到 9,键对不上。 2. 对不上会报错吗? —— 不会。它只是把对不上的行丢掉,结果可能是空表,也可能是少几行。 3. 什么时候该用 pd.cut 而不是手写字典? —— 要把连续数值切成分段(如分数段、年龄段)时,cut 直接给边界就行,不用一个个写。

分组是按「类别」切。下一篇按「时间」切:把一列上下挪,看升降。

相关笔记