自学教程 学编程从这里开始

Pandas 分组操作

承接前面数据合并、拼接章节,本篇系统讲解 Pandas groupby 分组全套用法,包含分组原理、分组对象、聚合、转换、过滤、应用、分组迭代、多层分组、分组排序、实战场景与坑点,和之前的 groupby 进阶篇互为补充,保持系列教程统一风格。
前置导入与示例数据

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "region": ["华东","华东","华南","华南","华北","华北","华东","华南"],
    "cate": ["手机","电脑","手机","电脑","手机","电脑","手机","电脑"],
    "sales": [1200, 800, 1500, 950, 1100, 780, 1350, 1020],
    "profit": [180, 120, 220, 140, 160, 110, 195, 150]
})
print(df)

一、groupby 核心思想:Split-Apply-Combine

  1. Split(拆分):按照指定字段把 DataFrame 切分成多组
  2. Apply(应用):对每一组执行函数(聚合、转换、过滤、自定义逻辑)
  3. Combine(合并):把各组计算结果合并成新的 DataFrame / Series
# 基础分组,返回分组对象(不是直接结果!)
g = df.groupby("region")
print(g)

groupby 不会立刻计算,是惰性求值,访问结果时才执行。

二、基础聚合 agg

2.1 单个聚合函数

# 按地区分组,销售额求和
g["sales"].sum()

2.2 多个聚合函数

# 同时求总和、均值、最大值
g["sales"].agg(["sum", "mean", "max"])

2.3 不同字段使用不同聚合(业务高频)

res = g.agg(
    sales_sum=("sales", "sum"),
    profit_avg=("profit", "mean")
)
print(res)

2.4 自定义聚合函数

def range_val(x):
    return x.max() - x.min()

res = g["sales"].agg(["sum", range_val])
print(res)

三、多字段分组(多层分组)

同时按 region + cate 两层分组,生成 MultiIndex

g2 = df.groupby(["region", "cate"])
res2 = g2["sales"].sum()
print(res2)
# 重置索引,转为普通表格,导出Excel常用
res2 = res2.reset_index()

四、分组三大操作:agg / transform / filter

4.1 agg:一组输出一行(聚合)

用于汇总统计,行数 = 分组数量

df.groupby("region")["sales"].agg("sum")

4.2 transform:分组计算,返回和原表行数一样(组内特征)

非常适合组内标准化、组内占比、组内均值回填到每一行

# 每个地区的销售均值回填到本行
df["region_sales_mean"] = df.groupby("region")["sales"].transform("mean")
# 组内占比
df["sales_pct"] = df["sales"] / df.groupby("region")["sales"].transform("sum")
print(df)

4.3 filter:按组条件过滤,保留符合条件的整组

对整个分组做判断,满足条件才保留该分组全部数据

# 只保留分组销售总和大于 2000 的地区
df_filter = df.groupby("region").filter(lambda x: x["sales"].sum() > 2000)
print(df_filter)

区分:布尔索引是筛选行;filter 是筛选整组

五、apply 分组自定义函数

apply 传入自定义函数,函数入参是该分组的子DataFrame

def group_top1(sub_df):
    # 每组取销售额最高一行
    return sub_df.sort_values("sales", ascending=False).head(1)

res_apply = df.groupby("region").apply(group_top1).reset_index(drop=True)
print(res_apply)

⚠️ apply 性能偏弱,优先用 agg / transform / 内置函数,大量数据避免滥用。

六、分组迭代遍历

循环取出每一组的名称和子表,适合自定义复杂处理

for name, sub in df.groupby("region"):
    print(f"分组名称:{name}")
    print(sub)

多字段分组时 name 是元组:

for key, sub in df.groupby(["region","cate"]):
    print(key)

七、groupby 参数详解

df.groupby(
    by,
    axis=0,
    level=None,
    as_index=True,
    sort=True,
    group_keys=True,
    observed=False,
    dropna=True
)
  • as_index=True(默认):分组字段作为结果索引;as_index=False 分组字段保留为普通列,不用反复 reset_index
# as_index=False,直接返回普通DataFrame
res = df.groupby("region", as_index=False)["sales"].sum()
  • sort=True:对分组key排序;不需要排序时设为False,提升性能
  • dropna=True:默认把分组key为NaN的行丢弃;dropna=False 会单独分出NaN分组
  • group_keys:搭配apply时,控制是否添加分组键到索引

八、分组内排序

# 每个地区内部,按销售额降序
df["sales_rank_in_region"] = df.groupby("region")["sales"].rank(ascending=False, method="min")
# 分组内排序
df_sort = df.groupby("region", group_keys=False).apply(lambda x: x.sort_values("sales", ascending=False))
print(df_sort)

九、分组和窗口函数的区别

  • groupby + agg:每组输出一条汇总结果
  • groupby + rolling:分组内滚动窗口(每个用户独立3日移动平均)
# 分组内rolling,时序高频
df["ma3"] = df.groupby("region")["sales"].rolling(window=3, min_periods=1).mean().reset_index(drop=True)

十、高频坑清单

  1. groupby 后忘记 as_index=False,每次都要 reset_index
  2. transform 和 agg 混淆:transform 行数和原表一致,agg行数等于分组数
  3. filter 返回的是原始行子集,不是汇总统计
  4. apply 速度慢,大数据量优先替换为内置聚合函数
  5. 分组key存在NaN,默认被dropna=True丢弃,需要单独统计NaN分组时设置 dropna=False
  6. 多字段分组后索引是MultiIndex,取值需要元组
  7. 分组内rolling一定要注意数据顺序,提前sort_values

十一、综合实战模板

def group_summary(df, group_cols, agg_map):
    """分组汇总模板
    agg_map: {"新列名": ("原列", "聚合函数")}
    """
    return df.groupby(group_cols, as_index=False).agg(**agg_map)

def add_group_ratio(df, group_cols, val_col):
    """增加组内占比"""
    group_sum = df.groupby(group_cols)[val_col].transform("sum")
    df[f"{val_col}_ratio"] = df[val_col] / group_sum
    return df

summary = group_summary(df, ["region","cate"], {
    "sales_total": ("sales", "sum"),
    "profit_avg": ("profit", "mean")
})
df = add_group_ratio(df, ["region"], "sales")
print(summary)

十二、速查表

需求写法
分组汇总求和、均值groupby + agg
把分组统计值回填到每一行groupby + transform
满足条件保留整组groupby + filter
每组自定义复杂逻辑groupby + apply
分组字段不变成索引groupby(…, as_index=False)
分组内滚动窗口groupby + rolling
标签:

0 条笔记