第148集:数据分组与聚合
一、课程概述
在数据分析中,数据分组与聚合是核心操作之一。通过分组,我们可以将数据按照特定维度进行划分;通过聚合,我们可以计算每组数据的统计指标(如平均值、总和、最大值等)。这种操作模式在实际业务中非常常见,例如按部门统计员工平均工资、按地区分析销售业绩等。本集将详细介绍Pandas中数据分组与聚合的各种方法和技巧。
二、数据分组的基本概念
数据分组与聚合的基本流程可以概括为:Split(拆分)→ Apply(应用)→ Combine(合并)。
- Split(拆分):将数据按照指定的键或条件拆分成若干组。
- Apply(应用):对每组数据应用特定的函数或操作(如聚合、转换、过滤等)。
- Combine(合并):将应用操作后的结果合并成一个新的数据结构。
Pandas提供了groupby()方法来实现数据分组,该方法返回一个GroupBy对象,然后可以对这个对象应用各种聚合函数。
三、groupby()方法的基本使用
1. 按单列分组
import pandas as pd
# 创建示例数据
data = {
'姓名': ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十'],
'班级': ['一班', '一班', '二班', '二班', '三班', '三班', '四班', '四班'],
'性别': ['男', '男', '女', '男', '女', '男', '女', '男'],
'数学': [85, 92, 78, 95, 88, 76, 90, 82],
'英语': [80, 88, 76, 92, 85, 74, 89, 77]
}
df = pd.DataFrame(data)
# 按班级分组
grouped_class = df.groupby('班级')
# 查看分组情况
print("按班级分组情况:")
for name, group in grouped_class:
print(f"\n{name}班:")
print(group)2. 分组后的基本信息
# 查看分组数量
print(f"\n分组数量:{grouped_class.ngroups}")
# 查看分组键
print(f"分组键:{grouped_class.groups.keys()}")
# 查看特定分组的数据
print("\n一班的数据:")
print(grouped_class.get_group('一班'))四、分组后的聚合操作
1. 内置聚合函数
Pandas提供了多种内置的聚合函数,可以直接应用于GroupBy对象:
# 计算每个班级的数学平均分
avg_math = grouped_class['数学'].mean()
print("每个班级的数学平均分:")
print(avg_math)
print()
# 计算每个班级的英语最高分
high_english = grouped_class['英语'].max()
print("每个班级的英语最高分:")
print(high_english)
print()
# 计算每个班级的学生人数
student_count = grouped_class.size()
print("每个班级的学生人数:")
print(student_count)
print()
# 计算每个班级的数学成绩总和
total_math = grouped_class['数学'].sum()
print("每个班级的数学成绩总和:")
print(total_math)
print()常用的内置聚合函数包括:
mean():平均值sum():总和max():最大值min():最小值count():非空值数量size():元素数量(包括空值)std():标准差var():方差median():中位数first():第一个元素last():最后一个元素
2. 对多个列应用相同的聚合函数
# 计算每个班级的数学和英语平均分
avg_scores = grouped_class[['数学', '英语']].mean()
print("每个班级的数学和英语平均分:")
print(avg_scores)
print()
# 计算每个班级的数学和英语成绩总和
total_scores = grouped_class[['数学', '英语']].sum()
print("每个班级的数学和英语成绩总和:")
print(total_scores)
print()3. 对不同列应用不同的聚合函数
使用agg()方法可以对不同列应用不同的聚合函数:
# 对数学列求平均分,对英语列求最高分
class_stats = grouped_class.agg({
'数学': 'mean',
'英语': 'max'
})
print("每个班级的数学平均分和英语最高分:")
print(class_stats)
print()
# 对同一列应用多个聚合函数
math_stats = grouped_class['数学'].agg(['mean', 'max', 'min', 'sum', 'std'])
print("每个班级的数学成绩统计:")
print(math_stats)
print()
# 对不同列应用多个聚合函数
stats = grouped_class.agg({
'数学': ['mean', 'max', 'min'],
'英语': ['mean', 'sum']
})
print("每个班级的成绩统计:")
print(stats)
print()五、多列分组
可以按照多个列进行分组,形成层次化的索引:
# 按班级和性别分组
grouped_class_gender = df.groupby(['班级', '性别'])
# 计算每组的数学平均分
avg_math_by_class_gender = grouped_class_gender['数学'].mean()
print("按班级和性别分组的数学平均分:")
print(avg_math_by_class_gender)
print()
# 重置索引
avg_math_reset = avg_math_by_class_gender.reset_index()
print("重置索引后的结果:")
print(avg_math_reset)
print()六、自定义聚合函数
除了内置的聚合函数,我们还可以使用自定义函数进行聚合:
# 定义自定义聚合函数:计算成绩的范围(最高分-最低分)
def score_range(x):
return x.max() - x.min()
# 计算每个班级的数学成绩范围
range_math = grouped_class['数学'].agg(score_range)
print("每个班级的数学成绩范围:")
print(range_math)
print()
# 在agg中使用多个自定义函数
def score_variance(x):
return x.var()
math_custom_stats = grouped_class['数学'].agg(['mean', score_range, score_variance])
print("每个班级的数学成绩自定义统计:")
print(math_custom_stats)
print()
# 对不同列使用不同的自定义函数
custom_stats = grouped_class.agg({
'数学': score_range,
'英语': score_variance
})
print("每个班级的自定义成绩统计:")
print(custom_stats)
print()七、分组后的数据转换
除了聚合操作,还可以对分组后的数据进行转换操作,常用的方法包括transform()和apply()。
1. transform()方法
transform()方法对每个分组内的元素应用函数,并返回与原数据形状相同的结果:
# 计算每个班级的数学平均分
avg_math_class = grouped_class['数学'].transform('mean')
print("每个班级的数学平均分:")
print(avg_math_class)
print()
# 在原DataFrame中添加平均分列
df['班级数学平均分'] = avg_math_class
print("添加班级数学平均分后的DataFrame:")
print(df)
print()
# 使用自定义函数:计算每个学生的数学成绩与班级平均分的差值
def score_diff(x):
return x - x.mean()
df['数学成绩与班级平均差值'] = grouped_class['数学'].transform(score_diff)
print("添加数学成绩与班级平均差值后的DataFrame:")
print(df)
print()2. apply()方法
apply()方法可以对每个分组应用任意函数,灵活性更高:
# 使用apply计算每个班级的数学平均分
avg_math_apply = grouped_class.apply(lambda x: x['数学'].mean())
print("使用apply计算每个班级的数学平均分:")
print(avg_math_apply)
print()
# 使用apply对分组数据进行更复杂的操作
def get_top_student(x):
return x.nlargest(1, '数学')
top_students = grouped_class.apply(get_top_student)
print("每个班级数学成绩最高的学生:")
print(top_students)
print()八、分组过滤
使用filter()方法可以根据分组后的条件过滤数据:
# 过滤出班级人数大于等于2的班级数据
filtered_class = grouped_class.filter(lambda x: len(x) >= 2)
print("班级人数大于等于2的班级数据:")
print(filtered_class)
print()
# 过滤出数学平均分大于80分的班级数据
filtered_math = grouped_class.filter(lambda x: x['数学'].mean() > 80)
print("数学平均分大于80分的班级数据:")
print(filtered_math)
print()九、综合案例:销售数据分析
案例描述
现有一个销售数据集,包含销售日期、地区、产品类别、销售额、成本等字段。需要进行以下分析:
- 按地区分组,计算每个地区的销售总额、成本总额和利润(销售额-成本)。
- 按产品类别分组,计算每个类别的平均销售额、最大销售额和最小销售额。
- 按地区和产品类别分组,计算每个组合的销售总额和利润。
- 找出每个地区利润最高的产品类别。
实现代码
import pandas as pd
import numpy as np
# 创建销售数据集
dates = pd.date_range('2023-01-01', '2023-03-31', freq='D')
sales_data = {
'日期': np.random.choice(dates, 100),
'地区': np.random.choice(['华东', '华南', '华北', '华西'], 100),
'产品类别': np.random.choice(['电子产品', '家居用品', '服装', '食品'], 100),
'销售额': np.random.randint(1000, 10000, 100),
'成本': np.random.randint(500, 6000, 100)
}
sales_df = pd.DataFrame(sales_data)
# 计算利润
sales_df['利润'] = sales_df['销售额'] - sales_df['成本']
print("原始销售数据:")
print(sales_df.head())
print()
# 1. 按地区分组,计算销售总额、成本总额和利润
region_stats = sales_df.groupby('地区').agg({
'销售额': 'sum',
'成本': 'sum',
'利润': 'sum'
})
print("1. 各地区销售统计:")
print(region_stats)
print()
# 2. 按产品类别分组,计算平均销售额、最大销售额和最小销售额
product_stats = sales_df.groupby('产品类别')['销售额'].agg(['mean', 'max', 'min'])
print("2. 各类别产品销售统计:")
print(product_stats)
print()
# 3. 按地区和产品类别分组,计算销售总额和利润
region_product_stats = sales_df.groupby(['地区', '产品类别']).agg({
'销售额': 'sum',
'利润': 'sum'
})
print("3. 各地区各类别产品销售统计:")
print(region_product_stats)
print()
# 4. 找出每个地区利润最高的产品类别
def top_profit_product(x):
return x.nlargest(1, '利润')
top_products = sales_df.groupby('地区').apply(top_profit_product)
print("4. 各地区利润最高的产品类别:")
print(top_products[['产品类别', '销售额', '成本', '利润']])
print()十、注意事项
分组键的数据类型:分组键可以是数值型、字符串型、日期型等,但要注意数据的一致性,避免因数据类型不一致导致的分组错误。
缺失值处理:默认情况下,分组键中的缺失值(NaN)会被排除在结果之外。
性能考虑:对于大型数据集,分组操作可能会比较耗时。可以通过以下方式提高性能:
- 只选择需要的列进行分组和聚合
- 使用内置的聚合函数(比自定义函数更快)
- 考虑使用
Cython或Numba加速自定义函数
层次化索引:多列分组后会产生层次化索引,需要使用
reset_index()方法将其转换为普通索引,便于后续处理。聚合函数的返回值:聚合函数应该返回一个标量值,否则可能会导致结果不符合预期。
十一、总结
本集详细介绍了Pandas中数据分组与聚合的各种方法和技巧,包括:
分组的基本概念:Split(拆分)→ Apply(应用)→ Combine(合并)。
groupby()方法:
- 按单列分组
- 按多列分组
- 分组后的基本信息
聚合操作:
- 内置聚合函数
- 对多个列应用相同的聚合函数
- 对不同列应用不同的聚合函数
- 对同一列应用多个聚合函数
自定义聚合函数:使用用户定义的函数进行聚合。
分组后的数据转换:
- transform()方法
- apply()方法
分组过滤:使用filter()方法根据分组条件过滤数据。
综合案例:销售数据分析,综合运用了分组、聚合、转换和过滤功能。
通过本集的学习,您已经掌握了Pandas中数据分组与聚合的核心技能,这些技能是数据分析的重要基础,将在实际工作中频繁使用。
十二、练习
创建一个包含员工信息的DataFrame,包括姓名、部门、职位、工资等字段,然后:
- 按部门分组,计算每个部门的平均工资、最高工资和最低工资
- 按部门和职位分组,计算每个组合的平均工资
- 找出每个部门工资最高的员工
针对销售数据集,尝试:
- 按日期(月份)分组,计算每月的销售总额
- 按地区分组,计算每个地区的平均利润和利润方差
- 过滤出销售总额大于50000的地区
通过这些练习,您可以进一步巩固本集所学的知识。
十三、扩展阅读
- Pandas官方文档:GroupBy: split-apply-combine
- 《Python for Data Analysis》(Wes McKinney著)第10章:数据聚合与分组操作
这些资源可以帮助您更深入地了解数据分组与聚合的原理和应用。