Pandas:按ID1去重后基于ID2聚合val均值求和的实现
Pandas分组聚合问题:按ID1去重后对均值求和
原始数据
创建DataFrame的代码:
import pandas as pd import numpy as np data = pd.DataFrame({"ID1": ["a", "a", "a", "b", "b", "b", "c", "c", "c"], "ID2": ["k", "k", "k", "k","k", "k", "j", "j", "j"], "val": [18, 19, 20, 18, 19, 20, 34, 35, 37] })
输出的DataFrame:
ID1 ID2 val 0 a k 18 1 a k 19 2 a k 20 3 b k 18 4 b k 19 5 b k 20 6 c j 34 7 c j 35 8 c j 37
需求说明
先按ID1分组计算val列的均值,再按ID2分组对该均值求和,同时统计每个ID2对应的唯一ID1数量(命名为Volume)。
原实现问题
原代码:
(data .assign(val_id1_avg = data.groupby("ID1")["val"].transform("mean")) .groupby("ID2") .agg(val_avg = ("val_id1_avg", lambda x: np.sum(x.unique())), volume=("ID1", 'nunique')) .reset_index())
原输出:
ID2 val_avg volume 0 j 35.333333 1 1 k 19.000000 2
错误原因:对于ID2=k,ID1=a和ID1=b的val均值都是19,x.unique()会去重为单个19,导致求和结果仅为19,而实际需要的是两个ID1的均值之和(19+19=38)。
正确实现方法
方法一:分步处理(清晰直观)
# 1. 计算每个ID1对应的val均值 id1_avg = data.groupby("ID1")["val"].mean().reset_index(name="val_id1_avg") # 2. 关联原数据的ID1和ID2,仅保留必要列 merged_data = data[["ID1", "ID2"]].merge(id1_avg, on="ID1") # 3. 按ID1和ID2去重,确保每个ID1在同一ID2下只出现一次 unique_records = merged_data.drop_duplicates(subset=["ID1", "ID2"]) # 4. 按ID2分组,求和均值并统计唯一ID1数量 final_result = unique_records.groupby("ID2").agg( val_avg=("val_id1_avg", "sum"), Volume=("ID1", "nunique") ).reset_index() # 格式化val_avg为两位小数 final_result["val_avg"] = final_result["val_avg"].round(2) print(final_result)
输出结果:
ID2 val_avg Volume 0 j 35.33 1 1 k 38.00 2
方法二:链式操作(简洁高效)
final_result = (data # 先计算每个ID1的val均值,添加为新列 .assign(val_id1_avg=lambda df: df.groupby("ID1")["val"].transform("mean")) # 按ID1和ID2去重,避免同一ID1在同一ID2下重复计算 .drop_duplicates(subset=["ID1", "ID2"]) # 按ID2分组聚合 .groupby("ID2") .agg( val_avg=("val_id1_avg", "sum"), Volume=("ID1", "nunique") ) .reset_index() # 保留两位小数 .round({"val_avg": 2}) ) print(final_result)
此方法与分步处理结果完全一致。
内容的提问来源于stack exchange,提问作者Ailurophile
相关产品推荐
相关产品推荐

