Python pandas groupby分组计数如何优化以缩短运行输出时间
性能优化方案
问题现状
现有多周期分组计数脚本存在严重性能瓶颈:
- 待处理DataFrame规模达3300万行
- 单次循环耗时超300ms
- 业务侧需累计运行超5万次循环,原逻辑总耗时超2天
测试用样例数据构造代码如下:
# 构造与实际场景耗时水平一致的样例数据 import pandas as pd import numpy as np df = pd.DataFrame(np.random.randint(3, size=(400000,1)), columns=['type']) df['class'] = np.random.randint(1, 7, df.shape[0]) df['country'] = np.random.randint(1, 12, df.shape[0]) df['period'] = np.random.randint(2010, 2018, df.shape[0]) df['season'] = np.random.randint(1, 4, df.shape[0])
原实现逻辑代码如下:
%%time # 周期参数 tr1_sta = 2011 tr1_end = 2016 h0 = 'type' h1 = 'class' h2 = 'country' holder = [h0,h1,h2] df = (df.set_index(holder).assign(tr1_tc = df[(df['period'].between(tr1_sta, tr1_end))].groupby(holder)['season'].count()).reset_index())
原逻辑性能瓶颈分析
原代码慢的核心原因是做了大量无意义的重复操作:
- 每次循环都对3300万行的原始大表做布尔索引切片、groupby聚合、set_index/reset_index操作,大表IO和计算开销极高
- 每次循环都直接修改原始大表,DataFrame新增列会触发全表内存拷贝,额外开销巨大
- 所有循环的聚合维度完全一致,仅统计的时间范围不同,反复扫描全表做重复聚合完全浪费算力
具体优化手段
- 预聚合全量统计结果,彻底避免循环内扫描大表
所有统计的聚合维度固定为[type, class, country],仅统计的年份区间不同。可以先一次性计算出「分组维度+单年份」的计数值,预聚合结果的规模仅为3种type * 6种class * 11种country * 最大年份跨度,最多数千行,和原始3300万行的规模相比可以忽略。后续所有时间区间的统计,都在这个千行级的预聚合结果上做区间列求和即可,完全不需要触碰原始大表。 - 低基数列转分类类型,压缩内存提升计算速度
用到的所有维度列(type、class、country、period、season)都是低基数枚举值,转成pandas的category类型可以把内存占用降到原来的1/10以下,全表聚合、关联的速度会进一步提升。 - 消除循环内大表修改操作,单次关联回表
所有需要计算的周期统计值,全部在小规模的预聚合结果集上计算完成,最后只做一次和原始大表的merge关联,把所有需要的新增列一次性挂到原表上,避免循环内反复触发大表内存拷贝。
优化后实现代码
%%time # 周期参数 tr1_sta = 2011 tr1_end = 2016 h0 = 'type' h1 = 'class' h2 = 'country' holder = [h0,h1,h2] # 步骤1:低基数列转分类压缩内存 for col in holder + ['period', 'season']: df[col] = df[col].astype('category') # 步骤2:一次性预聚合所有分组+单年份的计数,结果仅千行规模 pre_agg = df.groupby(holder + ['period'], observed=True)['season'].count().unstack('period', fill_value=0) # 步骤3:在预聚合结果上计算目标区间的总计数,单次耗时不到1ms tr1_tc = pre_agg.loc[:, tr1_sta:tr1_end].sum(axis=1).rename('tr1_tc').reset_index() # 步骤4:一次性关联回原始表,全程仅做一次大表操作 df = df.merge(tr1_tc, on=holder, how='left')
优化效果
- 40万行样例数据下,原实现单次运行耗时约18ms,优化后全流程(含预聚合+目标区间计算+回表关联)总耗时不到4ms
- 3300万行生产数据场景下,预聚合操作单次耗时仅数秒,后续5万次区间计算全部在千行级结果集上运行,总耗时可控制在1分钟以内,对比原逻辑2天的累计耗时,性能提升超1000倍。
内容的提问来源于stack exchange,提问作者ManOnTheMoon
相关产品推荐
相关产品推荐

