分组汇总与透视表
3 道练习题·预计 45 分钟·做对一题解锁下一段
前言
还记得第 1 章那三行 pandas 代码吗?其中有一个 groupby() ,按类别把金额加了起来。不过当时只说了它能做什么,并没有细讲。
这一章,咱们就把它讲透。为什么值得专门花一章来讲呢?因为「每个类别花了多少」「每个月花了多少」「每个月、每个类别各花了多少」,这类问题在数据分析里最常见。而且仔细想想,它们其实都是同一个套路:按某一列分组,每组算一个数,再拼成一张表。
第 4 章里,咱们用 for 循环,把一日三餐挨个算了一遍。学完这一章,这种事一行代码就能做完。
学这一章之前,需要先学完 数据分析课第 5 章 。
目录
这一章会学到:
- 一、分组汇总——
groupby()在做什么 - 二、一次算好几个数——
agg(),按结果排序 - 三、按月汇总——用上日期
- 四、两个方向一起分——
pivot_table() - 五、常见坑
- 六、自己查一次文档——给透视表加上合计
- 七、小结与回看
学完这一章,记账本里任何「每个……花了多少」的问题,你都能用一两行代码算出来。
一、分组汇总
groupby() 到底做了些什么呢?咱们先回到第 1 章的那一行,把它单独拿出来看:
import pandas as pd
ledger = pd.read_csv('ledger_2026.csv')
print(ledger.groupby('category')['cost'].sum())输出的结果:
category
交通 1713.0
医疗 1181.5
娱乐 3178.0
学习 1659.0
居住 32888.5
购物 4992.5
餐饮 23583.0
Name: cost, dtype: float64别看只有一行,它其实做了三件事:
- 分:
groupby('category')按类别,把一千多行分成 7 堆:交通一堆,医疗一堆…… - 算:
['cost'].sum()从每一堆里各自取出金额,加起来。 - 合:把 7 个结果拼成一个 Series,类别就成了左边的索引。
你也许注意到了,结果是按类别的名字排的,而不是按金额。这是因为 groupby() 默认会把分组的名字排好序。想按金额排,后面再接一个 sort_values() 就行,第 1 章就是这么做的。
那么,「算」这一步只能求和吗?当然不是,换成别的方法也行。下面这段代码,就换了两种算法:
import pandas as pd
ledger = pd.read_csv('ledger_2026.csv')
print(ledger.groupby('item')['cost'].mean().round(2).head(4))
print(ledger.groupby('category').size())输出的结果:
item
书 59.81
午饭 26.40
咖啡 22.24
唱歌 105.23
Name: cost, dtype: float64
category
交通 241
医疗 7
娱乐 46
学习 11
居住 36
购物 29
餐饮 984
dtype: int64上面打印了两个结果,咱们分开来看:
- 前一个是按名称分组,再求平均,算的是每种花销平均一笔多少钱。第 4 章用
for循环才算出三餐的平均,这里一行就全算出来了。 - 后一个用的是
size(),它数的是每一组有几行,所以不用取哪一列。结果和第 4 章的value_counts()一样,只是按名字排了序。
总结一下就是:groupby() 后面接什么方法,每一组就算什么。sum() 、mean() 、median() 、max() 、count() ……前几章学过的这些,在这里都能用。
不过到现在为止,每次都只算了一个数。要是想同时看好几个数呢?下一节就来解决。
二、一次算好几个数
上一节,groupby() 后面每次只接一个方法,一次只算一个数。可真正分析的时候,常常想同时知道好几个数。比如每个类别,既想知道记了几笔,又想知道合计和平均。
分三次写,当然也能算出来,就是太啰嗦了。这时候可以用 agg() ,一次全算完。用法是在括号里放一个列表,列表里写上要用的方法名:
import pandas as pd
ledger = pd.read_csv('ledger_2026.csv')
summary = ledger.groupby('category')['cost'].agg(['count', 'sum', 'mean']).round(2)
print(summary)输出的结果:
count sum mean
category
交通 241 1713.0 7.11
医疗 7 1181.5 168.79
娱乐 46 3178.0 69.09
学习 11 1659.0 150.82
居住 36 32888.5 913.57
购物 29 4992.5 172.16
餐饮 984 23583.0 23.97和上一节比一比,结果有什么不一样?上一节只算一个数,结果是一个 Series。这回算了好几个数,结果就成了一张 DataFrame:每个方法占一列,列名就是方法名。
不过,这张表是按类别的名字排的,谁多谁少看不出来。要是按合计排一下序,就好读多了。要注意的是,DataFrame 有好几列,所以它的 sort_values() 得说明按哪一列来排:
import pandas as pd
ledger = pd.read_csv('ledger_2026.csv')
summary = ledger.groupby('category')['cost'].agg(['count', 'sum', 'mean']).round(2)
print(summary.sort_values('sum', ascending=False))输出的结果:
count sum mean
category
居住 36 32888.5 913.57
餐饮 984 23583.0 23.97
购物 29 4992.5 172.16
娱乐 46 3178.0 69.09
交通 241 1713.0 7.11
学习 11 1659.0 150.82
医疗 7 1181.5 168.79排好序以后,把三列放在一起看,就能看出每个类别的「性格」:
- 餐饮:984 笔,每笔平均 24 元。笔数多、单笔小,钱是一点一点花出去的。
- 交通:241 笔,平均每笔才 7 元,大多是坐地铁,一年合计还不到 2000 元。
- 学习、医疗:一年加起来才十来笔,每笔却要一百多元。也就是说,次数少,单笔大。
那么,想省钱的话,该从哪儿下手呢?单笔小、次数多的餐饮,每顿省一点,就能积少成多;单笔大的购物,少买一两件,效果立竿见影。
到这里,分组都是按类别来分的。可要是想知道每个月花了多少呢?下一节就来按月分组。在那之前,先做一道练习:换成按付款方式分组,看看每种付款方式平均一笔多少钱。
读进记账本,按付款方式(payment)分组,算出每种付款方式平均一笔多少钱,保留两位小数,从大到小排序后打印。
运行后的输出应该是:
payment
银行卡 186.18
支付宝 33.00
微信 26.54
现金 23.88
Name: cost, dtype: float64推荐一个,全网最全最友好的中文 AI 编程指南: coding.stormzhang.ai