使用groupby和sum()分组后计算子类别对应类别的百分比
这个需求我之前做数据分析的时候经常碰到,用Pandas的分组和transform功能就能轻松搞定,不用折腾复杂的合并操作~
先看你的示例数据和初始代码,你已经能通过groupby(['key','data2'])['data1'].sum()得到每个子类别(比如A1、A2)的求和结果,接下来只需要计算这些子项在对应父类别(A、B、C)中的占比就行,核心思路是先算出每个父类别的总求和值,再让每个子项的求和值除以这个总数值。
给你两种实用的实现方式:
方法一:分步实现(清晰易懂,适合新手)
- 先计算每个
(key, data2)组合的求和结果,并转成带列名的DataFrame:
import pandas as pd df = pd.DataFrame({'key': ['A', 'B', 'C', 'A', 'B', 'C'], 'data1': range(6), 'data2': ['A1', 'B1', 'C1', 'A2', 'B2', 'C2']}, columns = ['key', 'data1', 'data2']) sum_df = df.groupby(['key','data2'])['data1'].sum().reset_index(name='sum_data1')
- 用
transform计算每个父类别的总和,并直接算出占比:
sum_df['proportion'] = sum_df['sum_data1'] / sum_df.groupby('key')['sum_data1'].transform('sum')
这里的transform('sum')会自动把每个key的总求和值,广播到该key下的所有子项行,这样就能直接做除法得到占比了,完全不用手动去合并数据。
最终得到的结果如下:
key data2 sum_data1 proportion 0 A A1 0 0.000000 1 A A2 3 1.000000 2 B B1 1 0.200000 3 B B2 4 0.800000 4 C C1 2 0.285714 5 C C2 5 0.714286
方法二:链式调用(简洁高效,适合熟练用户)
如果喜欢写紧凑的代码,可以利用多级索引的特性直接链式操作:
# 只得到占比列 proportion_series = df.groupby(['key','data2'])['data1'].sum() \ .groupby(level='key') \ .apply(lambda x: x / x.sum()) \ .rename('proportion') # 如果要同时保留求和值和占比 result_df = df.groupby(['key','data2'])['data1'].sum().to_frame('sum_data1') result_df['proportion'] = result_df.groupby(level='key')['sum_data1'].transform(lambda x: x/x.sum())
这里的groupby(level='key')是针对分组后得到的多级索引的第一级(也就是key)进行分组,省去了重置索引的步骤。
小提示
transform是这里的核心:它和普通的agg不同,不会把数据聚合压缩,而是返回和原数据行数一致的结果,把分组聚合的值“映射”回每一行,这在计算占比、均值这类相对指标时特别好用。- 如果需要保留多级索引,就不用调用
reset_index,直接用level参数处理就行,效率更高。
内容的提问来源于stack exchange,提问作者SBad
相关产品推荐
相关产品推荐

