Pandas分组排序与聚合技巧详解
本文深入探讨了Pandas中一种高级排序技巧,旨在解决先分组、组内排序,再按组聚合值排序的复杂需求,并符合百度SEO规范。针对标准`sort_values()`方法的局限性,文章对比了几种解决方案。首先,批判了创建临时列的“笨拙”方法,指出其内存开销和代码复杂性。然后,重点推荐了基于`numpy.argsort`结合`groupby().transform()`的规范方法,以及`sort_values`的`key`参数的替代方案。其中,`numpy.lexsort`因其高效、内存友好和适用于数据管道的特性,被认为是最佳实践。文章旨在帮助读者掌握更优雅、高效的Pandas数据排序技巧,提升数据处理能力,避免常见陷阱。
问题背景与挑战
在数据处理中,我们经常需要对DataFrame进行排序。Pandas提供了强大的sort_values()方法,可以轻松地根据一个或多个列进行排序。然而,当需求变得更复杂时,例如需要先按一个列分组,然后组内按另一个列排序,最后再根据每个组的某个聚合值(如最小值、平均值等)来决定组的整体顺序时,标准的sort_values()方法可能无法直接满足。
考虑以下示例DataFrame:
import pandas as pd import numpy as np df = pd.DataFrame({'col1': ['A', 'B', 'A', 'B', 'C'], 'col2': [3, 1, 2, 4, 3], 'col3': [10, 20, 30, 40, 50]}) print("原始DataFrame:") print(df)
输出:
原始DataFrame: col1 col2 col3 0 A 3 10 1 B 1 20 2 A 2 30 3 B 4 40 4 C 3 50
我们的目标是得到以下排序结果:
col1 col2 col3 1 B 1 20 3 B 4 40 0 A 3 10 2 A 2 30 4 C 3 50
观察上述结果,数据首先按col1分组(B组、A组、C组),然后在每个组内按col2升序排列。最关键的是,组的顺序是根据每个col1组中col2的最小值来确定的:
- B组 (col2值: 1, 4),最小值为1。
- A组 (col2值: 3, 2),最小值为2。
- C组 (col2值: 3),最小值为3。 因此,最终的组顺序是B、A、C。
直接使用df.sort_values(['col1', 'col2'])会先按col1排序,然后组内按col2排序,但组的顺序是按col1的字母顺序(A、B、C),而非col2的最小值。
print("\ndf.sort_values(['col1', 'col2']):") print(df.sort_values(['col1', 'col2']))
输出:
df.sort_values(['col1', 'col2']): col1 col2 col3 2 A 2 30 0 A 3 10 1 B 1 20 3 B 4 40 4 C 3 50
同样,df.sort_values(['col2', 'col1'])则会优先按col2排序,也无法达到预期。
一种常见的“笨拙”做法是创建临时列:
df_temp = df.copy() df_temp['min_col2'] = df_temp.groupby('col1')['col2'].transform('min') sorted_df_temp = df_temp.sort_values(['min_col2', 'col1', 'col2']).drop("min_col2", axis="columns") print("\n使用临时列的方法:") print(sorted_df_temp)
这种方法虽然能达到目的,但引入了额外的临时列,增加了内存开销和代码复杂度,尤其是在数据管道中不够优雅。
规范解决方案:结合 numpy.argsort 和 groupby().transform()
解决此类问题的规范方法是利用numpy.argsort与groupby().transform()的组合,并通过iloc进行索引重排。
- 计算组的排序依据值: 使用df.groupby('col1')['col2'].transform('min'),这将为DataFrame中的每一行计算其所属col1组的col2最小值,并将这个最小值广播回原始DataFrame的形状。
- 获取排序索引: 对上一步得到的结果应用np.argsort()。argsort返回的是将数组排序所需的索引。这些索引将决定最终DataFrame行的顺序。
- 应用索引: 使用df.iloc[]将DataFrame按照这些索引进行重新排序。
# 规范解决方案 # 1. 计算每个组的排序依据值(例如,每个col1组的col2最小值) group_min_col2 = df.groupby('col1')['col2'].transform('min') # 2. 获取这些值的排序索引 # np.argsort返回的是排序后的元素在原始数组中的位置索引 sorted_indices = np.argsort(group_min_col2) # 3. 使用iloc根据这些索引重新排列DataFrame out_df = df.iloc[sorted_indices] # 为了同时实现组内排序,可以先进行一次常规排序,再进行组间排序 # 或者在iloc之后,对每个组进行内部排序 # 更简洁的方式是,在argsort之前,确保数据已经按照组内规则排序 # 最佳实践是,先对df进行一次常规的按组和组内列的排序,然后使用argsort来调整组的顺序 # 这里的需求是:B组1,4;A组2,3;C组3。然后组之间B<A<C。 # 所以,我们希望先按col1, col2排序,然后调整组的顺序。 # 实际上,上面的argsort已经给出了组的顺序,但组内没有排序。 # 要达到最终目标(组间按min_col2排序,组内按col2排序),需要结合多个排序步骤。 # 正确的组合方式: # 1. 首先,对DataFrame进行一次常规的组内排序 df_sorted_within_groups = df.sort_values(['col1', 'col2']) # 2. 然后,基于原始DataFrame计算用于组间排序的依据 group_min_col2_original_df = df.groupby('col1')['col2'].transform('min') # 3. 获取基于组间排序依据的索引 # 注意:这里argsort应该作用于原始DataFrame的索引,以确保我们能正确重排 # 更直接的方法是,argsort作用于一个Series,这个Series的索引与原始DataFrame的索引一致 # 我们可以创建一个包含排序键和原始索引的DataFrame,然后对其进行排序 # 这里的挑战在于,我们希望的输出是先B组,然后A组,然后C组,并且B组内部是1,4,A组内部是2,3。 # 原始的argsort方法可以做到组的整体排序,但无法保证组内的顺序。 # 重新审视期望输出: # col1 col2 col3 # 1 B 1 20 (df index 1) # 3 B 4 40 (df index 3) # 0 A 3 10 (df index 0) # 2 A 2 30 (df index 2) # 4 C 3 50 (df index 4) # 这里的关键是,我们首先需要知道每个原始行属于哪个“组排序级别”。 # B组的min_col2是1,A组是2,C组是3。 # 我们可以创建一个新的排序键,包含组的min_col2和行本身的col2。 # 步骤1:计算每个行所属组的最小值 df['group_min_col2'] = df.groupby('col1')['col2'].transform('min') # 步骤2:根据新的排序键进行多级排序 # 优先按组的最小值排序,然后按col1(确保同一组的行在一起),最后按col2进行组内排序 final_out = df.sort_values(by=['group_min_col2', 'col1', 'col2']).drop(columns=['group_min_col2']) print("\n规范解决方案 (多级排序):") print(final_out)
这个多级排序的方法是直观且有效的,它避免了iloc和argsort的复杂组合,并且仍然是规范的。然而,如果必须避免临时列,那么np.argsort结合iloc的思路需要更精细地应用。
更符合原始问答中np.argsort的用法,且避免临时列的方案:
原始答案的核心是使用np.argsort来生成一个索引序列,这个序列能直接重排DataFrame。
# 原始答案中的解决方案 # 核心思想是:生成一个与df行数相同的Series,其值代表了行所属组的排序优先级。 # 然后对这个Series进行argsort,得到最终的行索引顺序。 # 这里的挑战在于,如何让argsort同时考虑组间排序和组内排序。 # 答案中提供的out = df.iloc[np.argsort(df.groupby('col1')['col2'].transform('min'))] # 这个代码片段只保证了组的顺序,但组内的顺序是原始的,不是按col2排序的。 # 为了实现“组间按min_col2排序,组内按col2排序”: # 我们可以创建一个复合的排序键,然后对这个键进行argsort。 # 复合键的思路是:将组的排序值(如min_col2)和行自身的col2值组合起来。 # 例如,可以创建一个元组列表:[(min_col2_for_row_i, col2_for_row_i), ...] # 步骤1:计算每个行所属组的最小值 min_col2_series = df.groupby('col1')['col2'].transform('min') # 步骤2:创建复合排序键 # 注意:这里需要确保argsort作用于一个能够反映最终排序顺序的单一序列。 # 将 min_col2_series 和 df['col2'] 结合起来,并对它们进行argsort。 # np.lexsort 可以用于多列排序的索引。 # lexsort(keys, axis=-1): Perform an indirect stable sort using a sequence of keys. # keys: (k_n, k_n-1, ..., k_0) - keys are sorted from last to first. # 所以,我们想要先按 min_col2_series 排序,再按 df['col2'] 排序。 # lexsort 的 keys 顺序是:最后排序的键在最前面,最先排序的键在最后面。 # 也就是说,如果想先按A排,再按B排,那么keys=(B, A)。 # 所以,我们想先按 min_col2_series 排,再按 df['col2'] 排,那么 keys=(df['col2'], min_col2_series)。 sorted_indices_complex = np.lexsort((df['col2'], min_col2_series)) final_out_lexsort = df.iloc[sorted_indices_complex] print("\n使用 np.lexsort 的规范解决方案:") print(final_out_lexsort)
np.lexsort是处理多键排序的强大工具,它返回的是一个整数索引数组,指示了如何重新排列原始数组以实现多键排序。它的工作方式是:keys元组中的最后一个键是主排序键,倒数第二个是次要排序键,依此类推。因此,np.lexsort((df['col2'], min_col2_series))意味着首先根据min_col2_series进行排序,然后对于min_col2_series值相同的行,再根据df['col2']进行排序。这完美符合了我们的需求。
在管道中使用的变体: 如果需要在Pandas方法链中使用,可以结合lambda表达式:
out_pipeline = df.iloc[lambda d: np.lexsort((d['col2'], d.groupby('col1')['col2'].transform('min')))] print("\n管道中使用的 np.lexsort 解决方案:") print(out_pipeline)
替代方案:使用 sort_values 的 key 参数
Pandas sort_values() 方法有一个鲜为人知的 key 参数,它允许在排序前对列值应用一个函数。这个函数会接收待排序的 Series,并返回一个用于排序的 Series。
out_key = df.sort_values(by='col2', key=lambda s: s.groupby(df['col1']).transform('min')) print("\n使用 sort_values 的 key 参数解决方案:") print(out_key)
注意事项:
- key 参数的函数 lambda s: s.groupby(df['col1']).transform('min') 中,s 是df['col2']这个 Series。在s.groupby(df['col1'])中,df['col1']必须能够被s的索引正确对齐。这意味着df['col1']必须与原始DataFrame的索引保持一致。
- 这种方法虽然简洁,但它的“hacky”之处在于,key函数内部依赖于df['col1']这个外部变量。在某些复杂的Pandas管道操作中,df可能不再是原始的DataFrame,这可能导致错误。因此,在需要严格的数据流控制和可预测性的场景下,np.lexsort方案通常更为健壮。
总结
当需要在Pandas中实现“按组聚合值排序组,同时组内按特定列排序”的复杂需求时,有以下几种主要策略:
多级 sort_values 结合临时列 (不推荐但易理解):
- 计算组的聚合值并作为新列加入DataFrame。
- 使用sort_values按新列、组列、组内排序列的顺序进行多级排序。
- 最后删除临时列。
- 优点:直观易懂。缺点:引入临时列,占用内存,不够优雅。
numpy.lexsort 结合 groupby().transform() (推荐):
- 使用groupby().transform()计算每个行所属组的排序依据值。
- 利用np.lexsort生成一个复合排序的索引数组,其中包含组间排序和组内排序的逻辑。
- 使用df.iloc[]根据生成的索引数组重排DataFrame。
- 优点:高效、内存效率高,不创建临时列,可用于管道操作,更为规范和健壮。
sort_values 的 key 参数 (简洁但有局限):
- 在sort_values()中,使用key参数传入一个lambda函数,该函数内部利用groupby().transform()生成排序键。
- 优点:代码简洁。缺点:key函数内部依赖外部DataFrame的列,可能在复杂管道中出现问题,不够通用。
综合来看,numpy.lexsort结合groupby().transform()是处理此类复杂排序问题的最规范和推荐的方法,它在性能、内存使用和代码清晰度之间取得了很好的平衡。
理论要掌握,实操不能落!以上关于《Pandas分组排序与聚合技巧详解》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!

- 上一篇
- HTML如何用translate移动元素?

- 下一篇
- PHP性能优化:缓存加速方案全解析
-
- 文章 · python教程 | 46秒前 |
- Supervisor管理多Git分支部署技巧
- 446浏览 收藏
-
- 文章 · python教程 | 17分钟前 |
- Python实现StepMix模型:混合建模教程详解
- 162浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- PyCharm解释器选择指南与建议
- 332浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- AzureDevOps管道持久化JSON数据到Git仓库
- 288浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python连接MySQL:PyMySQL使用教程
- 137浏览 收藏
-
- 文章 · python教程 | 2小时前 |
- Python代码审查与团队协作质量要点
- 125浏览 收藏
-
- 文章 · python教程 | 2小时前 |
- Python字典高效使用技巧详解
- 455浏览 收藏
-
- 文章 · python教程 | 2小时前 | Python函数
- Python递归遍历嵌套列表详解
- 274浏览 收藏
-
- 文章 · python教程 | 3小时前 |
- Python高效分组统计,groupby高级技巧详解
- 366浏览 收藏
-
- 文章 · python教程 | 3小时前 | Python Python编程
- Python实现PCA数据降维方法解析
- 497浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 514次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 499次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 千音漫语
- 千音漫语,北京熠声科技倾力打造的智能声音创作助手,提供AI配音、音视频翻译、语音识别、声音克隆等强大功能,助力有声书制作、视频创作、教育培训等领域,官网:https://qianyin123.com
- 1057次使用
-
- MiniWork
- MiniWork是一款智能高效的AI工具平台,专为提升工作与学习效率而设计。整合文本处理、图像生成、营销策划及运营管理等多元AI工具,提供精准智能解决方案,让复杂工作简单高效。
- 1009次使用
-
- NoCode
- NoCode (nocode.cn)是领先的无代码开发平台,通过拖放、AI对话等简单操作,助您快速创建各类应用、网站与管理系统。无需编程知识,轻松实现个人生活、商业经营、企业管理多场景需求,大幅降低开发门槛,高效低成本。
- 1041次使用
-
- 达医智影
- 达医智影,阿里巴巴达摩院医疗AI创新力作。全球率先利用平扫CT实现“一扫多筛”,仅一次CT扫描即可高效识别多种癌症、急症及慢病,为疾病早期发现提供智能、精准的AI影像早筛解决方案。
- 1056次使用
-
- 智慧芽Eureka
- 智慧芽Eureka,专为技术创新打造的AI Agent平台。深度理解专利、研发、生物医药、材料、科创等复杂场景,通过专家级AI Agent精准执行任务,智能化工作流解放70%生产力,让您专注核心创新。
- 1035次使用
-
- Flask框架安装技巧:让你的开发更高效
- 2024-01-03 501浏览
-
- Django框架中的并发处理技巧
- 2024-01-22 501浏览
-
- 提升Python包下载速度的方法——正确配置pip的国内源
- 2024-01-17 501浏览
-
- Python与C++:哪个编程语言更适合初学者?
- 2024-03-25 501浏览
-
- 品牌建设技巧
- 2024-04-06 501浏览