Pandas df.groupby() 函数
groupby() 是 Pandas 中最强大的分组操作函数之一,它允许你根据一个或多个列的值将数据分成不同的组,然后对每个组进行各种操作。
简单来说,groupby() 实现了"拆分-应用-合并"(Split-Apply-Combine)的工作流程:先把数据按条件拆分,对每个组应用相应的函数,最后把结果合并起来。
这在数据分析中非常常见,比如按部门统计员工平均工资、按月份统计销售额、按地区统计用户数量等。
基本语法与参数
groupby() 是 DataFrame 的成员函数,通过点运算符 . 来调用。调用后会返回一个 GroupBy 对象,这个对象本身不会直接显示结果,需要配合聚合函数一起使用。
语法格式
DataFrame.groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, observed=False, dropna=True)
参数说明
| 参数 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| by | str、list 或 dict | 用于分组的列名或列名列表。如果是字典或函数,则按其结果分组。 | None |
| axis | int | 分组的轴方向,0 表示按行(默认),1 表示按列。 | 0 |
| level | int 或 str | 如果是多层索引(MultiIndex),按指定层级分组。 | None |
| as_index | bool | 如果为 True,分组列将作为返回结果的索引;如果为 False,分组列会作为普通列保留。 | True |
| sort | bool | 是否对分组标签进行排序。设置为 False 可以提高性能。 | True |
| group_keys | bool | 调用 apply() 时,是否在结果中添加分组键作为索引。 |
True |
| observed | bool | 如果为 True,则只显示分类变量的实际观测值,而不是所有可能的值。 | False |
| dropna | bool | 如果为 True,则包含 NA/null 值的组将被丢弃。 | True |
返回值
- 返回类型:
DataFrameGroupBy或SeriesGroupBy对象 - 说明:返回的是一个分组对象,不是最终结果。后续需要调用聚合函数(如
sum()、mean()、count()等)才能得到具体的计算结果。
实例
让我们通过一系列从简单到复杂的例子,彻底掌握 groupby() 的用法。
示例 1:按单列分组
最基础的用法是按照某一列的值进行分组。假设我们有一个销售数据表,需要按地区统计总销售额。
实例
import pandas as pd
# 创建一个简单的销售数据 DataFrame
# 模拟一个包含地区、产品、销售额的表格
data = {
'地区': ['华北', '华东', '华南', '华北', '华东', '华南', '华北', '华东'],
'产品': ['A', 'B', 'C', 'B', 'A', 'C', 'A', 'B'],
'销售额': [1000, 2000, 1500, 1800, 2200, 1600, 1200, 2100]
}
# 创建 DataFrame
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print()
# 按"地区"列分组,计算每个地区的总销售额
# as_index=True 表示地区作为索引返回
grouped = df.groupby('地区', as_index=True)['销售额'].sum()
print("按地区分组后的总销售额:")
print(grouped)
print()
# as_index=False 时,分组列会作为普通列保留
grouped_df = df.groupby('地区', as_index=False)['销售额'].sum()
print("as_index=False 时的结果:")
print(grouped_df)
# 创建一个简单的销售数据 DataFrame
# 模拟一个包含地区、产品、销售额的表格
data = {
'地区': ['华北', '华东', '华南', '华北', '华东', '华南', '华北', '华东'],
'产品': ['A', 'B', 'C', 'B', 'A', 'C', 'A', 'B'],
'销售额': [1000, 2000, 1500, 1800, 2200, 1600, 1200, 2100]
}
# 创建 DataFrame
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print()
# 按"地区"列分组,计算每个地区的总销售额
# as_index=True 表示地区作为索引返回
grouped = df.groupby('地区', as_index=True)['销售额'].sum()
print("按地区分组后的总销售额:")
print(grouped)
print()
# as_index=False 时,分组列会作为普通列保留
grouped_df = df.groupby('地区', as_index=False)['销售额'].sum()
print("as_index=False 时的结果:")
print(grouped_df)
运行结果预期:
原始数据: 地区 产品 销售额 0 华东 B 2000 1 华南 C 1500 2 华北 A 1000 3 华东 B 1800 4 华南 C 2200 5 华北 A 1600 6 华东 A 1200 7 华南 B 2100 按地区分组后的总销售额: 地区 华东 7100 华南 7300 华北 3600 dtype: int64 as_index=False 时的结果: 地区 销售额 0 华东 7100 1 华南 7300 2 华北 3600
代码解析:
df.groupby('地区')按照"地区"列的值将数据分成三组:华东、华南、华北。['销售额'].sum()表示只对"销售额"列进行求和聚合。as_index=True(默认值)时,返回的 Series 以地区作为索引;as_index=False时,返回的 DataFrame 保留地区作为普通列,更适合后续处理。
示例 2:按多列分组
有时需要同时按多个列进行分组,比如按地区和产品统计销售额。
实例
import pandas as pd
# 创建销售数据
data = {
'地区': ['华北', '华东', '华南', '华北', '华东', '华南', '华北', '华东'],
'产品': ['A', 'B', 'C', 'B', 'A', 'C', 'A', 'B'],
'销售额': [1000, 2000, 1500, 1800, 2200, 1600, 1200, 2100]
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print()
# 按"地区"和"产品"两列分组,计算总销售额
# 使用列表指定多个分组列
grouped = df.groupby(['地区', '产品'], as_index=False)['销售额'].sum()
print("按地区和产品分组后的总销售额:")
print(grouped)
print()
# 使用 pivot_table 更直观地展示结果
pivot = df.pivot_table(values='销售额', index='地区', columns='产品', aggfunc='sum', fill_value=0)
print("使用 pivot_table 展示:")
print(pivot)
# 创建销售数据
data = {
'地区': ['华北', '华东', '华南', '华北', '华东', '华南', '华北', '华东'],
'产品': ['A', 'B', 'C', 'B', 'A', 'C', 'A', 'B'],
'销售额': [1000, 2000, 1500, 1800, 2200, 1600, 1200, 2100]
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print()
# 按"地区"和"产品"两列分组,计算总销售额
# 使用列表指定多个分组列
grouped = df.groupby(['地区', '产品'], as_index=False)['销售额'].sum()
print("按地区和产品分组后的总销售额:")
print(grouped)
print()
# 使用 pivot_table 更直观地展示结果
pivot = df.pivot_table(values='销售额', index='地区', columns='产品', aggfunc='sum', fill_value=0)
print("使用 pivot_table 展示:")
print(pivot)
运行结果预期:
原始数据: 地区 产品 销售额 0 华北 A 1000 1 华东 B 2000 2 华南 C 1500 3 华北 B 1800 4 华东 A 2200 5 华南 C 1600 6 华北 A 1200 7 华东 B 2100 按地区和产品分组后的总销售额: 地区 产品 销售额 0 华东 A 2200 1 华东 B 4100 2 华南 C 3100 3 华北 A 2200 4 华北 B 1800 使用 pivot_table 展示: 产品 A B C 地区 华北 2200 1800 0 华东 2200 4100 0 华南 0 0 3100
代码解析:
['地区', '产品']使用列表可以同时按多列分组,结果会产生多级索引。as_index=False时,分组列会作为普通列保留在结果中,方便查看和后续处理。pivot_table()提供了类似的交叉表功能,可以将分组结果以更直观的方式呈现。
示例 3:使用字典和函数分组
groupby() 不仅可以按列名分组,还支持通过字典或函数来定义分组规则,这在需要自定义分组逻辑时非常有用。
实例
import pandas as pd
# 创建学生成绩数据
data = {
'姓名': ['张三', '李四', '王五', '赵六', '孙七', '周八'],
'语文': [85, 92, 78, 88, 95, 82],
'数学': [90, 85, 92, 78, 88, 91],
'英语': [88, 90, 85, 92, 87, 89]
}
df = pd.DataFrame(data)
print("原始学生成绩数据:")
print(df)
print()
# 1. 使用字典进行自定义分组
# 假设我们要按照"姓氏"分组(张、李、王算一组,赵、孙、周算另一组)
def get_surname_group(name):
"""根据姓氏返回分组名称"""
if name in ['张三', '李四', '王五']:
return '第一组'
else:
return '第二组'
# 使用 apply 函数进行分组
grouped = df.groupby(get_surname_group).mean(numeric_only=True)
print("使用函数自定义分组(计算每组平均分):")
print(grouped)
print()
# 2. 使用字典映射特定列的分组
# 按数值区间将语文成绩分为"优秀"和"良好"两组
score_mapping = {
'语文': lambda x: '优秀' if x >= 90 else '良好'
}
# 对语文列按条件分组
grouped_by_score = df.groupby(lambda x: '优秀' if df.loc[x, '语文'] >= 90 else '良好').mean(numeric_only=True)
print("按语文成绩分组(>=90为优秀):")
print(grouped_by_score)
# 创建学生成绩数据
data = {
'姓名': ['张三', '李四', '王五', '赵六', '孙七', '周八'],
'语文': [85, 92, 78, 88, 95, 82],
'数学': [90, 85, 92, 78, 88, 91],
'英语': [88, 90, 85, 92, 87, 89]
}
df = pd.DataFrame(data)
print("原始学生成绩数据:")
print(df)
print()
# 1. 使用字典进行自定义分组
# 假设我们要按照"姓氏"分组(张、李、王算一组,赵、孙、周算另一组)
def get_surname_group(name):
"""根据姓氏返回分组名称"""
if name in ['张三', '李四', '王五']:
return '第一组'
else:
return '第二组'
# 使用 apply 函数进行分组
grouped = df.groupby(get_surname_group).mean(numeric_only=True)
print("使用函数自定义分组(计算每组平均分):")
print(grouped)
print()
# 2. 使用字典映射特定列的分组
# 按数值区间将语文成绩分为"优秀"和"良好"两组
score_mapping = {
'语文': lambda x: '优秀' if x >= 90 else '良好'
}
# 对语文列按条件分组
grouped_by_score = df.groupby(lambda x: '优秀' if df.loc[x, '语文'] >= 90 else '良好').mean(numeric_only=True)
print("按语文成绩分组(>=90为优秀):")
print(grouped_by_score)
运行结果预期:
原始学生成绩数据:
姓名 语文 数学 英语
0 张三 85 90 88
1 李四 92 85 90
2 王五 78 92 85
3 赵六 88 78 92
4 孙七 95 88 87
5 周八 82 91 89
使用函数自定义分组(计算每组平均分):
语文 数学 英语
姓名
第一组 85.000000 89.000000 87.666667
第二组 88.333333 85.666667 89.333333
按语文成绩分组(>=90为优秀):
语文 数学 英语
语文
优秀 93.500000 86.500000 88.500000
良好 83.250000 89.500000 88.750000
代码解析:
- 自定义函数分组可以处理复杂的分组逻辑,只要函数返回一个用于分组的值即可。
- 使用
numeric_only=True参数可以只对数值列进行聚合计算,避免对非数值列(如姓名)进行操作时出错。 - 通过字典映射可以实现"按条件将不同列分成不同组"的需求。
示例 4:分组后迭代处理
有时候我们需要对每个分组进行更复杂的自定义操作,这时可以遍历分组对象。
实例
import pandas as pd
# 创建销售数据
data = {
'地区': ['华北', '华东', '华南', '华北', '华东', '华南'],
'产品': ['A', 'B', 'C', 'B', 'A', 'C'],
'销售额': [1000, 2000, 1500, 1800, 2200, 1600]
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print()
# 遍历每个分组进行处理
print("每个分组的详细信息:")
print("-" * 40)
for group_name, group_data in df.groupby('地区'):
print(f"n分组名称: {group_name}")
print(f"该组数据行数: {len(group_data)}")
print(f"该组销售总额: {group_data['销售额'].sum()}")
print(f"该组平均销售额: {group_data['销售额'].mean():.2f}")
print("-" * 40)
# 创建销售数据
data = {
'地区': ['华北', '华东', '华南', '华北', '华东', '华南'],
'产品': ['A', 'B', 'C', 'B', 'A', 'C'],
'销售额': [1000, 2000, 1500, 1800, 2200, 1600]
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print()
# 遍历每个分组进行处理
print("每个分组的详细信息:")
print("-" * 40)
for group_name, group_data in df.groupby('地区'):
print(f"n分组名称: {group_name}")
print(f"该组数据行数: {len(group_data)}")
print(f"该组销售总额: {group_data['销售额'].sum()}")
print(f"该组平均销售额: {group_data['销售额'].mean():.2f}")
print("-" * 40)
运行结果预期:
原始数据: 地区 产品 销售额 0 华东 B 2000 1 华南 C 1500 2 华北 A 1000 3 华东 B 1800 4 华南 C 2200 5 华北 B 1800 每个分组的详细信息: ---------------------------------------- 分组名称: 华东 该组数据行数: 2 该组销售总额: 3800 该组平均销售额: 1900.00 ---------------------------------------- 分组名称: 华南 该组数据行数: 2 3700 1850.00 ---------------------------------------- 分组名称: 华北 该组数据行数: 2 2800 1400.00 ----------------------------------------
代码解析:
groupby()返回的分组对象可以直接迭代,每次迭代返回一个元组:(分组名, 该组的数据)。- 通过这种方式,可以对每个分组执行任意复杂的自定义操作。
- 在实际应用中,这种方式常用于生成复杂的报表或进行数据清洗。
提示:
groupby()返回的 GroupBy 对象是"延迟执行"的,不会立即计算结果。只有调用聚合函数(如sum()、mean()、count()等)或进行迭代时才会真正执行计算。

Pandas 常用函数