趋近智
在数据分析以及为机器学习 (machine learning)准备数据时,根据特定类别或条件汇总数据是一个常见做法。分组和聚合操作为此变得不可或缺。Pandas提供了一种强大且灵活的groupby机制,可让您高效地执行这些任务。
分组操作的核心思想通常遵循一种称为分治-应用-合并的模式:
接下来我们看看这在Pandas中是如何运作的。
groupby() 方法Pandas中用于分组的主要工具是.groupby()方法。当在一个DataFrame上调用时,它不会立即进行任何计算,而是返回一个GroupBy对象。此对象包含对每个组应用操作所需的所有信息。
考虑一个表示销售数据的示例DataFrame:
import pandas as pd
import numpy as np
# 示例销售数据
data = {
'Category': ['Electronics', 'Clothing', 'Electronics', 'Groceries', 'Clothing', 'Groceries', 'Electronics', 'Clothing'],
'Region': ['North', 'South', 'North', 'West', 'North', 'South', 'West', 'South'],
'Sales': [1200, 850, 1500, 300, 700, 450, 1800, 950],
'Quantity': [10, 25, 8, 50, 15, 60, 12, 30]
}
df = pd.DataFrame(data)
print(df)
# 输出:
# Category Region Sales Quantity
# 0 Electronics North 1200 10
# 1 Clothing South 850 25
# 2 Electronics North 1500 8
# 3 Groceries West 300 50
# 4 Clothing North 700 15
# 5 Groceries South 450 60
# 6 Electronics West 1800 12
# 7 Clothing South 950 30
要根据Category对这些数据进行分组,您可以使用:
grouped_by_category = df.groupby('Category')
print(grouped_by_category)
# 输出: <pandas.core.groupby.generic.DataFrameGroupBy object at 0x...>
此grouped_by_category对象现在已准备好进行“应用”步骤。您可以查看它已识别的组:
# 查看属于每个组的索引
print(grouped_by_category.groups)
# 输出:
# {'Clothing': [1, 4, 7], 'Electronics': [0, 2, 6], 'Groceries': [3, 5]}
# 获取一个特定组作为DataFrame
print(grouped_by_category.get_group('Electronics'))
# 输出:
# Category Region Sales Quantity
# 0 Electronics North 1200 10
# 2 Electronics North 1500 8
# 6 Electronics West 1800 12
聚合涉及为每个组计算汇总统计量(如总和、平均值、计数)。一旦您有了GroupBy对象,就可以直接应用聚合函数。
要计算每个类别的总销售额:
total_sales_per_category = grouped_by_category['Sales'].sum()
print(total_sales_per_category)
# 输出:
# Category
# Clothing 2500
# Electronics 4500
# Groceries 750
# Name: Sales, dtype: int64
这里,我们首先从GroupBy对象中选择Sales列,然后应用sum()聚合。Pandas会自动将结果与组名称对齐 (alignment)。
您可以应用各种内置聚合函数:
count(): 每个组中非空条目的数量。size(): 每个组中条目的总数(包括空值)。返回一个Series。sum(): 值的总和。mean(): 值的平均值。median(): 值的中位数。min(): 最小值。max(): 最大值。std(): 标准差。var(): 方差。first(): 第一个值。last(): 最后一个值。如果您在不先选择列的情况下,直接将聚合函数应用于GroupBy对象,Pandas将尝试将其应用于所有数值列:
# 计算每个类别的平均销售额和数量
mean_values_per_category = grouped_by_category.mean(numeric_only=True)
print(mean_values_per_category)
# 输出:
# Sales Quantity
# Category
# Clothing 833.333333 23.333333
# Electronics 1500.000000 10.000000
# Groceries 375.000000 55.000000
请注意,使用numeric_only=True是为了避免在“Region”等非数值列上出现警告或错误。
通常,您需要为每个组计算多个汇总统计量。.agg()方法提供了一种灵活的方式来完成此任务。
要计算每个类别的销售额总和、平均值和计数:
sales_summary = grouped_by_category['Sales'].agg(['sum', 'mean', 'count'])
print(sales_summary)
# 输出:
# sum mean count
# Category
# Clothing 2500 833.333333 3
# Electronics 4500 1500.000000 3
# Groceries 750 375.000000 2
您还可以通过向.agg()传递一个字典,将不同的聚合函数应用于不同的列。字典的键是列名,值是要应用于这些列的聚合函数(或函数列表)。
# 计算每个类别的总销售额和平均数量
custom_summary = grouped_by_category.agg({
'Sales': 'sum',
'Quantity': 'mean'
})
print(custom_summary)
# 输出:
# Sales Quantity
# Category
# Clothing 2500 23.333333
# Electronics 4500 10.000000
# Groceries 750 55.000000
您甚至可以在.agg()调用中重命名生成的聚合列:
# 计算总销售额和平均数量,并重命名列
named_summary = grouped_by_category.agg(
TotalSales=('Sales', 'sum'),
AverageQuantity=('Quantity', 'mean')
)
print(named_summary)
# 输出:
# TotalSales AverageQuantity
# Category
# Clothing 2500 23.333333
# Electronics 4500 10.000000
# Groceries 750 55.000000
您可以向.groupby()传递一个列名列表,以便按多于一列进行分组。这会在结果中创建一个层级索引(MultiIndex)。
# 按类别和区域分组
grouped_multi = df.groupby(['Category', 'Region'])
# 计算每个类别-区域组合的平均销售额
mean_sales_multi = grouped_multi['Sales'].mean()
print(mean_sales_multi)
# 输出:
# Category Region
# Clothing North 700.0
# South 900.0
# Electronics North 1350.0
# West 1800.0
# Groceries South 450.0
# West 300.0
# Name: Sales, dtype: float64
为了更方便地使用结果,您可以重置索引:
mean_sales_multi_flat = grouped_multi['Sales'].mean().reset_index()
print(mean_sales_multi_flat)
# 输出:
# Category Region Sales
# 0 Clothing North 700.0
# 1 Clothing South 900.0
# 2 Electronics North 1350.0
# 3 Electronics West 1800.0
# 4 Groceries South 450.0
# 5 Groceries West 300.0
apply应用自定义函数虽然聚合操作汇总组数据,但有时您需要对每个组执行更复杂、自定义的操作。GroupBy对象上的.apply()方法接受一个函数,并将其应用于每个组(作为DataFrame传入)。结果随后被组合。
例如,我们来定义一个函数,计算每个类别内销售额的范围(最大值 - 最小值):
def sales_range(group):
return group['Sales'].max() - group['Sales'].min()
range_per_category = grouped_by_category.apply(sales_range, include_groups=False)
print(range_per_category)
# 输出:
# Category
# Clothing 250
# Electronics 600
# Groceries 150
# dtype: int64
注意:include_groups=False参数 (parameter)阻止Pandas尝试将分组键再次传递给函数,这有时会根据您的函数逻辑导致问题。由于Pandas 0.23版本之后默认行为正在改变,因此明确指定是一个良好的实践。
apply方法非常灵活,但可能比内置聚合或转换慢,因为它通常涉及Python级别的循环,而非优化的C实现。
Pandas提供了专用方法:
.transform(): 按组应用函数,但返回一个与原始输入具有相同索引的对象(Series或DataFrame)。这对于在组内标准化数据等操作很有用(例如,计算相对于组平均值和标准差的z分数)。.filter(): 允许您根据组级别的计算舍弃整个组。例如,您可以只保留平均销售额超过特定阈值的类别。我们在这里不会详细介绍这些,但知道它们的存在对更高级的数据操作任务很有用。
分组和聚合是理解和汇总结构化数据的基本操作。它们使您能够从分类数据中获取洞察,比较组间的特征,并为机器学习 (machine learning)模型准备特征(例如,创建基于用户ID或时间窗口的聚合特征)。熟练运用groupby显著提升了您使用Pandas高效操作和分析数据的能力。
这部分内容有帮助吗?
© 2026 ApX Machine LearningAI伦理与透明度•