概述

聚合(Aggregation)是分组操作中最常用的一类:对每个组计算一个或多个标量统计量。核心方法是 agg() / aggregate()。


3.1 agg() / aggregate()

两者完全等价, aggregate 是 agg 的别名。

常用调用形式

形式说明示例
传入单个函数名对所有选择列应用同一聚合df.groupby("部门")["薪资"].agg("sum")
传入函数对象使用可调用对象df.groupby("部门")["薪资"].agg(np.sum)
传入列表多列同时应用多个聚合df.groupby("部门")["薪资"].agg(["sum", "mean"])
传入字典不同列使用不同聚合df.groupby("部门").agg({"薪资": "sum", "员工": "count"})
传入命名元组列表pandas 2.x 命名聚合df.groupby("部门").agg(总薪资=("薪资", "sum"))
import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    "部门": ["A", "A", "B", "B", "C"],
    "性别": ["男", "女", "男", "女", "男"],
    "薪资": [8000, 9500, 12000, 11000, 9000],
    "年龄": [30, 25, 40, 35, 28],
})
 
# 单列单聚合
df.groupby("部门")["薪资"].agg("sum")
 
# 单列多聚合:结果列名为函数名
df.groupby("部门")["薪资"].agg(["sum", "mean", "max"])
 
# 自定义列名:新版本使用 (新列名, 函数) 元组
df.groupby("部门")["薪资"].agg([("总薪资", "sum"), ("平均薪资", "mean")])
 
# 多列多聚合
df.groupby("部门").agg(["sum", "mean"])
 
# 按列指定不同聚合函数
df.groupby("部门").agg({"薪资": "sum", "年龄": "mean"})
 
# 聚合后重置索引
df.groupby("部门").agg({"薪资": "sum"}).reset_index()

3.2 内置聚合函数

下表为 GroupBy 可直接使用的内置聚合方法(等价于传入同名字符串)。

聚合方法说明聚合方法说明
sum()求和count()非空计数
mean()均值nunique()唯一值个数
median()中位数all()所有值为真
std()样本标准差any()任意值为真
var()样本方差prod()连乘
min()最小值sem()均值标准误
max()最大值skew()偏度
first()第一个非空值kurt()峰度
last()最后一个非空值size()行数(含 NaN)
nth(n)第 n 个非空值ohlc()金融 OHLC 四值

示例

# 常用聚合
df.groupby("部门")["薪资"].sum()
df.groupby("部门")["薪资"].mean()
df.groupby("部门")["薪资"].std()
df.groupby("部门")["薪资"].var()
df.groupby("部门")["薪资"].min()
df.groupby("部门")["薪资"].max()
df.groupby("部门")["薪资"].median()
df.groupby("部门")["年龄"].skew()
df.groupby("部门")["年龄"].kurt()
df.groupby("部门")["年龄"].sem()
 
# first / last / nth
df.groupby("部门")["员工"].first()    # 每组第一个
df.groupby("部门")["员工"].last()     # 每组最后一个
df.groupby("部门")["员工"].nth(0)     # 第 0 行(位置)
df.groupby("部门")["员工"].nth(-1)    # 每组最后一个
 
# ohlc:开盘-最高-最低-收盘(常用于时间序列)
ts_df = pd.DataFrame({
    "日期": pd.date_range("2024-01-01", periods=6, freq="D"),
    "价格": [100, 102, 101, 105, 107, 106],
})
ts_df.set_index("日期").groupby(pd.Grouper(freq="3D"))["价格"].ohlc()
 
# all / any / prod / nunique
df.groupby("部门")["薪资"].all()
df.groupby("部门")["薪资"].any()
df.groupby("部门")["薪资"].prod()
df.groupby("部门")["性别"].nunique()

3.3 命名聚合

pandas 0.25+ 支持通过 (名称, 聚合函数) 元组,直接在 agg() 中生成命名结果列,避免 .rename()。

# 关键字命名聚合(推荐)
df.groupby("部门", as_index=False).agg(
    总薪资=("薪资", "sum"),
    平均薪资=("薪资", "mean"),
    最大年龄=("年龄", "max"),
)
 
# 使用 pandas.NamedAgg
df.groupby("部门", as_index=False).agg(
    总薪资=pd.NamedAgg(column="薪资", aggfunc="sum"),
    平均薪资=pd.NamedAgg(column="薪资", aggfunc="mean"),
)

3.4 多个聚合函数

# 列表方式:同时计算 sum、mean、count
df.groupby("部门")["薪资"].agg(["sum", "mean", "count"])
 
# 重命名聚合列
df.groupby("部门")["薪资"].agg(
    求和="sum",
    均值="mean",
    个数="count",
)
 
# 混合使用函数对象与字符串
df.groupby("部门")["薪资"].agg(["sum", np.mean, lambda x: x.max() - x.min()])

3.5 自定义聚合函数

自定义聚合函数需接收 一组 Series/ndarray 并返回标量。

# 自定义函数
def 极差(x):
    return x.max() - x.min()
 
def 变异系数(x):
    return x.std() / x.mean()
 
df.groupby("部门")["薪资"].agg(极差)
df.groupby("部门")["薪资"].agg(极差=极差, 变异系数=变异系数)
 
# 使用 lambda
df.groupby("部门")["薪资"].agg(lambda x: x.max() - x.min())
 
# 多个自定义函数
df.groupby("部门")["薪资"].agg(["sum", 极差, 变异系数])
 
# 自定义函数中调用 Series 方法
df.groupby("部门")["薪资"].agg(lambda x: x.quantile(0.75))

3.6 多列聚合

# 多列使用相同聚合
df.groupby("部门")[["薪资", "年龄"]].agg("sum")
df.groupby("部门")[["薪资", "年龄"]].agg(["sum", "mean"])
 
# 多列使用不同聚合(字典方式)
df.groupby("部门").agg({"薪资": ["sum", "mean"], "年龄": ["min", "max"]})
 
# 字典 + 列表组合
df.groupby("部门").agg(
    薪资合计=("薪资", "sum"),
    薪资均值=("薪资", "mean"),
    年龄最大=("年龄", "max"),
)
 
# 对所有数值列聚合
df.groupby("部门").agg("sum")
df.groupby("部门").sum(numeric_only=True)

注意

  • 字典方式中,列名与聚合函数的顺序在旧版本中有依赖关系,pandas 2.x 已修复;
  • 当多种聚合函数数据类型不一致时,结果可能自动升维并产生多层列名。

相关笔记