
本文详解在pandas中对dataframe进行groupby().nunique()操作时,为何优先选取目标列子集而非全表分组,并给出性能更优、语义更清晰的写法。
本文详解在pandas中对dataframe进行groupby().nunique()操作时,为何优先选取目标列子集而非全表分组,并给出性能更优、语义更清晰的写法。
在实际数据分析中,我们常需按某列(如用户ID)统计另一列(如商品类别)的去重数量。此时常见两种写法:
# 写法1:先切片再分组(常见于遗留代码)
df[cols].groupby('user_id').nunique()['category']
# 写法2:直接全表分组后索引
df.groupby('user_id').nunique()['category']
表面上看二者结果一致,但性能与可维护性差异显著。核心问题在于:df.groupby(...).nunique()会为所有数值列(及可哈希的非数值列) 计算去重计数,即使你最终只取其中一列。若DataFrame含20列,却仅需统计category列的去重值,这种写法将浪费约19/20的计算资源。
✅ 推荐写法(最优实践):
将列筛选逻辑前置到.groupby()之后、聚合之前,即使用链式索引明确指定目标列:
# ✅ 推荐:仅对目标列执行nunique,避免冗余计算
df.groupby('user_id')['category'].nunique()
# ✅ 同等效果,更显式地分离索引与分组逻辑
df['category'].groupby(df['user_id']).nunique()
这两种写法语义清晰、执行高效——Pandas仅对category列内部去重,不扫描无关列,内存与CPU开销大幅降低。
⚠️ 注意事项:
- 若cols包含额外列(如cols = ['user_id', 'category', 'timestamp']),df[cols].groupby(...).nunique()仍会计算timestamp列的去重值,造成无谓开销;
- 仅当some_column是外部Series(非DataFrame原生列)时,df[cols].groupby(external_series)才有存在意义;
- nunique()默认跳过NaN,如需包含空值计数,需显式传参dropna=False。
总结:永远让聚合操作作用于最小必要列集合。用df.groupby(key)[target_col].nunique()替代df.groupby(key).nunique()[target_col],既是性能优化,也是代码可读性与意图表达的最佳实践。











