如何在 Pandas 中对多列进行分组聚合并应用自定义函数

10次阅读

如何在 Pandas 中对多列进行分组聚合并应用自定义函数

本文介绍如何使用 `groupby().apply()` 正确实现对多个列同时传入自定义聚合函数(如需基于条件筛选后计算)的场景,解决 `agg()` 中混合内置方法与跨列 Lambda 函数导致的 typeerror。

pandas 中,DataFrame.groupby().agg() 方法虽然强大,但其设计初衷是对单列独立应用聚合函数(如 ‘sum’, ‘mean’),或通过元组形式指定 (column, func) 的映射关系。当你尝试在 agg() 中直接使用 lambda x: arbFun(x[‘A’], x[‘B’]) 这类需要同时访问多列的匿名函数时,Pandas 无法将其解析为合法的聚合规范,从而抛出 TypeError: Must provide ‘func’ or tuples of ‘(column, aggfunc)’ —— 这并非代码逻辑错误,而是 agg() 的接口限制所致。

正确的解决方案是改用 groupby().apply(),它将每个分组子集(即 pd.DataFrame)完整传递给用户函数,从而天然支持跨列操作、条件过滤和任意复杂逻辑。关键在于:自定义函数必须返回一个 pd.Series(而非标量或字典),以便 Pandas 能自动展开为结果 DataFrame 的列。

以下为推荐实现方式:

def group_fn(g):     return pd.Series({         "Column_A": g["A"].sum(),         "Filtered_Mean": arbFun(g["A"], g["B"])  # 完全复用原有 arbFun,无需修改     })  result = data.groupby(["Label1", "Label2"], as_index=False).apply(group_fn) print(result)

输出结果如下(注意 NaN 自动替代 None,符合 Pandas 空值惯例):

Label1 Label2  Column_A  Filtered_Mean 0       1  north       2.0            NaN 1       1  south      18.0            9.0 2       2  north      10.0            4.0 3       2  south      12.0           12.0

优势说明

  • 完全保留 arbFun 的原始签名与语义,满足“不可移除该函数”的硬性约束;
  • apply() 的分组对象 g 是 DataFrame,可自由访问任意列(如 g[‘A’], g[‘B’]),无跨列限制;
  • 返回 pd.Series 可确保列名被正确识别,避免 apply() 默认返回 Series 造成索引混乱。

⚠️ 注意事项

  • 避免在 apply() 中使用 as_index=True(默认),否则结果会以多级索引形式返回,需额外调用 .reset_index();显式设置 as_index=False 更直观;
  • 若 arbFun 内部有较重计算(如循环、IO),建议增加空组判断(如 if len(g) == 0: return pd.Series({…}))提升鲁棒性;
  • 性能敏感场景下,优先考虑向量化替代(例如本例中 g.loc[g[“B”] == 1, “A”].mean() 比调用 arbFun 更高效),但若业务逻辑必须封装在 arbFun 中,则 apply() 是最清晰、可维护的选择。

综上,当聚合逻辑涉及多列协同、条件筛选或复杂状态依赖时,groupby().apply() 是比 agg() 更灵活、更可靠的工具

text=ZqhQzanResources