在数据分析与数据处理场景中,按类别汇总是一类非常常见的需求。例如,在销售数据里按地区统计平均销售额,在用户日志里按渠道统计记录条数,或者在运营报表中按产品类型计算累计收入。Python的pandas库提供的groupby()方法将“先分组、再聚合”这两个动作整合到一起,使这类批量统计工作变得简洁、直观。理解groupby()背后的执行逻辑,比单纯记忆几个函数参数更有助于应对灵活多变的统计任务。

一、groupby的基本工作原理
groupby()操作在pandas内部遵循经典的“拆分、应用、合并”模型。拆分阶段,pandas会根据传入的分组键,例如某一列的值,把原始DataFrame拆成多个子集;应用阶段,pandas逐个对每个子集执行聚合函数;合并阶段,则把所有子集的计算结果重新组装为一个结构化的输出对象。相比手动写循环判断每行数据属于哪个组,这种向量化机制可以显著提升执行效率,也让代码更加简洁。
调用df.groupby('region')并不会立即产生聚合结果,而是返回一个GroupBy对象。这个对象保存了分组信息,但真正的计算延迟到调用mean()、sum()、count()或agg()等方法时才执行。这种惰性计算带来的好处是:先完成分组,再根据需求灵活选择聚合指标,而不必反复扫描原始数据。
下面代码首先构建一份包含地区、销售额和订单量的数据集,然后演示分组对象与真实聚合结果的区别。
import pandas as pd
data = {
'region': ['华北', '华北', '华南', '华南', '华南'],
'sales': [200, 150, 300, 250, 400],
'orders': [2, 1, 3, 2, 4]
}
df = pd.DataFrame(data)
# 仅完成分组,返回GroupBy对象,尚未执行计算
grouped = df.groupby('region')
print(type(grouped))
# 对每个分组求平均,触发真正的聚合
result = grouped.mean()
print(result)
二、用mean按组计算平均值
mean()是最常用的聚合函数之一,主要用于观察每个分组在数值列上的平均水平。比如要比较不同地区的平均销售额,只需对分组对象调用mean()即可。如果不在分组后指定列,pandas默认会对所有数值列分别计算平均值;如果只关心某个特定指标,可以先通过['sales']选择列,再执行聚合,这样能够减少不必要的计算量。
需要留意的是,mean()在计算时会自动忽略缺失值NaN。如果某一组中的目标列全部为NaN,该组的结果仍会是NaN,而不会直接报错。另一个容易忽略的问题是分组键本身如果包含NaN,pandas通常会把缺失的键单独归为一组,这可能在数据清洗不充分时产生额外分组。因此,在正式聚合前最好先对关键列做dropna()处理。
# 仅计算各地区销售额的平均值
avg_sales = df.groupby('region')['sales'].mean()
print(avg_sales)
# 对所有数值列同时求平均
avg_all = df.groupby('region').mean()
print(avg_all)
三、用sum按组求和
sum()用于统计累计量,例如各地区的销售总额、订单总数等。与mean()关注平均水平不同,sum()把组内数值直接相加,默认情况下skipna=True,即缺失值会被忽略,不会影响求和结果。当分析目标是总体规模而非单值水平时,sum()是更合适的选择。
使用sum()时要注意列的数据类型。如果误对字符串列直接求和,pandas可能会把组内字符串进行拼接,而不是抛错,得到的结果往往难以解释。因此,聚合前应当确认所选列是否为数值类型,或者使用agg()显式指定每个列要使用的聚合函数,避免隐式行为带来的困惑。
# 计算各地区销售额与订单量的总和
total = df.groupby('region').sum()
print(total)
# 仅对销售额列求和
total_sales = df.groupby('region')['sales'].sum()
print(total_sales)
四、用count与size统计组内记录数
count()和size()看起来相似,但统计口径不同。count()返回每个分组中非缺失值的数量,如果某一列存在NaN,这些位置不会被计数;而size()返回每个分组的总行数,即使字段值为NaN也会被纳入统计。因此,在订单量字段存在缺失值时,用count()统计记录条数会少于真实行数,这时应当使用size()。
从执行效率来看,size()通常略快于count(),因为它不需要逐列检查缺失情况。在只关心分组规模、不关心具体字段缺失状态的场景下,优先使用size()可以获得更清晰的语义和更快的速度。下面示例同时展示两者的差异,以及如何用agg()在一个表达式中组合多个统计量。
# 统计每个地区的非缺失值数量
cnt = df.groupby('region').count()
print(cnt)
# 统计每个地区的总行数
sz = df.groupby('region').size()
print(sz)
# 使用agg组合多个聚合结果
summary = df.groupby('region').agg(
avg_sales=('sales', 'mean'),
total_sales=('sales', 'sum'),
record_num=('sales', lambda x: x.size)
)
print(summary)
五、聚合结果的索引与后续处理
默认情况下,groupby()的分组键会成为结果DataFrame的行索引。这种结构便于通过loc按组标签取值,但在把多个聚合结果与其他表拼接时,往往需要先执行
Pythongroupbypandas_aggregate修改时间:2026-08-05 11:33:34