pandas groupby 分组聚合与列计算实战指南

花开有时
花开有时
发布时间:2026-10-12 | 2人浏览过
原创

在数据分析流程中,按特定字段划分数据集并计算组内指标是高频需求。本文详解 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 高级操作的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表