Pandas按GROUP列分组实现多列TopN归一化值计数统计
可直接复用的实现代码
import pandas as pd # 初始化测试用DataFrame df = pd.DataFrame({ 'GROUP': ['A', 'A', 'B', 'B', 'C', 'C','A', 'A', 'B', 'B', 'C', 'C','B', 'B', 'C', 'C','A'], 'CATEGORY': ['ORANGE', 'WHITE', 'WHITE','ORANGE','ORANGE','BLACK', 'WHITE', 'BLACK', 'BROWN','BLACK','ORANGE','BLACK', 'WHITE', 'WHITE', 'WHITE', 'BLACK', 'BLACK'], 'SHAPE':['SQUARE','TRIANGLE','SQUARE','CIRCLE','CIRCLE','CIRCLE','SQUARE','CIRCLE','TRIANGLE','CIRCLE','CIRCLE','SQUARE','CIRCLE','TRIANGLE','CIRCLE','SQUARE','CIRCLE'] }) # 配置参数 top_n = 2 # 每个分组下取前n个高频结果 value_cols = [col for col in df.columns if col != 'GROUP'] # 所有待统计的列 merged_result = None # 存储最终拼接结果 for col in value_cols: # 1. 复用单列表分组计数逻辑,取top n的归一化值计数结果 temp = df.groupby('GROUP')[col].apply( lambda x: x.value_counts(normalize=True).head(top_n) ).rename(f'{col}_占比').reset_index() # 2. 自动识别值列,避免索引命名不一致导致的报错 value_col_name = [c for c in temp.columns if c not in ['GROUP', f'{col}_占比']][0] temp = temp.rename(columns={value_col_name: f'{col}_取值'}) # 3. 给每个分组内的结果加排名序号,作为横向对齐的锚点 temp['组内排名'] = temp.groupby('GROUP').cumcount() # 4. 逐列横向拼接结果 if merged_result is None: merged_result = temp else: merged_result = merged_result.merge( temp, on=['GROUP', '组内排名'], how='outer' ) # 还原为带分组层级的最终输出结构 final_result = merged_result.set_index(['GROUP', '组内排名'])
代码逻辑说明
- 逐列遍历所有非分组字段,完全复用你已验证的分组值计数逻辑,独立计算每个列在对应分组下的归一化频次,截取每个分组下Top n的高频结果
- 自动适配列名规则,将分类值和占比拆分为独立列,统一命名为
[原列名]_取值、[原列名]_占比,满足“列名+百分比标识”的列名格式要求 - 通过累计计数为每个分组内的结果添加从0开始的排名标记,以
GROUP和组内排名为关联键做拼接,保证同组内同一排名的结果在同一行对齐,最终输出结构和你预期的横向对齐效果完全匹配 - 仅需修改
top_n参数即可调整每个分组下展示的高频结果数量,适配不同统计粒度需求
可选优化
如果需要将小数形式的占比转换为直观的百分比字符串格式,可在得到最终结果后追加以下处理:
# 占比转百分比字符串,保留1位小数 for col in value_cols: final_result[f'{col}_占比'] = (final_result[f'{col}_占比'] * 100).round(1).astype(str) + '%'
内容的提问来源于stack exchange,提问作者AVal
相关产品推荐
相关产品推荐

