groupby变慢主因是默认排序、隐式拷贝和逐字符串哈希;优化核心是设sort=false、转category类型并配observed=true、用agg替代apply,超千万行应换polars/dask/duckdb。

直接说结论:groupby变慢,90%不是代码写得“错”,而是默认行为在偷偷做三件事——排序、隐式拷贝、逐字符串哈希。优化核心是绕过这三步,而不是换写法。
为什么groupby一上千万行就卡住?
根本原因不在聚合逻辑,而在分组键(by参数)的处理方式:
- object类型(如字符串)列做分组键时,Pandas 每次都要逐字符比对+构造哈希值,重复值越多越浪费
- 默认
sort=True会强制对结果按分组键排序,哪怕你后续立刻reset_index()也白排 - 未设
as_index=False时,返回的是以分组列为索引的DataFrame,后续任何合并、导出都触发一次深拷贝
astype('category') 不是“技巧”,是必要操作
当分组列满足“类别少、重复多”(比如 100 个地区名、50 种产品类型),必须转 category:
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
- 内部用整数编码替代字符串存储,哈希和比较快 3–5 倍
- 配合
observed=True可跳过未出现的分类值,避免生成上千个空组 - 安全前提:先确认
df['col'].nunique() / len(df) (类别密度低于 5%) - 显式指定
categories更稳:df['region'] = pd.Categorical(df['region'], categories=['华北','华东','华南'])
别用 apply,优先用 agg + 明确列和函数
apply 是万能但最慢的出口——它把每组转成独立 DataFrame 或 Series,再调 Python 函数,完全无法向量化:
- ✅ 推荐:
df.groupby('cat', sort=False, observed=True).agg({'sales': 'sum', 'price': ['mean', 'max']}) - ❌ 避免:
df.groupby('cat').apply(lambda x: pd.Series({'s': x.sales.sum(), 'p': x.price.mean()})) - ⚠️ 自定义函数能进
agg,但必须本身可向量化(例如用np.quantile而非x.quantile(0.75)) - 注意
NamedAgg仅提升可读性,不提速;真正影响性能的是是否启用底层 Cython 实现
超千万行?别硬扛,换执行引擎
Pandas groupby 是单线程+全内存模型,数据远超物理内存时,OOM 比慢更致命:
- 内存够但 CPU 利用率低:改用
polars,语法接近,lazy().group_by().agg()默认并行,同逻辑常快 3–8 倍 - 需要无缝迁移现有 Pandas 代码:用
dask.dataframe,read_csv和groupbyAPI 完全一致,自动分块+延迟计算 - 已有 SQL 习惯或需复杂过滤:
duckdb直接SELECT ... GROUP BY,百万行以下甚至比 Pandas 还快 - 切记:换引擎前先做
df.info(memory_usage='deep'),确认瓶颈真在计算,而非加载或类型膨胀
最容易被忽略的一点:sort=False 和 observed=True 必须同时设。只关排序不设 observed,category 类型仍会遍历全部预设类别;只设 observed 不关排序,又白花 20% 时间——这两个开关从来不是孤立的。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










