Pandas按多列分组并对多列应用value_counts的高效实现
高效实现DataFrame多列分组频次统计的优化方案
问题痛点拆解
你需要按id和shop分组,统计多列(type的A/B、status的close/open)的类别频次,还要支持后续新增统计列。原来多次groupby+concat的方式不仅效率低,还容易出现索引对齐、列名冲突等问题——下面给你一套更高效、扩展性更强的解决方案。
核心优化思路
把需要统计的列转成长格式(一行对应一个分组+一个统计项+一个类别),然后一次性完成分组计数,最后转回宽格式得到目标结果。这种方式仅需一次分组操作,从根源上解决多次合并的隐患,同时性能提升显著。
代码实现(附示例数据)
先假设你的原始数据如下:
import pandas as pd df = pd.DataFrame({ 'id': [1, 1, 1, 2, 2, 3], 'shop': ['a', 'a', 'b', 'a', 'a', 'b'], 'type': ['A', 'B', 'A', 'B', 'B', 'A'], 'status': ['open', 'close', 'open', 'open', 'close', 'close'] })
步骤1:转换为长格式
用melt把type、status这类统计列拆成变量-值对,保留id和shop作为分组键:
melted = df.melt( id_vars=['id', 'shop'], value_vars=['type', 'status'], # 后续新增统计列直接在这里添加 var_name='metric', value_name='category' )
步骤2:分组计数+转宽格式
一次分组搞定所有统计,再用unstack把类别展开为列,缺失类别自动补0:
result = ( melted.groupby(['id', 'shop', 'metric', 'category']) .size() # 统计每组的数量 .unstack(['metric', 'category'], fill_value=0) .sort_index(axis=1) # 可选:让列按顺序排列,结果更整齐 ) # 可选:把层级列名合并为单级(比如"type_A"、"status_open") result.columns = [f'{metric}_{category}' for metric, category in result.columns] result = result.reset_index()
最终输出
id shop type_A type_B status_close status_open 0 1 a 1 1 1 1 1 1 b 1 0 0 1 2 2 a 0 2 1 1 3 3 b 1 0 1 0
方案优势
- 性能更优:大数据量下,一次
groupby比多次单独统计再合并的效率提升明显 - 无合并隐患:全程基于同一分组结果处理,不会出现索引不对齐、列名重复的问题
- 扩展性极强:后续新增统计列(比如
payment_type),只需在melt的value_vars里添加列名即可,无需修改其他逻辑
备选简化方案:用pivot_table
如果觉得melt+groupby流程繁琐,也可以用pivot_table一步实现:
result = pd.pivot_table( melted, index=['id', 'shop'], columns=['metric', 'category'], aggfunc='size', fill_value=0 ) # 同样处理列名 result.columns = [f'{m}_{c}' for m, c in result.columns] result = result.reset_index()
内容的提问来源于stack exchange,提问作者Ibrahim Sherif
相关产品推荐
相关产品推荐

