
本文介绍如何利用 pandas 的 `transform` 方法,直接在原始 dataframe 中高效计算按用户和类别分组后的唯一计数值平均值,避免冗余的去重、新建 dataframe 和 merge 操作,提升代码简洁性与执行效率。
在数据分析中,常需基于去重逻辑(如每个 user_id + Category 组合下的唯一 counts 值)计算分组统计量,并将结果广播回原始数据行。传统做法(如先 drop_duplicates 再 merge)不仅代码冗长,还易引入索引错位、重复匹配等隐患,且性能较差。
更优雅、高效的解法是直接在原始 DataFrame 上应用 transform ——但关键前提在于:明确“唯一值的平均”究竟指什么。从原始示例的输出可反推真实需求:
- Overall_average__Unique_Counts = 1.83 → 实际是全部 不重复 (user_id, Category, counts) 组合 中 counts 的全局均值(即 df1.drop_duplicates(['user_id','Category','counts'])['counts'].mean()),而非按 user_id 分组均值;
- Categorywise_average_Unique_counts → 是每个 Category 下所有不重复 counts 值的均值(如 'ABC' 对应 {3,2,1} → 均值 2.0;'XYZ' 对应 {2,1} → 均值 1.5?但示例输出为 1.67,说明实际用了 {2,1,2} 去重后 {1,2} → (1+2)/2 = 1.5 ❌;再核对:df1_unique 中 'XYZ' 行为 (D,XYZ,2), (D,XYZ,1), (E,XYZ,2) → 去重后 counts 为 [2,1] → 均值 1.5,但输出是 1.67。发现原示例代码中 df1_unique["Categorywise_average_Unique_counts"] = df1_unique.groupby(["Category"])['counts'].transform('mean') 计算的是 df1_unique(已去重)中各 Category 内 counts 的均值:'ABC' 有 (A,ABC,3), (B,ABC,2), (B,ABC,1) → counts=[3,2,1] → mean=2.0;'XYZ' 有 (D,XYZ,2), (D,XYZ,1), (E,XYZ,2) → counts=[2,1,2] → 去重后仍保留三行,transform('mean') 对每行赋值 mean([2,1,2]) = 5/3 ≈ 1.67。⚠️ 注意:此处“唯一值”指组合唯一,但 counts 列本身未二次去重!因此正确理解是:先按 (user_id, Category, counts) 去重得到唯一观测,再对这些观测按需分组求 counts 均值。
因此,若严格按业务语义(即“每个用户/类别的唯一计数值的平均”),必须先去重,但可避免 merge:用 transform 结合 groupby 在去重后 DataFrame 上计算,再通过 map 或 join 回填——但最简实践仍是复用原答案思路,并修正语义:
✅ 推荐方案(兼顾简洁性与可读性):
import pandas as pd
df1 = pd.DataFrame({
'user_id': ['A','A','A', 'B','B','B', 'D','D','D', 'E','E'],
'Category': ['ABC','ABC','ABC','ABC','ABC','ABC','XYZ','XYZ','XYZ','XYZ','XYZ'],
'counts': [3,3,3,2,2,1,2,1,2,2,2]
})
# 步骤1:构造去重后的基准表(唯一组合)
df_unique = df1.drop_duplicates(['user_id', 'Category', 'counts']).copy()
# 步骤2:计算全局唯一 counts 均值(标量),广播到所有行
overall_avg = df_unique['counts'].mean()
df1['Overall_average__Unique_Counts'] = overall_avg
# 步骤3:计算每个 Category 下唯一 counts 的均值(按 Category 分组均值)
cat_avg_series = df_unique.groupby('Category')['counts'].transform('mean')
# 将结果映射回原 df1:以 (user_id, Category, counts) 为键 join
df1 = df1.merge(
df_unique.assign(Categorywise_average_Unique_counts=cat_avg_series)[
['user_id', 'Category', 'counts', 'Categorywise_average_Unique_counts']
],
on=['user_id', 'Category', 'counts'],
how='left'
)? 进阶技巧:若追求极致简洁且接受轻微语义偏移(即用原始行分组代替去重后分组),可直接用:
# 注意:此方式计算的是“每个 user_id 下所有 counts 的均值”,非唯一值均值
df1['Overall_avg_by_user'] = df1.groupby('user_id')['counts'].transform('mean')
# 此方式计算的是“每个 (user_id, Category) 组合下所有 counts 的均值”
df1['Cat_avg_by_group'] = df1.groupby(['user_id', 'Category'])['counts'].transform('mean')但这与原始需求不符(原始需求明确要求“unique values”)。
? 总结:
- transform 本身不能直接处理“先去重再分组”的复合逻辑,必须显式去重;
- 最佳实践是:drop_duplicates → groupby().transform() 计算 → merge 回填,代码清晰、语义准确、性能可控;
- 避免在未去重数据上直接 transform,否则会混淆“唯一观测”与“原始行”的统计口径;
- 若数据量极大,可考虑用 pd.util.hash_pandas_object 构造组合哈希键加速去重与合并。










