概述
聚合(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 已修复;
- 当多种聚合函数数据类型不一致时,结果可能自动升维并产生多层列名。
相关笔记
- 上一节:分组与聚合-2-迭代与分组
- 下一节:分组与聚合-4-变换
- 延伸:分组内累积与排名