编程学习网 > 编程语言 > Python > Pandas GroupBy提速神技:40分钟到4秒!
2026
03-06

Pandas GroupBy提速神技:40分钟到4秒!


我先说结论哈:一个破 groupby.apply,我从 40 分钟干到 4 秒,真的不是标题党…就是那种干到怀疑人生的那种优化。

那天晚上我刚准备关电脑回家,运营同学在工位后面拍我一下说:东哥,那个用户行为报表,你这脚本是不是挂了?跑了半小时了还没出。 我心里咯噔一下:完了,又是我那坨 pandas 屎山在作妖。

数据量不算吓人,大概两三千万行,一张日志表,长这样:

  • user_id 用户
  • event 事件类型
  • cost 花了多少钱
  • ts 时间戳

业务小哥的需求也很朴素: 每个用户统计一下各种事件次数、总金额、首末时间,顺带算个留存啥的。听起来就像“很适合用 groupby 啊,对吧”,于是年轻时候的我就写出了下面这坨——典型的“学会了一个 groupby,然后用到死”的代码:


这个玩意在我们那台 8 核 32G 的机器上,跑全量数据大概 40 分钟。 而且最离谱的是:越到晚上数据越多,报表就越慢,运营越烦我,我就越想转行卖煎饼。

后来我冷静下来,把这段代码翻来覆去看了几遍,问题其实特别“pandas 教科书式”:

  1. groupby.apply 把每个 user_id 的小 DataFrame 丢给 Python 层函数去算,完全吃不到底层 C 的红利。
  2. 函数里面再套一个 for e in user_df['event'],双重 Python 循环,GIL 锁死。
  3. 动态往 Series 上加字段,pandas 一直在帮你“扩容”,非常费。

就好比你明明有个搅拌机,非要自己拿筷子一点点搅。

我当时的想法就是:能不能把所有逻辑都塞回 pandas 自己的算发里,让它里面那些 C 写的轮子转起来? 于是开始一点点拆那个函数,先把最简单的统计抽出来,看能不能用 agg 搞定:


这里有两个点:

  • 把“一个 groupby 做一堆事”写进 agg 里,一次扫描就把总金额、首末时间都算完;
  • active_days 实在没现成函数,只能来个小 lambda,但它只跑一次,不是每个事件都跑,损失还行。

然后是那个 event_cnt,原来我在 apply 里手动 dict 计数。这个完全没必要,人家 groupby + size 就能办到,而且顺带还能帮你 pivot:


到这一步,其实我们已经把那个大 calc_user_stats 拆成了两块纯 pandas 的矢量操作

  • base:每个用户的整体统计
  • event_cnt:每个用户各种事件的次数

最后再拼回来就行了:


看起来也没多高深对吧,就是从“一个 apply 全干”变成“两个 groupby + 一次 merge”。 但是——就是这一手,直接从 40 分钟干到了 7 秒多。 我当时在终端敲了个 %%time,愣是又跑了三遍确认自己没看错。

后来又手贱往里加了几个小优化,就从 7 秒挤到了 4 秒,顺便说下都是啥:

第一,小字段提前转 category。 我们的 user_idevent 其实重复率特别高,转换下能减少分组成本:


第二,groupby 的时候关掉没必要的排序:


第三,那个 active_days 其实可以提前算好,不用在 agg 里再来个 lambda:


把 agg 里最后一个自定义函数也弄没了,整条链路就全是“底层有 C 实现”的操作了。

有同学在群里问我:“东哥,我这不也是 groupby 嘛,为什么你那就能从 40 分钟到 4 秒?” 我就一句话:你用的是“groupby + Python 函数”,我用的是“groupby + 内置 agg/size/unstack/merge”,看起来都叫 groupby,实则一个骑共享单车,一个开高铁。

还有几个小点我顺嘴说一下,你们可以对照自己代码挨个查:

  • 一个字段多次 groupby:很多人写业务逻辑的时候,先按 user_id 分一次算 A 指标,再分一次算 B 指标,其实可以一次 groupby 用多个聚合函数搞定。
  • 在 groupby 里拼字符串、做正则:这玩意放外面,提前算好一个列,用 map 或 merge 回填;
  • 在循环里频繁 df[df.xxx == ...] 再 groupby:这是真·内耗,我见过有人 for 每个用户再切一片 DataFrame…当场 CPU 热得能煎蛋。

那次优化完之后,运营那边报表页面刷新直接从“点完先去倒杯水”变成了“刚准备打开微信就出结果了”, 我自己看着监控面板上那条 CPU 曲线从山脉变成小土包,心情非常复杂:一半是爽,一半是想抽几年前写这段代码的自己。

顺带感慨一句,这种“默认写法坑死人”的事,我在别的技术栈也干过,比如 SpringBoot 那堆默认配置不改,线上迟早出事 pandas 也一样,你只要手欠写了个 groupby.apply(lambda df: …) + for,它就会默默陪你 CPU 炖 40 分钟。

以上就是“Pandas GroupBy提速神技:40分钟到4秒!的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。

扫码二维码 获取免费视频学习资料

Python编程学习

查 看2022高级编程视频教程免费获取