前言
在数据分析工作中,原始数据往往杂乱分散,我们经常需要按照某一类维度对数据进行划分,再对各组数据进行统计运算,这一整套流程就是分组 + 聚合。Pandas 中 DataFrame 提供了强大的分组聚合 API,熟练掌握groupby、聚合函数、转换、筛选分组数据,是开展探索性数据分析(EDA)的基础技能。
本文基于 Pandas 讲解 DataFrame 分组与聚合常用用法,附带可直接运行代码示例。
一、核心概念理解
- 分组 groupby:按照指定一列/多列的相同值,把整张 DataFrame 切分成若干个子数据集;
- 聚合 aggregate:对每个分组内的数据执行汇总计算(求和、均值、计数、最大最小值等);
执行流程通俗概括:拆分 → 应用函数 → 合并结果
Split(拆分)→ Apply(运算)→ Combine(合并)
二、准备测试数据
先构造示例数据集,后续所有示例统一使用:
importpandasaspd data={"部门":["技术部","运营部","技术部","市场部","运营部","市场部","技术部"],"员工":["张三","李四","王五","赵六","钱七","孙八","周九"],"薪资":[18000,12000,22000,15000,11000,14000,20000],"工龄":[3,2,5,2,1,4,4]}df=pd.DataFrame(data)print(df)三、基础分组 groupby 使用
3.1 单列分组
df.groupby(分组字段)不会立刻执行计算,返回的是分组对象DataFrameGroupBy,属于惰性计算。
# 按照【部门】分组group_obj=df.groupby("部门")# 查看分组名称print(group_obj.groups.keys())3.2 多列联合分组
同时依据多个维度分组,传入列表:
# 先按部门分组,再按工龄分组group_multi=df.groupby(["部门","工龄"])3.3 获取指定分组数据
使用get_group()提取某一分组完整数据:
tech_df=df.groupby("部门").get_group("技术部")print(tech_df)四、数据聚合操作
4.1 内置简单聚合函数
groupby 对象支持直接调用统计方法:
sum()求和mean()平均值count()非空计数max()/min()最大、最小值std()标准差
# 按部门分组,计算薪资平均值res=df.groupby("部门")["薪资"].mean()print(res)# 多列同时聚合res2=df.groupby("部门")[["薪资","工龄"]].sum()print(res2)4.2 aggregate() / agg() 多函数聚合
agg()是最灵活的聚合方法,可以一次性使用多个统计函数:
# 对薪资同时计算均值、总和、人数res=df.groupby("部门")["薪资"].agg(["mean","sum","count"])print(res)不同字段使用不同聚合函数
res=df.groupby("部门").agg(平均薪资=("薪资","mean"),最高薪资=("薪资","max"),平均工龄=("工龄","mean"))print(res)推荐写法:支持直接自定义结果列名,避免后续重复重命名。
4.3 自定义聚合函数
除内置函数,支持传入自定义函数完成特殊统计:
defrange_salary(x):# 计算薪资极差:最大值-最小值returnx.max()-x.min()res=df.groupby("部门")["薪资"].agg(range_salary)print(res)五、transform 分组转换(重点难点)
普通聚合会缩减行数(一组返回一行结果);transform()保持原始 DataFrame 行数不变,将分组计算结果广播填充到每组所有行。
典型场景:计算每个员工薪资与所在部门平均薪资差值
# 部门平均薪资,广播到每行df["部门平均薪资"]=df.groupby("部门")["薪资"].transform("mean")df["薪资与均值差"]=df["薪资"]-df["部门平均薪资"]print(df)适用场景:组内归一化、组内差值对比、批量填充组内缺失值。
六、filter 分组筛选
对整个分组进行条件过滤,保留满足条件的全部分组数据。
区别布尔索引:filter 筛选分组,不是筛选单行。
示例:只保留员工人数≥3人的部门全部数据
# x 代表每个分组的子DataFrameres=df.groupby("部门").filter(lambdax:len(x)>=3)print(res)七、分组结果索引处理
groupby 默认会将分组字段设为行索引,日常分析中经常需要转为普通列:
res=df.groupby("部门")["薪资"].mean().reset_index()print(res)reset_index()是高频操作,方便后续绘图、导出Excel、SQL风格处理。
八、sort 分组排序控制
关闭分组自动排序
groupby 默认会对分组键排序,数据量大时存在性能损耗,可以关闭:
df.groupby("部门",sort=False)["薪资"].mean()聚合后排序
res=df.groupby("部门")["薪资"].mean().reset_index()res=res.sort_values("薪资",ascending=False)九、常见踩坑总结
- 分组后直接使用 df[“列名”] 减少计算范围
优先指定需要聚合的列groupby()[["A","B"]],避免对全列运算,提升性能; - 区分 agg / transform / filter
- agg:一组输出一行(汇总报表)
- transform:行数不变,组内广播(行级别对比)
- filter:按分组整体条件过滤数据集
- 多列分组结果为多层索引,记得适时使用 reset_index();
- 缺失值:groupby 默认会剔除分组键为空值的行,如需保留空值分组需要额外处理。
十、综合实战案例
需求:统计各部门信息:平均薪资、总人数、薪资极差,筛选平均薪资大于14000的部门
df_res=df.groupby("部门").agg(员工数量=("员工","count"),平均薪资=("薪资","mean"),薪资极差=("薪资",range_salary)).reset_index()# 筛选df_final=df_res[df_res["平均薪资"]>14000]print(df_final)结语
DataFrame 分组与聚合是 Pandas 数据分析的核心能力。简单统计使用内置聚合函数;多指标、多字段差异化计算优先使用agg();需要保留原始行数做组内对比选用transform;需要按分组整体过滤数据使用filter。
掌握这套工具链,绝大多数数据透视、维度汇总、报表统计需求都可以快速实现。