分组键不一定得是表里的一列,字典和 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 秒实验
- 把字典里某个人的组名改掉,看均值怎么变。
- 把 set_index 去掉再跑一遍,确认结果真的是 0 行。
- 想一想:如果字典少写了两个人,那两个人的分会去哪?
常见误区
- 不 set_index 就用字典分组:结果是空的,查半天以为是字典写错。
- 字典漏了几个人:那几个人被丢掉,结果里没有他们,也不报错。
- Series 的 index 用了行号:一样对不上,结果空。
- 把「对不上」当成「没有这一类」:这两件事看起来一样,其实完全不同。
自测三问(点开对答案)
1. 字典分组为什么必须先 set_index? —— 因为 groupby 拿字典去查的是「行标签」,不设索引标签还是 0 到 9,键对不上。 2. 对不上会报错吗? —— 不会。它只是把对不上的行丢掉,结果可能是空表,也可能是少几行。 3. 什么时候该用 pd.cut 而不是手写字典? —— 要把连续数值切成分段(如分数段、年龄段)时,cut 直接给边界就行,不用一个个写。分组是按「类别」切。下一篇按「时间」切:把一列上下挪,看升降。
- ☐ (本篇完,下一篇 4.9 数据移位:shift 与升降比较)