pandas groupby 分组聚合与列计算实战指南
在数据分析流程中,按特定字段划分数据集并计算组内指标是高频需求。本文详解 pandas groupby 的底层对象结构,演示使用 agg 实现多列自定义聚合,并通过 transform 将分组统计值精准广播回原表,附带完整代码与对照输出。
groupby 的底层结构与遍历方式
执行分组操作后,pandas 会返回一个 DataFrameGroupBy 对象。直接打印该对象仅显示内存地址,无法直观查看数据分布。若需核对分组结果,可将对象转换为列表,此时会得到一系列元组。每个元组的第一个元素为分组键值(如 L1),第二个元素为该组对应的子 DataFrame。这种结构便于逐组迭代或检查划分是否符合预期。
def ddd():
levels = ["L1", "L1", "L1", "L2", "L2", "L3", "L3"]
nums = [10, 20, 30, 20, 15, 10, 12]
df = pd.DataFrame({"level": levels, "num": nums})
g = df.groupby('level')
print(g)
print()
print(list(g))
运行上述代码后的输出如下:
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x10f6f96d0>
[('L1', level num
0 L1 10
1 L1 20
2 L1 30), ('L2', level num
3 L2 20
4 L2 15), ('L3', level num
5 L3 10
6 L3 12)]
基础操作:使用 agg 实现多列聚合与占比计算
当需要针对同一分组的不同列应用不同的统计函数(如求和、均值)时,应调用 .agg() 方法。该方法支持传入字典格式的参数,明确指定每一列的聚合规则,最终生成缩减后的 DataFrame。
def group1():
levels = ["L1", "L1", "L1", "L2", "L2", "L3", "L3"]
nums = [10, 20, 30, 20, 15, 10, 12]
scores = [100, 200, 300, 200, 150, 100, 120]
df = pd.DataFrame({"level": levels, "num": nums, "score": scores})
# 对 num 列求和,对 score 列求平均值
result = df.groupby('level').agg({'num': 'sum', 'score': 'mean'})
# 计算各组 num 总和占总体总和的比例
allnum = result['num'].sum()
result['rate'] = result['num'].map(lambda x: x / allnum)
print(result)
执行后的结果表格如下:
num score rate
level
L1 60 200.0 0.512821
L2 35 175.0 0.299145
L3 22 110.0 0.188034
此处先通过 result['num'].sum() 获取全局总和,再利用 .map() 逐行传入匿名函数计算占比,并将新列追加至结果表中,完成指标衍生。
进阶技巧:利用 transform 快速填充组内统计列
在实际处理中,常需要将分组计算出的统计量(如组内平均值)反向映射回原始 DataFrame 的每行,以维持原表行数不变。.transform() 正是为此设计,它会在每个分组上独立执行函数,返回与原 DataFrame 索引完全对齐的结果 Series,可直接赋值新建列。
传统做法通常先提取组统计值转为字典,再使用 .map() 关联,代码如下:
def t10():
levels = ["L1", "L1", "L1", "L2", "L2", "L3", "L3"]
nums = [10, 20, 30, 20, 15, 10, 12]
df = pd.DataFrame({"level": levels, "num": nums})
ret = df.groupby('level')['num'].mean().to_dict()
df['avg_num'] = df['level'].map(ret)
print(ret)
print(df)
输出结果为:
{'L1': 20.0, 'L2': 17.5, 'L3': 11.0}
level num avg_num
0 L1 10 20.0
1 L1 20 20.0
2 L1 30 20.0
3 L2 20 17.5
4 L2 15 17.5
5 L3 10 11.0
6 L3 12 11.0
若直接使用 transform,逻辑更为紧凑且不易出错:
def trans():
levels = ["L1", "L1", "L1", "L2", "L2", "L3", "L3"]
nums = [10, 20, 30, 20, 15, 10, 12]
df = pd.DataFrame({"level": levels, "num": nums})
df['avg_num'] = df.groupby('level')['num'].transform('mean')
print(df)
两者生成的 df 效果一致。transform 的核心优势在于其返回值保持了原始数据的索引结构,避免了手动构建映射关系时的维度对齐问题,特别适合需要保留原始行数的特征工程场景。
关键核对点与注意事项
- 完成判断:执行
.agg()后得到的 DataFrame 行数等于唯一分组键的数量;执行.transform()后新增列的行数必须与原表严格相等。 - 缺失值处理:参与分组的列若包含 NaN,默认会将空值视为一个独立分组。建议在分组前使用
dropna()预处理以确保业务逻辑准确。 - 性能提示:数据量较大时,优先使用内置聚合字符串(如
'sum','mean')而非自定义函数,可显著降低 CPU 开销。
简要总结
pandas 的 groupby 配合 .agg() 可实现灵活的多维数据聚合,而 .transform() 则完美解决了“统计结果逆向回填”的需求。掌握这两种模式即可覆盖绝大多数报表生成与特征构造任务。以上就是 pandas groupby 分组聚合与列计算实战指南的详细内容,更多关于 Python 数据处理与 pandas 高级操作的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













