导读:本期聚焦于蚂蚁创作的《Python怎么按组统计?groupby()聚合与mean/sum/count应用详解》,敬请观看详情。面对销售数据按地区算平均销售额、日志按用户数事件次数这类需求,直接用循环写不仅慢还易错。Pandas的groupby机制先在内存里把相同键的行分到同一组,再对每组调用聚合函数。本文以mean、sum、count三个常用函数为线索,说明分组后的对象结构、单列与多列聚合写法,以及空值与重复索引带来的统计偏差。读懂拆分、应用、合并三步,就能把几百行脚本缩成一行表达式,既提升可读性也降低出错概率。

在数据分析与数据处理场景中,按类别汇总是一类非常常见的需求。例如,在销售数据里按地区统计平均销售额,在用户日志里按渠道统计记录条数,或者在运营报表中按产品类型计算累计收入。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按组标签取值,但在把多个聚合结果与其他表拼接时,往往需要先执行 1500) print(filtered) # 只保留组内记录数大于等于2的区域 filtered_by_size = df.groupby('region').filter(lambda x: len(x) >= 2) print(filtered_by_size) 九、避免常见误用与性能注意点 在使用groupby()时,有几个容易混淆的地方值得留意。第一,分组聚合返回的列名可能不直观,尤其在使用agg()并传入多个函数时,建议通过命名元组或自定义聚合函数名让结果列名清晰可控。第二,分组键如果存在NaN,pandas默认会忽略这些NaN所在的行,分组结果中不会出现对应的组。第三,在对大数据集做groupby操作时,应尽量使用内置聚合函数,避免使用Python原生循环或较慢的lambda函数。内置函数是经过优化的,通常比自定义Python函数快很多。 # 对大数据集优先使用内置聚合函数 large_df = pd.DataFrame({ 'key': np.random.randint(0, 100, 1000000), 'value': np.random.randn(1000000) }) # 推荐:使用内置sum result_builtin = large_df.groupby('key')['value'].sum() # 不推荐:使用自定义lambda,速度较慢 result_lambda = large_df.groupby('key')['value'].agg(lambda x: x.sum()) print(result_builtin.head()) print(result_lambda.head()) 十、总结与完整示例 groupby()是pandas中处理分组数据的核心工具,它的灵活性来自split-apply-combine的统一框架。从基本的sum、mean聚合,到count与size的差异,再到多列分组、transform、filter以及性能优化,掌握这些能力之后,可以高效完成大多数分组分析任务。下面给出一个完整的端到端示例,把本文涉及的主要操作串联起来,便于读者对照练习。 import pandas as pd import numpy as np # 构造示例数据 data = { 'region': ['华东', '华北', '华东', '华南', '华北', '华南', '华东', '华北'], 'product_line': ['A', 'B', 'A', 'C', 'B', 'C', 'A', 'B'], 'sales': [1200, 800, 1500, 600, 950, 1100, 1300, 700], 'profit': [120, 80, 200, 50, 90, 130, 180, 60] } df = pd.DataFrame(data) # 按区域分组并计算多个统计量 summary = ( df.groupby('region') .agg( total_sales=('sales', 'sum'), avg_sales=('sales', 'mean'), total_profit=('profit', 'sum'), order_count=('sales', 'size') ) .reset_index() ) # 添加区域销售占比列 summary['sales_ratio'] = summary['total_sales'] / summary['total_sales'].sum() # 添加平均利润率列 summary['avg_profit_rate'] = summary['total_profit'] / summary['total_sales'] # 按总销售额降序排列 summary = summary.sort_values('total_sales', ascending=False) # 只保留总销售额大于2000的区域 final_result = summary[summary['total_sales'] > 2000] print(final_result) 以上完整示例展示了从数据构造、分组聚合、派生指标计算到条件筛选的完整流程。通过将分组结果与普通DataFrame列操作结合,可以在保持代码可读性的同时完成复杂的分析逻辑。groupby()的学习重点不在于记忆所有方法,而在于理解分组后对象的操作模式:聚合将组压缩为一行,transform将组结果广播回每一行,filter则根据组条件保留或删除整组数据。掌握这三种模式后,面对大多数分组任务都能游刃有余地选择合适的工具。

Pythongroupbypandas_aggregate修改时间:2026-08-05 11:33:34

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。