分组聚合 #

分组聚合概述 #

分组聚合是数据分析中最常用的操作之一,Pandas 提供了强大的 groupby 功能。

text
┌─────────────────────────────────────────────────────────────┐
│                    分组聚合流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  原始数据                                                   │
│  ┌─────┬─────┬─────┐                                       │
│  │  A  │  B  │  C  │                                       │
│  ├─────┼─────┼─────┤                                       │
│  │  1  │  x  │ 10  │                                       │
│  │  1  │  y  │ 20  │                                       │
│  │  2  │  x  │ 30  │                                       │
│  │  2  │  y  │ 40  │                                       │
│  └─────┴─────┴─────┘                                       │
│           │                                                 │
│           ▼                                                 │
│  分组 (groupby)                                             │
│  ┌─────┬─────┐                                             │
│  │ A=1 │ A=2 │                                             │
│  └─────┴─────┘                                             │
│           │                                                 │
│           ▼                                                 │
│  聚合 (agg)                                                 │
│  ┌─────┬──────┐                                            │
│  │ A=1 │  15  │                                            │
│  │ A=2 │  35  │                                            │
│  └─────┴──────┘                                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

准备数据 #

python
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'department': np.random.choice(['Sales', 'Engineering', 'Marketing'], 100),
    'team': np.random.choice(['A', 'B', 'C'], 100),
    'employee_id': range(1, 101),
    'salary': np.random.randint(40000, 100000, 100),
    'performance': np.random.randint(60, 100, 100),
    'years_exp': np.random.randint(1, 15, 100)
})

print(df.head())

基本分组 #

创建分组对象 #

python
# 单列分组
grouped = df.groupby('department')
print(grouped)

# 查看分组
print(grouped.groups)
print(grouped.size())

# 遍历分组
for name, group in grouped:
    print(f"Department: {name}")
    print(group.head())

单列分组聚合 #

python
# 单列聚合
print(df.groupby('department')['salary'].mean())

# 多列聚合
print(df.groupby('department')[['salary', 'performance']].mean())

# 所有数值列聚合
print(df.groupby('department').mean(numeric_only=True))

多列分组 #

python
# 多列分组
print(df.groupby(['department', 'team'])['salary'].mean())

# 分组后选择多列
print(df.groupby(['department', 'team'])[['salary', 'performance']].mean())

聚合函数 #

内置聚合函数 #

python
# 常用聚合函数
print(df.groupby('department')['salary'].sum())      # 求和
print(df.groupby('department')['salary'].mean())     # 平均值
print(df.groupby('department')['salary'].median())   # 中位数
print(df.groupby('department')['salary'].std())      # 标准差
print(df.groupby('department')['salary'].var())      # 方差
print(df.groupby('department')['salary'].min())      # 最小值
print(df.groupby('department')['salary'].max())      # 最大值
print(df.groupby('department')['salary'].count())    # 计数
print(df.groupby('department')['salary'].first())    # 第一个值
print(df.groupby('department')['salary'].last())     # 最后一个值

agg 方法 #

python
# 单列多聚合
print(df.groupby('department')['salary'].agg(['mean', 'std', 'count']))

# 重命名聚合列
print(df.groupby('department')['salary'].agg(
    mean_salary='mean',
    std_salary='std',
    count='count'
))

# 多列多聚合
print(df.groupby('department').agg({
    'salary': ['mean', 'std'],
    'performance': ['mean', 'max'],
    'employee_id': 'count'
}))

# 多列自定义命名
print(df.groupby('department').agg(
    mean_salary=('salary', 'mean'),
    std_salary=('salary', 'std'),
    mean_perf=('performance', 'mean'),
    count=('employee_id', 'count')
))

自定义聚合函数 #

python
# 使用 lambda
print(df.groupby('department')['salary'].agg(lambda x: x.max() - x.min()))

# 自定义函数
def range_func(x):
    return x.max() - x.min()

def iqr_func(x):
    return x.quantile(0.75) - x.quantile(0.25)

print(df.groupby('department')['salary'].agg(['mean', range_func, iqr_func]))

# 多个自定义函数
print(df.groupby('department')['salary'].agg({
    'range': range_func,
    'iqr': iqr_func,
    'cv': lambda x: x.std() / x.mean()
}))

同时应用多个函数 #

python
# 使用列表
print(df.groupby('department')['salary'].agg(['mean', 'std', 'min', 'max']))

# 使用字典
print(df.groupby('department').agg({
    'salary': ['mean', 'std'],
    'performance': ['mean', 'max'],
    'years_exp': 'mean'
}))

# 命名聚合(推荐)
print(df.groupby('department').agg(
    avg_salary=('salary', 'mean'),
    std_salary=('salary', 'std'),
    avg_performance=('performance', 'mean'),
    employee_count=('employee_id', 'count')
))

transform 方法 #

transform 返回与原数据相同形状的结果,常用于组内标准化。

python
# 组内标准化
df['salary_std'] = df.groupby('department')['salary'].transform(
    lambda x: (x - x.mean()) / x.std()
)

# 组内排名
df['salary_rank'] = df.groupby('department')['salary'].transform(
    lambda x: x.rank(ascending=False)
)

# 组内填充缺失值
df['salary_filled'] = df.groupby('department')['salary'].transform(
    lambda x: x.fillna(x.mean())
)

# 组内累计求和
df['cumsum_salary'] = df.groupby('department')['salary'].transform('cumsum')

# 组内累计计数
df['cumcount'] = df.groupby('department').cumcount()

apply 方法 #

apply 可以对每个分组应用任意函数,返回值可以是标量、Series 或 DataFrame。

python
# 返回标量
print(df.groupby('department').apply(lambda x: x['salary'].max() - x['salary'].min()))

# 返回 Series
def top_n(df, n=3, column='salary'):
    return df.nlargest(n, column)

print(df.groupby('department').apply(top_n))

# 返回 DataFrame
def summary(group):
    return pd.DataFrame({
        'mean': group.mean(numeric_only=True),
        'std': group.std(numeric_only=True)
    })

print(df.groupby('department').apply(summary))

filter 方法 #

filter 根据分组条件过滤整个分组。

python
# 过滤小分组
print(df.groupby('department').filter(lambda x: len(x) > 30))

# 过滤满足条件的分组
print(df.groupby('department').filter(lambda x: x['salary'].mean() > 60000))

# 过滤后保留原索引
filtered = df.groupby('department').filter(lambda x: x['performance'].mean() > 75)
print(filtered)

分组操作技巧 #

分组后排序 #

python
# 分组后排序取前 N
print(df.groupby('department').apply(lambda x: x.nlargest(3, 'salary')))

# 分组后排序
print(df.groupby('department')['salary'].rank(ascending=False))

分组后采样 #

python
# 每组随机采样
print(df.groupby('department').sample(2))

# 每组采样比例
print(df.groupby('department').sample(frac=0.5))

分组后累计操作 #

python
# 累计求和
print(df.groupby('department')['salary'].cumsum())

# 累计最大值
print(df.groupby('department')['salary'].cummax())

# 累计最小值
print(df.groupby('department')['salary'].cummin())

# 累计计数
print(df.groupby('department').cumcount())

分组后滚动窗口 #

python
# 分组后滚动平均
print(df.groupby('department')['salary'].rolling(3).mean())

# 分组后扩展窗口
print(df.groupby('department')['salary'].expanding().mean())

透视表 #

基本透视表 #

python
# 简单透视表
print(pd.pivot_table(df, values='salary', index='department', aggfunc='mean'))

# 多值
print(pd.pivot_table(df, 
                      values=['salary', 'performance'], 
                      index='department', 
                      aggfunc='mean'))

# 多索引
print(pd.pivot_table(df, 
                      values='salary', 
                      index=['department', 'team'], 
                      aggfunc='mean'))

# 多列
print(pd.pivot_table(df, 
                      values='salary', 
                      index='department', 
                      columns='team', 
                      aggfunc='mean'))

# 填充缺失值
print(pd.pivot_table(df, 
                      values='salary', 
                      index='department', 
                      columns='team', 
                      aggfunc='mean',
                      fill_value=0))

# 添加汇总
print(pd.pivot_table(df, 
                      values='salary', 
                      index='department', 
                      columns='team', 
                      aggfunc='mean',
                      margins=True))

多聚合函数透视表 #

python
print(pd.pivot_table(df, 
                      values=['salary', 'performance'], 
                      index='department', 
                      columns='team', 
                      aggfunc={'salary': 'mean', 'performance': 'max'}))

分组时间序列 #

python
# 创建时间序列数据
dates = pd.date_range('2024-01-01', periods=100, freq='D')
df_ts = pd.DataFrame({
    'date': dates,
    'category': np.random.choice(['A', 'B'], 100),
    'value': np.random.randn(100).cumsum()
})
df_ts.set_index('date', inplace=True)

# 按月分组
print(df_ts.groupby('category').resample('M').mean())

# 按周分组
print(df_ts.groupby('category').resample('W').sum())

性能优化 #

使用内置函数 #

python
# 快:内置函数
df.groupby('department')['salary'].mean()

# 慢:自定义函数
df.groupby('department')['salary'].agg(lambda x: x.mean())

避免重复分组 #

python
# 不推荐:重复分组
df.groupby('department')['salary'].mean()
df.groupby('department')['performance'].mean()

# 推荐:一次分组
grouped = df.groupby('department')
grouped['salary'].mean()
grouped['performance'].mean()

# 或使用 agg
df.groupby('department').agg({'salary': 'mean', 'performance': 'mean'})

使用分类类型 #

python
# 分组列使用 category 类型可以加速
df['department'] = df['department'].astype('category')
df.groupby('department')['salary'].mean()

实用案例 #

分组统计报告 #

python
def group_report(df, group_col, value_col):
    return df.groupby(group_col)[value_col].agg([
        ('count', 'count'),
        ('mean', 'mean'),
        ('std', 'std'),
        ('min', 'min'),
        ('25%', lambda x: x.quantile(0.25)),
        ('50%', 'median'),
        ('75%', lambda x: x.quantile(0.75)),
        ('max', 'max')
    ])

print(group_report(df, 'department', 'salary'))

分组 Top N #

python
def top_n_per_group(df, group_col, value_col, n=3):
    return df.groupby(group_col).apply(
        lambda x: x.nlargest(n, value_col)
    ).reset_index(drop=True)

print(top_n_per_group(df, 'department', 'salary', n=3))

下一步 #

掌握了分组聚合后,接下来学习 合并连接,了解如何合并多个 DataFrame!

最后更新:2026-04-04