如何在Pandas中对重叠独热编码的分类数据进行分组?
问题描述
我有一个包含游戏评分和媒体曝光分类数据的数据集,简化示例如下:
import pandas as pd df = pd.DataFrame( {'titleName': ['game_A', 'game_B', 'game_C', 'game_D'], 'reviewScore': [88.1, 70.3, 91.3, 66.1], 'mediaAppearances': [['Pub_A', 'Pub_C'], ['Pub_B'], ['Pub_B', 'Pub_C'], ['Pub_A', 'Pub_B', 'Pub_C']]} )
其中mediaAppearances是多值分类特征,记录游戏是否出现在某出版商的报道中。我对该特征做了独热编码,生成各出版商的离散布尔列(游戏出现在该出版商报道中则为1,否则为0):
final_df = pd.concat([df,pd.get_dummies(df['mediaAppearances'].apply(pd.Series).stack()).groupby(level=0).sum()], axis=1)
生成的DataFrame如下:
| titleName | reviewScore | mediaAppearances | Pub_A | Pub_B | Pub_C |
|---|---|---|---|---|---|
| game_A | 88.1 | "Pub_A, Pub_C" | 1 | 0 | 1 |
| game_B | 70.3 | "Pub_B" | 0 | 1 | 0 |
| game_C | 91.3 | "Pub_B, Pub_C" | 0 | 1 | 1 |
| game_D | 66.1 | "Pub_A, Pub_B, Pub_C" | 1 | 1 | 1 |
我希望按每个出版商分组,分析其报道过的游戏的reviewScore,最终得到对应三个出版商的分组结果,方便做聚合分析。
目前我可以通过循环单独过滤每个出版商的数据来计算统计量:
for publisher in ['Pub_A', 'Pub_B', 'Pub_C']: _mean = final_df[final_df[publisher] == True]['reviewScore'].mean() print(f"Mean reviewScore for games appearing in {publisher}: {_mean:.1f}")
输出:
Mean reviewScore for games appearing in Pub_A: 77.1 Mean reviewScore for games appearing in Pub_B: 75.9 Mean reviewScore for games appearing in Pub_C: 81.8
但这种方法只适合计算单一统计量,当需要使用自定义聚合函数、同时分析多个统计量(比如用pandas的describe函数)或者快速切换分组变量时,流程会很繁琐。
我尝试过用标准的groupby语法,但由于独热编码后的出版商列不互斥,分组后会生成所有True/False组合的冗余结果:
final_df.groupby(['Pub_A', 'Pub_B', 'Pub_C']).describe().reset_index()
输出结果包含各种组合的分组,并非我想要的按单个出版商维度的聚合。
我想知道是否有办法用groupby查询生成单个分组DataFrame,能直接对整个数据框应用聚合函数,而不用逐个过滤数据?比如能否通过查询得到如下用describe生成的结果:
| Publisher | count | mean | std | min | 25% | 50% | 75% | max |
|---|---|---|---|---|---|---|---|---|
| Pub_A | 2.0 | 77.1 | 15.6 | 66.1 | 71.6 | 77.1 | 82.6 | 88.1 |
| Pub_B | 3.0 | 75.9 | 13.5 | 66.1 | 68.2 | 70.3 | 80.8 | 91.3 |
| Pub_C | 3.0 | 81.8 | 13.7 | 66.1 | 77.1 | 88.1 | 89.7 | 91.3 |
解决方案
可以通过**将独热编码的出版商列转换为长格式(long format)**来实现按单个出版商分组的需求,具体步骤如下:
- 提取出版商相关列和评分列,将出版商列转置为行,保留对应的评分数据
- 过滤出值为1的行(即游戏被该出版商报道的记录)
- 按出版商分组后应用聚合函数
具体代码如下:
# 提取需要的列:出版商列和reviewScore pub_cols = ['Pub_A', 'Pub_B', 'Pub_C'] temp_df = final_df[['reviewScore'] + pub_cols] # 将出版商列转为长格式,重置索引 melted = temp_df.melt(id_vars='reviewScore', var_name='Publisher', value_name='IsPresent') # 过滤出被报道的记录(IsPresent=1),然后分组聚合 result = melted[melted['IsPresent'] == 1].groupby('Publisher')['reviewScore'].describe() # 调整列名和格式(可选,让结果更贴合需求) result = result.round(1).reset_index()
运行上述代码后,得到的result就是你想要的按单个出版商聚合的统计结果:
| Publisher | count | mean | std | min | 25% | 50% | 75% | max |
|---|---|---|---|---|---|---|---|---|
| Pub_A | 2.0 | 77.1 | 15.6 | 66.1 | 71.6 | 77.1 | 82.6 | 88.1 |
| Pub_B | 3.0 | 75.9 | 13.5 | 66.1 | 68.2 | 70.3 | 80.8 | 91.3 |
| Pub_C | 3.0 | 81.8 | 13.7 | 66.1 | 77.1 | 88.1 | 89.7 | 91.3 |
代码解释
melt函数将宽格式的出版商列转为长格式,每条记录对应一个游戏-出版商的关系- 过滤
IsPresent=1的行,只保留被该出版商报道的游戏记录 - 直接按
Publisher分组后调用describe(),就能一次性得到所有统计量,也可以替换成自定义聚合函数(比如agg(['mean', 'median', 'std']))
这种方法既符合Pandas的标准语法,又能灵活支持各种聚合需求,无需逐个处理每个出版商。
内容的提问来源于stack exchange,提问作者Nikesh Patel
相关产品推荐
相关产品推荐

