
Pandas 的 groupby.agg() 默认不支持直接在单个聚合函数中访问多个列,但可通过预计算新列(如 eval)或改用 groupby.apply() 实现多列协同计算,前者更高效,后者更灵活。
pandas 的 `groupby.agg()` 默认不支持直接在单个聚合函数中访问多个列,但可通过预计算新列(如 `eval`)或改用 `groupby.apply()` 实现多列协同计算,前者更高效,后者更灵活。
在 Pandas 中,DataFrameGroupBy.aggregate()(即 .agg())的设计原则是按列独立聚合:每个聚合操作作用于单列(或其 Series),即使使用字典或命名元组语法(如 {'agg1': ('value1', 'mean')}),其底层仍以单列为单位调用函数。因此,像 lambda group: (group["value1"] * group["value2"]).mean() 这类需同时引用 value1 和 value2 的跨列逻辑,无法直接在 .agg() 的 callable 参数中生效——因为该 callable 并不会接收整个子 DataFrame,而是仅接收当前被聚合的单列 Series。
✅ 推荐方案:预计算辅助列 + .agg()(高效、向量化)
最简洁且性能最优的方式是在 groupby 前通过 eval() 或普通赋值构造中间列,再对新列执行标准聚合:
import pandas as pd
df = pd.DataFrame({
"value1": range(8),
"value2": range(7, -1, -1),
"Sample": [1, 2] * 4,
"Year": list(range(2023, 2027)) * 2
})
# 方案1:使用 eval 链式操作(推荐)
result = (df.eval('prod = value1 * value2')
.groupby(['Sample', 'Year'])
.agg(agg1=('prod', 'mean')))
print(result)
# 输出:
# agg1
# Sample Year
# 1 2023 6.0
# 2025 8.0
# 2 2024 8.0
# 2026 6.0该方式完全利用 Pandas 向量化运算,避免 Python 循环开销,适用于大数据量场景。
⚠️ 替代方案:.apply()(灵活但低效)
当逻辑复杂、难以拆解为预计算列时(例如需调用外部函数、条件分支、多步 DataFrame 操作),可改用 groupby.apply()。它确实将每个分组作为完整子 DataFrame 传入,支持任意跨列计算:
# 方案2:使用 apply(仅在必要时选用)
result_apply = (df.groupby(['Sample', 'Year'])
.apply(lambda x: x['value1'].mul(x['value2']).mean())
.rename('agg1')
.reset_index(name='agg1'))
print(result_apply)
# 输出(Series → 可转为 DataFrame):
# Sample Year agg1
# 0 1 2023 6.0
# 1 1 2025 8.0
# 2 2 2024 8.0
# 3 2 2026 6.0注意:apply() 在内部会逐组迭代,性能显著低于向量化 .agg(),且返回结构可能为 Series(需手动 reset_index 调整格式)。
? 总结与建议:
- ✅ 优先用预计算列 +
.agg():清晰、高效、符合 Pandas 最佳实践; - ⚠️ 慎用
.apply():仅当聚合逻辑强依赖组内多列交互且无法前置化时使用; - ❌ 避免在
.agg()中尝试传入接收整个 DataFrame 的 lambda——这属于误用,会导致KeyError或意外行为; - ? 扩展提示:若需多个跨列指标(如
(v1*v2).mean()和(v1+v2).std()),可一次性创建多个辅助列(如df.assign(prod=df.v1*df.v2, total=df.v1+df.v2)),再统一聚合。

















