0

0

python中怎么用pandas进行分组聚合(groupby)?

穿越時空

穿越時空

发布时间:2025-09-15 22:17:01

|

879人浏览过

|

来源于php中文网

原创

Pandas的groupby通过“分、用、合”实现数据聚合,支持多列分组与复杂聚合,结合filter、sort_values和reset_index可高效处理结果,并可通过优化数据类型、使用Dask等提升大数据性能。

python中怎么用pandas进行分组聚合(groupby)?

Pandas中的

groupby
操作,简单来说,就是将你的数据集根据一个或多个键(列)进行拆分,然后对每个独立的组应用一个函数(比如求和、平均值),最后将这些结果组合起来。它是我在数据分析工作中,处理聚合统计、探索数据模式时,几乎离不开的核心工具。理解并掌握它,能让你对数据有更深层次的洞察。

解决方案

groupby
的核心思想是“分、用、合”(Split-Apply-Combine)。当你需要对数据集中的某个类别或分组进行统计分析时,
groupby
就是你的首选。

首先,你需要一个DataFrame。我们创建一个简单的例子:

import pandas as pd
import numpy as np

data = {
    '城市': ['北京', '上海', '北京', '广州', '上海', '北京'],
    '商品': ['A', 'B', 'A', 'C', 'B', 'C'],
    '销售额': [100, 150, 200, 50, 120, 80],
    '利润': [20, 30, 40, 10, 25, 15]
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)

1. 基本分组聚合

立即学习Python免费学习笔记(深入)”;

如果你想知道每个城市的总销售额,你可以这样做:

# 按城市分组,计算销售额总和
city_sales = df.groupby('城市')['销售额'].sum()
print("\n按城市分组的总销售额:")
print(city_sales)

这里,

df.groupby('城市')
完成了“分”的步骤,它根据“城市”列的值将DataFrame拆分成了“北京”、“上海”、“广州”三个组。
['销售额'].sum()
则是在每个组上“应用”了求和操作,最后Pandas自动将这些结果“组合”成了一个新的Series。

你也可以对多个列进行聚合,或者使用不同的聚合函数

# 按城市分组,计算销售额的平均值和利润的最大值
city_stats = df.groupby('城市').agg({
    '销售额': 'mean',
    '利润': 'max'
})
print("\n按城市分组的平均销售额和最大利润:")
print(city_stats)

常用的聚合函数包括:

sum()
(求和),
mean()
(平均值),
median()
(中位数),
min()
(最小值),
max()
(最大值),
count()
(非NaN值的数量),
size()
(组的大小,包含NaN),
std()
(标准差),
var()
(方差)等。你也可以直接传入字符串形式的函数名。

2. 多列分组

如果你想更细致地分析,比如想知道每个城市里,每种商品的销售额总和,那就需要多列分组:

# 按城市和商品分组,计算销售额总和
city_product_sales = df.groupby(['城市', '商品'])['销售额'].sum()
print("\n按城市和商品分组的总销售额:")
print(city_product_sales)

这样会生成一个MultiIndex的Series,非常适合进行多维度的分析。

Pandas Groupby如何实现多列分组与复杂聚合操作?

在实际的数据分析场景中,我们很少只对一个列进行简单的聚合。多列分组是常态,而复杂聚合则意味着我们可能需要对不同的列应用不同的聚合逻辑,甚至自定义聚合函数。

当我们用

df.groupby(['列1', '列2'])
进行多列分组时,Pandas会根据这些列的唯一组合来创建独立的组。这就像是先按“城市”分,再在每个城市内部按“商品”分,形成一个层级结构。结果通常是一个MultiIndex(多级索引)的Series或DataFrame,这在视觉上可能需要一点时间来适应,但它的信息量非常大。

对于复杂聚合,

agg()
方法提供了极大的灵活性。你可以传入一个字典,其中键是你要聚合的列名,值可以是单个聚合函数字符串、函数对象,或者一个包含多个函数字符串/函数对象的列表。

# 示例:对不同列应用不同聚合,并使用多个聚合函数
complex_agg = df.groupby('城市').agg(
    总销售额=('销售额', 'sum'), # 命名聚合,结果列名为“总销售额”
    平均利润=('利润', 'mean'), # 结果列名为“平均利润”
    商品种类=('商品', lambda x: x.nunique()) # 使用lambda表达式自定义聚合:计算商品种类数
)
print("\n复杂聚合操作:")
print(complex_agg)

这里我用了Python的

lambda
表达式来定义一个匿名函数
lambda x: x.nunique()
,它计算了每个组中“商品”列的唯一值数量,这比写一个完整的函数更简洁。这种自定义聚合的能力,让
groupby
变得异常强大。

有时,你可能需要对整个组应用一个更复杂的逻辑,而不仅仅是简单的聚合函数。这时,

apply()
方法就派上用场了。
apply()
会将整个DataFrame的子集(每个组)传递给你的函数。

# 示例:使用apply()查找每个城市销售额最高的商品
def top_product(group):
    return group.loc[group['销售额'].idxmax()]

top_selling_per_city = df.groupby('城市').apply(top_product)
print("\n每个城市销售额最高的商品信息:")
print(top_selling_per_city)

apply()
agg()
更灵活,但通常也更慢,因为它无法像
agg()
那样利用Pandas底层的优化。所以,在能用
agg()
解决问题时,优先选择
agg()
。只有当你的逻辑真的需要访问整个组的结构时,才考虑
apply()

处理Groupby结果时,如何高效地筛选、排序与重置索引?

当我们完成分组聚合后,得到的结果往往需要进一步的整理和分析。筛选、排序和重置索引是处理

groupby
结果时最常见的后续操作。

1. 筛选分组后的数据 (

filter()
)

有时候,我们只对满足特定条件的分组结果感兴趣。比如,我们只想看到总销售额超过某个阈值的城市。这时候,

filter()
方法就非常有用。它允许你根据每个组的聚合结果来决定是否保留这个组。

# 筛选出总销售额大于250的城市
filtered_cities = df.groupby('城市').filter(lambda x: x['销售额'].sum() > 250)
print("\n筛选出总销售额大于250的原始数据行:")
print(filtered_cities)

需要注意的是,

filter()
返回的是原始DataFrame中满足条件的行,而不是聚合后的结果。如果你想筛选聚合后的结果,你需要先进行聚合,然后对聚合后的DataFrame进行筛选。

# 先聚合,再筛选聚合结果
agg_result = df.groupby('城市')['销售额'].sum()
high_sales_cities_agg = agg_result[agg_result > 250]
print("\n筛选出总销售额大于250的城市及其总销售额(聚合结果):")
print(high_sales_cities_agg)

2. 排序分组结果 (

sort_values()
)

聚合后的数据通常是按分组键的顺序排列的,但我们可能需要根据聚合值进行排序,以便快速识别最大值或最小值。

吐槽大师
吐槽大师

吐槽大师(Roast Master) - 终极 AI 吐槽生成器,适用于 Instagram,Facebook,Twitter,Threads 和 Linkedin

下载
# 按城市分组并计算总销售额,然后按销售额降序排列
sorted_city_sales = df.groupby('城市')['销售额'].sum().sort_values(ascending=False)
print("\n按总销售额降序排列的城市:")
print(sorted_city_sales)

如果你的聚合结果是一个DataFrame(比如你使用了多个聚合函数),你可以指定按哪个列进行排序:

# 按城市分组,计算销售额和利润的平均值,然后按平均销售额降序排列
avg_stats = df.groupby('城市').agg({'销售额': 'mean', '利润': 'mean'})
sorted_avg_stats = avg_stats.sort_values(by='销售额', ascending=False)
print("\n按平均销售额降序排列的城市统计:")
print(sorted_avg_stats)

3. 重置索引 (

reset_index()
)

groupby
操作默认会把分组键变成结果DataFrame的索引(或MultiIndex)。虽然这在很多情况下很方便,但有时我们更希望这些分组键作为普通的列存在,方便后续的筛选、合并或其他操作。
reset_index()
就是用来解决这个问题的。

# 按城市和商品分组计算总销售额,并将分组键转为普通列
reset_index_example = df.groupby(['城市', '商品'])['销售额'].sum().reset_index()
print("\n重置索引后的分组聚合结果:")
print(reset_index_example)

你也可以在

groupby
时就避免生成索引,通过设置
as_index=False

# 在groupby时就避免生成索引
no_index_groupby = df.groupby(['城市', '商品'], as_index=False)['销售额'].sum()
print("\ngroupby时设置as_index=False的结果:")
print(no_index_groupby)

在我看来,

reset_index()
是一个非常实用的操作,尤其是在需要将聚合结果与其他DataFrame进行合并时,或者当你希望将分组键作为普通列进行进一步的筛选或可视化时。它能让你的数据结构更扁平,更易于处理。

Groupby在大型数据集上可能面临的性能瓶颈与优化策略有哪些?

虽然

groupby
功能强大,但在处理非常大的数据集时,我确实遇到过性能瓶颈。了解这些瓶颈并掌握一些优化策略,对于高效地进行数据分析至关重要。

1. 内存消耗

groupby
在内部会创建每个组的视图或副本,尤其是在使用
apply()
时,如果组的数量非常多,或者每个组的数据量很大,这可能会导致大量的内存消耗。我的经验是,当数据集大到一定程度,内存溢出就成了家常便饭。

优化策略:

  • 选择合适的聚合方法: 优先使用内置的聚合函数(如

    sum
    ,
    mean
    ,
    count
    ),它们通常是在C语言层面实现的,效率很高。如果非要用自定义函数,尽量用
    agg()
    而不是
    apply()
    ,因为
    agg()
    可以更好地利用Pandas的优化。

  • 提前过滤和选择列: 在进行

    groupby
    之前,只保留你需要的列,并过滤掉不相关的行。减少数据量是提高性能最直接的方法。

  • 使用

    category
    dtype: 如果你的分组键(比如“城市”、“商品”)是字符串类型,并且唯一值的数量相对较少,将其转换为
    category
    dtype可以显著减少内存占用并加速操作。Pandas在处理分类数据时效率更高。

    df['城市'] = df['城市'].astype('category')
    df['商品'] = df['商品'].astype('category')
    # 再次进行groupby操作,可能会更快
  • 分块处理(Chunking): 对于超大型数据集,如果一次性加载会爆内存,可以考虑将数据分块加载,对每个块进行

    groupby
    ,然后将各块的结果合并。这通常需要一些手动编码,但能有效解决内存问题。

2.

apply()
的性能问题

前面提过,

apply()
虽然灵活,但效率通常不如内置聚合函数和
agg()
。因为它会在Python循环中迭代每个组,并且每次迭代都可能涉及Python和C代码之间的上下文切换。

优化策略:

  • 尽量用

    agg()
    transform()
    替代
    apply()

    • agg()
      用于返回每个组的单个聚合值。
    • transform()
      用于返回一个与原始DataFrame形状相同的Series,其中每个值都是其所在组的聚合结果。这在需要将组级统计量“广播”回原始数据时非常有用,比如标准化某个组内的数据。
    # 示例:使用transform()计算每个城市的销售额占城市总销售额的比例
    df['城市销售额占比'] = df.groupby('城市')['销售额'].transform(lambda x: x / x.sum())
    print("\n使用transform()计算城市销售额占比:")
    print(df)

    transform
    的效率通常远高于
    apply
    ,因为它能对组内的所有元素同时执行操作,而不需要显式的Python循环。

3. 数据类型不一致

在某些情况下,如果分组键的数据类型不一致(比如混合了字符串和数字),Pandas可能无法有效地优化操作,导致性能下降。确保分组键的数据类型统一且合适,能避免一些不必要的性能开销。

4. 外部库的利用

对于真正的大数据量(GB级别甚至TB级别),Pandas可能就力不从心了。这时,可以考虑使用专门为大数据设计的库:

  • Dask: Dask提供了与Pandas类似的API,但能够处理超出内存的数据集,并利用多核CPU或分布式集群进行计算。它的
    dask.dataframe
    模块可以无缝替代Pandas DataFrame。
  • Polars: Polars是一个用Rust编写的DataFrame库,以其卓越的性能和内存效率而闻名,尤其是在处理大型数据集时。它的
    groupby
    操作通常比Pandas快得多。

在我实际工作中,面对大型数据集,我往往会先尝试优化Pandas本身的用法(如

category
dtype、
transform
),如果还是不行,Dask通常是我的下一个选择,它能让我用熟悉的Pandas语法处理更大的数据。这是一个迭代优化的过程,没有一劳永逸的方案,关键在于理解数据的规模和操作的特性。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

410

2023.06.20

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

638

2023.07.25

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

362

2023.08.02

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

263

2023.08.09

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

631

2023.09.05

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

564

2023.09.20

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

671

2023.09.20

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

618

2023.09.22

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Pandas 教程
Pandas 教程

共15课时 | 1.2万人学习

最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号