0

0

Pandas中按组排序并根据组聚合值对组进行排序的技巧

聖光之護

聖光之護

发布时间:2025-07-23 14:10:02

|

762人浏览过

|

来源于php中文网

原创

Pandas中按组排序并根据组聚合值对组进行排序的技巧

本文探讨了在Pandas中如何实现一种特殊的排序需求:首先根据某个列(如col1)进行分组,然后在每个组内根据另一列(如col2)进行排序,最后再根据每个组的某个聚合值(如col2的最小值)来对这些组进行整体排序。文章介绍了使用numpy.argsort结合groupby().transform()和iloc的规范方法,以及利用sort_values的key参数的替代方案,旨在提供高效且易于理解的解决方案。

问题背景与挑战

在数据处理中,我们经常需要对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进行索引重排。

  1. 计算组的排序依据值: 使用df.groupby('col1')['col2'].transform('min'),这将为DataFrame中的每一行计算其所属col1组的col2最小值,并将这个最小值广播回原始DataFrame的形状。
  2. 获取排序索引: 对上一步得到的结果应用np.argsort()。argsort返回的是将数组排序所需的索引。这些索引将决定最终DataFrame行的顺序。
  3. 应用索引: 使用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的思路需要更精细地应用。

PathFinder
PathFinder

AI驱动的销售漏斗分析工具

下载

更符合原始问答中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中实现“按组聚合值排序组,同时组内按特定列排序”的复杂需求时,有以下几种主要策略:

  1. 多级 sort_values 结合临时列 (不推荐但易理解):

    • 计算组的聚合值并作为新列加入DataFrame。
    • 使用sort_values按新列、组列、组内排序列的顺序进行多级排序。
    • 最后删除临时列。
    • 优点:直观易懂。缺点:引入临时列,占用内存,不够优雅。
  2. numpy.lexsort 结合 groupby().transform() (推荐):

    • 使用groupby().transform()计算每个行所属组的排序依据值。
    • 利用np.lexsort生成一个复合排序的索引数组,其中包含组间排序和组内排序的逻辑。
    • 使用df.iloc[]根据生成的索引数组重排DataFrame。
    • 优点:高效、内存效率高,不创建临时列,可用于管道操作,更为规范和健壮。
  3. sort_values 的 key 参数 (简洁但有局限):

    • 在sort_values()中,使用key参数传入一个lambda函数,该函数内部利用groupby().transform()生成排序键。
    • 优点:代码简洁。缺点:key函数内部依赖外部DataFrame的列,可能在复杂管道中出现问题,不够通用。

综合来看,numpy.lexsort结合groupby().transform()是处理此类复杂排序问题的最规范和推荐的方法,它在性能、内存使用和代码清晰度之间取得了很好的平衡。

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

WorkBuddy
WorkBuddy

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

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
Python 时间序列分析与预测
Python 时间序列分析与预测

本专题专注讲解 Python 在时间序列数据处理与预测建模中的实战技巧,涵盖时间索引处理、周期性与趋势分解、平稳性检测、ARIMA/SARIMA 模型构建、预测误差评估,以及基于实际业务场景的时间序列项目实操,帮助学习者掌握从数据预处理到模型预测的完整时序分析能力。

80

2025.12.04

Python 数据清洗与预处理实战
Python 数据清洗与预处理实战

本专题系统讲解 Python 在数据清洗与预处理中的核心技术,包括使用 Pandas 进行缺失值处理、异常值检测、数据格式化、特征工程与数据转换,结合 NumPy 高效处理大规模数据。通过实战案例,帮助学习者掌握 如何处理混乱、不完整数据,为后续数据分析与机器学习模型训练打下坚实基础。

32

2026.01.31

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

215

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

192

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

61

2026.01.05

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

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

1

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

39

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

140

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

47

2026.03.10

热门下载

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

精品课程

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

共58课时 | 6万人学习

ASP 教程
ASP 教程

共34课时 | 5.8万人学习

Vue3.x 工具篇--十天技能课堂
Vue3.x 工具篇--十天技能课堂

共26课时 | 1.6万人学习

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

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