Pandas如何基于其他分类列对同一列多次groupby分组求和
更优实现方案
不需要逐类筛选再拼接,用规则映射+单次分组的方式即可实现,代码冗余度低,计算效率远高于多次筛选pd.concat的方案,后续调整分类规则只需要修改规则字典即可,不需要改动核心计算逻辑。
具体实现步骤
- 首先导入依赖、加载原始数据,把分类规则统一定义为字典格式:
import pandas as pd import numpy as np # 原始数据加载 df = pd.DataFrame( [ ["id1",1,1.23],["id1",2,1.56],["id1",3,1.65],["id1",4,1.73],["id1",5,0.89], ["id2",1,1.07],["id2",2,1.38],["id2",3,0.94],["id2",4,0.72],["id2",5,1.37], ["id3",1,1.04],["id3",2,0.56],["id3",3,0.78],["id3",4,1.12],["id3",5,0.84] ], columns=["order_id", "otype", "score"] ) # 统一定义分类规则,新增/修改分类直接改这个字典即可 class_rules = { "class1": {1,2,3}, "class2": {4,5}, "class3": {1,2,3,4,5}, "class4": {2,3,5} }
- 生成otype到分类的映射表,和原始数据合并后单次分组求和:
# 构建otype与分类的匹配关系 otype_unique = df["otype"].unique() map_pairs = [] for cls_name, otype_collect in class_rules.items(): valid_otype = [ot for ot in otype_unique if ot in otype_collect] map_pairs.extend([(ot, cls_name) for ot in valid_otype]) cls_map = pd.DataFrame(map_pairs, columns=["otype", "class_name"]) # 合并后直接分组聚合 merge_df = df.merge(cls_map, on="otype", how="left") result = merge_df.groupby( ["order_id", "class_name"], as_index=False )["score"].sum().rename( columns={"class_name": "otype", "score": "score_sum"} )
结果说明
运行后得到的结果和预期完全一致,你示例中id1对应class1的4.4是保留1位小数的展示值,实际计算结果为1.23+1.56+1.65=4.44,需要对齐小数位的话加一句result["score_sum"] = result["score_sum"].round(1)即可。id1的输出如下:
order_id otype score_sum 0 id1 class1 4.44 1 id1 class2 2.62 2 id1 class3 7.06 3 id1 class4 4.10
方案优势
- 仅做1次表合并、1次分组聚合,没有重复遍历全表的冗余计算,数据量越大性能优势越明显
- 分类规则统一收敛在字典中维护,不需要为每个分类写重复的筛选、聚合、拼接代码
- 逻辑一致性强,不会因为漏写某个分类的筛选逻辑导致结果缺失
如果处理百万行以上的超大数据集追求极致性能,可以用numpy向量化判断替代merge操作,核心逻辑如下:
res_list = [] otype_arr = df["otype"].values score_arr = df["score"].values order_arr = df["order_id"].values for cls_name, otype_collect in class_rules.items(): mask = np.isin(otype_arr, list(otype_collect)) cls_res = pd.Series(score_arr[mask]).groupby(order_arr[mask]).sum() res_list.append( cls_res.rename("score_sum").reset_index().assign(otype=cls_name) ) result = pd.concat(res_list, ignore_index=True)[["order_id", "otype", "score_sum"]]
内容的提问来源于stack exchange,提问作者Jason Bourne
相关产品推荐
相关产品推荐

