当前位置:首页>python>15_Python数据分析:分组操作 (GroupBy)

15_Python数据分析:分组操作 (GroupBy)

  • 2026-09-02 19:16:39
15_Python数据分析:分组操作 (GroupBy)

Python数据分析:分组操作 (GroupBy)

1. 核心知识点概述

GroupBy是Pandas中非常强大的数据分析工具,遵循"拆分-应用-合并"(split-apply-combine)模式:

  • groupby()
    : 按指定列分组,创建GroupBy对象。
  • 聚合操作
    : sum()、mean()、count()、min()、max()等。
  • 转换操作
    : transform()对每个分组进行转换,保持原DataFrame形状。
  • 过滤操作
    : filter()根据条件筛选分组。
  • 应用自定义函数
    : apply()对每个分组应用自定义函数。

关键参数说明

  • by
    : 分组依据的列名或列名列表。
  • as_index
    : 是否将分组键作为索引(默认True)。
  • sort
    : 是否对分组键排序(默认True)。
  • agg()
    : 同时应用多个聚合函数。

2. 示例代码

2.1 准备数据

In [1]:

import pandas as pd
import numpy as np
# 创建示例数据
np.random.seed(42)
data = {
    'department': ['Sales', 'Sales', 'IT', 'IT', 'HR', 'HR', 'Sales', 'IT', 'HR', 'Sales'],
    'employee': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack'],
    'gender': ['F', 'M', 'M', 'M', 'F', 'M', 'F', 'M', 'F', 'M'],
    'salary': np.random.randint(5000, 15000, 10),
    'bonus': np.random.randint(500, 2000, 10),
    'years': np.random.randint(1, 10, 10)
}
df = pd.DataFrame(data)
df['total_comp'] = df['salary'] + df['bonus']
print("原始数据:")
print(df)
原始数据:
  department employee gender  salary  bonus  years  total_comp
0      Sales    Alice      F   12270   1832      6       14102
1      Sales      Bob      M    5860   1269      9        7129
2         IT  Charlie      M   10390    843      1       11233
3         IT    David      M   10191   1937      3       12128
4         HR      Eve      F   10734   1305      7       12039
5         HR    Frank      M   11265    885      4       12150
6      Sales    Grace      F    5466   1715      9        7181
7         IT    Henry      M    9426   1455      3       10881
8         HR      Ivy      F   10578    776      5       11354
9      Sales     Jack      M   13322   1684      3       15006

2.2 基础分组与聚合

按单列分组并进行聚合计算。

In [2]:

# 按部门分组,计算平均工资
dept_avg_salary = df.groupby('department')['salary'].mean()
print("\n各部门平均工资:")
print(dept_avg_salary)
# 按部门分组,计算多个统计量
dept_stats = df.groupby('department')['salary'].agg(['count', 'sum', 'mean', 'min', 'max'])
print("\n各部门薪资统计:")
print(dept_stats)
# 多列聚合
dept_multi = df.groupby('department')[['salary', 'bonus', 'total_comp']].mean()
print("\n各部门平均薪资、奖金、总薪酬:")
print(dept_multi)
各部门平均工资:
department
HR       10859.000000
IT       10002.333333
Sales     9229.500000
Name: salary, dtype: float64
各部门薪资统计:
            count    sum          mean    min    max
department                                          
HR              3  32577  10859.000000  10578  11265
IT              3  30007  10002.333333   9426  10390
Sales           4  36918   9229.500000   5466  13322
各部门平均薪资、奖金、总薪酬:
                  salary        bonus    total_comp
department                                         
HR          10859.000000   988.666667  11847.666667
IT          10002.333333  1411.666667  11414.000000
Sales        9229.500000  1625.000000  10854.500000

2.3 多列分组

按多个列进行分组。

In [3]:

# 按部门和性别分组
dept_gender = df.groupby(['department', 'gender'])['salary'].mean()
print("\n各部门性别平均工资:")
print(dept_gender)
# 重置索引
dept_gender_reset = df.groupby(['department', 'gender'], as_index=False)['salary'].mean()
print("\n重置索引后:")
print(dept_gender_reset)
# 多列分组多聚合
dept_gender_stats = df.groupby(['department', 'gender']).agg({
    'salary': ['mean', 'min', 'max'],
    'years': 'mean'
})
print("\n各部门性别详细统计:")
print(dept_gender_stats)
各部门性别平均工资:
department  gender
HR          F         10656.000000
            M         11265.000000
IT          M         10002.333333
Sales       F          8868.000000
            M          9591.000000
Name: salary, dtype: float64
重置索引后:
  department gender        salary
0         HR      F  10656.000000
1         HR      M  11265.000000
2         IT      M  10002.333333
3      Sales      F   8868.000000
4      Sales      M   9591.000000
各部门性别详细统计:
                         salary                   years
                           mean    min    max      mean
department gender                                      
HR         F       10656.000000  10578  10734  6.000000
           M       11265.000000  11265  11265  4.000000
IT         M       10002.333333   9426  10390  2.333333
Sales      F        8868.000000   5466  12270  7.500000
           M        9591.000000   5860  13322  6.000000

2.4 agg() 多聚合函数

使用agg()同时应用多个聚合函数。

In [4]:

# 对单列应用多个聚合函数
salary_agg = df.groupby('department')['salary'].agg(['count', 'sum', 'mean', 'std', 'min', 'max'])
print("\n薪资多维度统计:")
print(salary_agg)
# 对不同列应用不同聚合函数
custom_agg = df.groupby('department').agg({
    'salary': ['mean', 'max'],
    'bonus': 'sum',
    'years': ['mean', 'min', 'max'],
    'employee': 'count'
})
print("\n自定义聚合:")
print(custom_agg)
# 使用自定义聚合函数
def salary_range(x):
    return x.max() - x.min()
custom_func = df.groupby('department')['salary'].agg(['mean', salary_range])
custom_func.columns = ['mean_salary', 'salary_range']
print("\n使用自定义函数:")
print(custom_func)
薪资多维度统计:
            count    sum          mean          std    min    max
department                                                       
HR              3  32577  10859.000000   360.154134  10578  11265
IT              3  30007  10002.333333   508.940403   9426  10390
Sales           4  36918   9229.500000  4143.696377   5466  13322
自定义聚合:
                  salary        bonus     years         employee
                    mean    max   sum      mean min max    count
department                                                      
HR          10859.000000  11265  2966  5.333333   4   7        3
IT          10002.333333  10390  4235  2.333333   1   3        3
Sales        9229.500000  13322  6500  6.750000   3   9        4
使用自定义函数:
             mean_salary  salary_range
department                            
HR          10859.000000           687
IT          10002.333333           964
Sales        9229.500000          7856

2.5 transform() 转换操作

对每个分组进行转换,保持与原DataFrame相同的形状。

In [5]:

# 计算每个员工薪资相对于部门平均的差值
df['dept_avg_salary'] = df.groupby('department')['salary'].transform('mean')
df['salary_diff'] = df['salary'] - df['dept_avg_salary']
print("\n薪资与部门平均差值:")
print(df[['employee', 'department', 'salary', 'dept_avg_salary', 'salary_diff']])
# 计算部门内薪资排名
df['salary_rank'] = df.groupby('department')['salary'].transform('rank', ascending=False)
print("\n部门内薪资排名:")
print(df[['employee', 'department', 'salary', 'salary_rank']].sort_values(['department', 'salary_rank']))
# 标准化(z-score)
df['salary_zscore'] = df.groupby('department')['salary'].transform(lambda x: (x - x.mean()) / x.std())
print("\n部门内薪资标准化:")
print(df[['employee', 'department', 'salary', 'salary_zscore']])
薪资与部门平均差值:
  employee department  salary  dept_avg_salary  salary_diff
0    Alice      Sales   12270      9229.500000  3040.500000
1      Bob      Sales    5860      9229.500000 -3369.500000
2  Charlie         IT   10390     10002.333333   387.666667
3    David         IT   10191     10002.333333   188.666667
4      Eve         HR   10734     10859.000000  -125.000000
5    Frank         HR   11265     10859.000000   406.000000
6    Grace      Sales    5466      9229.500000 -3763.500000
7    Henry         IT    9426     10002.333333  -576.333333
8      Ivy         HR   10578     10859.000000  -281.000000
9     Jack      Sales   13322      9229.500000  4092.500000
部门内薪资排名:
  employee department  salary  salary_rank
5    Frank         HR   11265          1.0
4      Eve         HR   10734          2.0
8      Ivy         HR   10578          3.0
2  Charlie         IT   10390          1.0
3    David         IT   10191          2.0
7    Henry         IT    9426          3.0
9     Jack      Sales   13322          1.0
0    Alice      Sales   12270          2.0
1      Bob      Sales    5860          3.0
6    Grace      Sales    5466          4.0
部门内薪资标准化:
  employee department  salary  salary_zscore
0    Alice      Sales   12270       0.733765
1      Bob      Sales    5860      -0.813163
2  Charlie         IT   10390       0.761713
3    David         IT   10191       0.370705
4      Eve         HR   10734      -0.347074
5    Frank         HR   11265       1.127295
6    Grace      Sales    5466      -0.908247
7    Henry         IT    9426      -1.132418
8      Ivy         HR   10578      -0.780222
9     Jack      Sales   13322       0.987645

2.6 filter() 过滤分组

根据条件筛选分组。

In [6]:

# 筛选员工数大于2的部门
large_depts = df.groupby('department').filter(lambda x: len(x) > 2)
print("\n员工数大于2的部门:")
print(large_depts[['employee', 'department']])
# 筛选平均工资大于8000的部门
high_pay_depts = df.groupby('department').filter(lambda x: x['salary'].mean() > 8000)
print("\n平均工资大于8000的部门:")
print(high_pay_depts[['employee', 'department', 'salary']])
# 筛选薪资方差较小的部门(薪资比较均衡)
stable_depts = df.groupby('department').filter(lambda x: x['salary'].std() < 3000)
print("\n薪资较均衡的部门:")
print(stable_depts[['employee', 'department', 'salary']])
员工数大于2的部门:
  employee department
0    Alice      Sales
1      Bob      Sales
2  Charlie         IT
3    David         IT
4      Eve         HR
5    Frank         HR
6    Grace      Sales
7    Henry         IT
8      Ivy         HR
9     Jack      Sales
平均工资大于8000的部门:
  employee department  salary
0    Alice      Sales   12270
1      Bob      Sales    5860
2  Charlie         IT   10390
3    David         IT   10191
4      Eve         HR   10734
5    Frank         HR   11265
6    Grace      Sales    5466
7    Henry         IT    9426
8      Ivy         HR   10578
9     Jack      Sales   13322
薪资较均衡的部门:
  employee department  salary
2  Charlie         IT   10390
3    David         IT   10191
4      Eve         HR   10734
5    Frank         HR   11265
7    Henry         IT    9426
8      Ivy         HR   10578

2.7 apply() 应用自定义函数

对每个分组应用任意自定义函数。

In [7]:

# 获取每个部门薪资最高的员工
def get_top_earner(group):
    return group.loc[group['salary'].idxmax()]
top_earners = df.groupby('department', group_keys=False).apply(get_top_earner)
print("\n各部门薪资最高员工:")
print(top_earners[['employee', 'department', 'salary']])
# 计算每个部门的薪资分布
def salary_distribution(group):
    return pd.Series({
        'q25': group['salary'].quantile(0.25),
        'q50': group['salary'].median(),
        'q75': group['salary'].quantile(0.75),
        'iqr': group['salary'].quantile(0.75) - group['salary'].quantile(0.25)
    })
dist = df.groupby('department').apply(salary_distribution)
print("\n各部门薪资分布:")
print(dist)
各部门薪资最高员工:
           employee department  salary
department                            
HR            Frank         HR   11265
IT          Charlie         IT   10390
Sales          Jack      Sales   13322
各部门薪资分布:
                q25      q50      q75     iqr
department                                   
HR          10656.0  10734.0  10999.5   343.5
IT           9808.5  10191.0  10290.5   482.0
Sales        5761.5   9065.0  12533.0  6771.5
C:\Users\zhanghc\AppData\Local\Temp\ipykernel_7176\3295751641.py:5: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.
  top_earners = df.groupby('department', group_keys=False).apply(get_top_earner)
C:\Users\zhanghc\AppData\Local\Temp\ipykernel_7176\3295751641.py:18: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.
  dist = df.groupby('department').apply(salary_distribution)

2.8 分组迭代

遍历每个分组进行处理。

In [8]:

# 遍历分组
print("\n遍历各部门:")
for name, group in df.groupby('department'):
    print(f"\n部门: {name}")
    print(f"  员工数: {len(group)}")
    print(f"  平均薪资: {group['salary'].mean():.2f}")
    print(f"  最高薪资: {group['salary'].max()}")
    print(f"  最低薪资: {group['salary'].min()}")
# 获取单个分组
sales_dept = df.groupby('department').get_group('Sales')
print("\nSales部门详情:")
print(sales_dept[['employee', 'salary', 'bonus']])
遍历各部门:
部门: HR
  员工数: 3
  平均薪资: 10859.00
  最高薪资: 11265
  最低薪资: 10578
部门: IT
  员工数: 3
  平均薪资: 10002.33
  最高薪资: 10390
  最低薪资: 9426
部门: Sales
  员工数: 4
  平均薪资: 9229.50
  最高薪资: 13322
  最低薪资: 5466
Sales部门详情:
  employee  salary  bonus
0    Alice   12270   1832
1      Bob    5860   1269
6    Grace    5466   1715
9     Jack   13322   1684

2.9 分组描述性统计

快速获取分组描述性统计信息。

In [9]:

# 描述性统计
desc = df.groupby('department')['salary'].describe()
print("\n各部门薪资描述性统计:")
print(desc)
# 多列描述性统计
desc_multi = df.groupby('department')[['salary', 'bonus']].describe()
print("\n多列描述性统计:")
print(desc_multi)
# 使用size()统计分组大小
group_sizes = df.groupby('department').size()
print("\n各部门人数:")
print(group_sizes)
# 使用nunique()统计唯一值数量
unique_genders = df.groupby('department')['gender'].nunique()
print("\n各部门性别种类数:")
print(unique_genders)
各部门薪资描述性统计:
            count          mean          std      min      25%      50%  \
department                                                                
HR            3.0  10859.000000   360.154134  10578.0  10656.0  10734.0   
IT            3.0  10002.333333   508.940403   9426.0   9808.5  10191.0   
Sales         4.0   9229.500000  4143.696377   5466.0   5761.5   9065.0   
                75%      max  
department                    
HR          10999.5  11265.0  
IT          10290.5  10390.0  
Sales       12533.0  13322.0  
多列描述性统计:
           salary                                                        \
            count          mean          std      min      25%      50%   
department                                                                
HR            3.0  10859.000000   360.154134  10578.0  10656.0  10734.0   
IT            3.0  10002.333333   508.940403   9426.0   9808.5  10191.0   
Sales         4.0   9229.500000  4143.696377   5466.0   5761.5   9065.0   
                             bonus                                            \
                75%      max count         mean         std     min      25%   
department                                                                     
HR          10999.5  11265.0   3.0   988.666667  279.321201   776.0   830.50   
IT          10290.5  10390.0   3.0  1411.666667  548.285814   843.0  1149.00   
Sales       12533.0  13322.0   4.0  1625.000000  245.741056  1269.0  1580.25   
               50%      75%     max  
department                           
HR           885.0  1095.00  1305.0  
IT          1455.0  1696.00  1937.0  
Sales       1699.5  1744.25  1832.0  
各部门人数:
department
HR       3
IT       3
Sales    4
dtype: int64
各部门性别种类数:
department
HR       2
IT       1
Sales    2
Name: gender, dtype: int64

2.10 分组与透视表结合

groupby与pivot_table的对比使用。

In [10]:

# 使用groupby创建透视效果
pivot_groupby = df.groupby(['department', 'gender'])['salary'].mean().unstack()
print("\nGroupBy创建的透视表:")
print(pivot_groupby)
# 使用pivot_table
pivot_table = df.pivot_table(values='salary', index='department', columns='gender', aggfunc='mean')
print("\nPivot Table:")
print(pivot_table)
# 添加边际值
pivot_with_margins = df.pivot_table(values='salary', index='department', columns='gender', aggfunc='mean', margins=True)
print("\n带边际值的透视表:")
print(pivot_with_margins)
GroupBy创建的透视表:
gender            F             M
department                       
HR          10656.0  11265.000000
IT              NaN  10002.333333
Sales        8868.0   9591.000000
Pivot Table:
gender            F             M
department                       
HR          10656.0  11265.000000
IT              NaN  10002.333333
Sales        8868.0   9591.000000
带边际值的透视表:
gender            F             M           All
department                                     
HR          10656.0  11265.000000  10859.000000
IT              NaN  10002.333333  10002.333333
Sales        8868.0   9591.000000   9229.500000
All          9762.0  10075.666667   9950.200000

3. 常见应用场景总结

  1. 部门统计
    :按部门、地区等维度统计员工数、平均工资等。
  2. 时间聚合
    :按年、月、日聚合销售数据。
  3. 异常检测
    :通过transform计算每个值相对于组平均的偏差。
  4. 数据标准化
    :按组进行z-score标准化。
  5. 分组排名
    :计算每个元素在组内的排名。
  6. 筛选分组
    :只保留符合条件的分组进行后续分析。

最新文章

随机文章