如何提升同时对多列执行求和的pandas groupby操作运行速度?
Pandas多列分组求和性能优化方案
pandas原生groupby求和本身是C实现的非迭代逻辑,你当前的性能瓶颈来自5个分组键的哈希计算开销(多为字符串类型时开销更高)、以及70万结果组的元数据管理开销,以下是可直接落地的优化方案,按改造成本从低到高排序:
- 方案1:分组键转分类类型(改造成本最低,可提速30%~80%)
字符串类型的分组键哈希计算开销远大于分类类型,提前转换后不需要修改原有聚合逻辑,示例代码如下:
group_cols = ['key','code','name','period','agg_metric'] # 提前把所有分组键转成分类类型,大幅降低哈希分组开销 for col in group_cols: df[col] = df[col].astype('category') # 执行原有分组逻辑即可 df = df.groupby(group_cols, sort=False, observed=True, dropna=False)[['metricA','metricB']]\ .sum().reset_index()
- 方案2:使用numba加速的聚合引擎(改造成本低,可提速1~3倍)
需要pandas版本≥1.3,开启并行和无GIL模式,针对数值求和场景优化明显,示例代码如下:
# 需提前安装numba:pip install numba df = df.groupby(group_cols, sort=False, observed=True, dropna=False)[['metricA','metricB']]\ .sum(engine='numba', engine_kwargs={'nogil': True, 'parallel': True}).reset_index()
- 方案3:使用numpy原生向量化实现(无额外依赖,可提速2~4倍)
完全绕过pandas groupby的额外开销,用唯一值ID+加权计数实现分组求和,逻辑稳定,示例代码如下:
import numpy as np group_cols = ['key','code','name','period','agg_metric'] # 把分组列转成结构化数组,生成唯一分组ID group_arr = df[group_cols].to_records(index=False) _, group_idx, inverse_idx = np.unique(group_arr, return_index=True, return_inverse=True) # 对两列做分组求和 sumA = np.bincount(inverse_idx, weights=df['metricA'].values) sumB = np.bincount(inverse_idx, weights=df['metricB'].values) # 拼接最终结果 res = df.loc[group_idx, group_cols].reset_index(drop=True) res['metricA'] = sumA res['metricB'] = sumB df = res
- 方案4:切换到Polars库(改造成本中等,可提速5~10倍,150万行场景通常可压缩到10秒以内)
Polars是Rust实现的高性能DataFrame库,分组聚合做了大量底层优化,处理完可直接转回pandas格式不影响后续逻辑,示例代码如下:
# 需提前安装polars:pip install polars import polars as pl group_cols = ['key','code','name','period','agg_metric'] # pandas df转polars df pl_df = pl.from_pandas(df) # 执行分组求和后转回pandas格式 df = pl_df.group_by(group_cols, maintain_order=False)\ .agg( pl.col('metricA').sum(), pl.col('metricB').sum() ).to_pandas()
补充说明:你之前测试的groupby.agg和groupby.apply不会提升性能是正常现象,apply本身走Python循环开销更高,字典形式的agg和直接选列求和的底层逻辑一致,不会有性能差异。不需要移除可推导的分组列,转换为分类类型后多列的分组开销极低。
内容的提问来源于stack exchange,提问作者Brent
相关产品推荐
相关产品推荐

