一文精通Python Pandas数据分组排序:从入门到实战案例
目录导读
- 引言:为什么你需要掌握Pandas分组排序?
- 第一部分:Pandas数据分组核心函数groupby详解
- 第二部分:排序操作:从单列到多级排序
- 第三部分:经典案例实战:销售数据分组排名
- 第四部分:高频问答区(FAQ)
- 第五部分:性能优化与最佳实践
- 掌握分组排序的思维模型
引言:为什么你需要掌握Pandas分组排序?
在日常数据分析工作中,你大概率遇到过这样的场景:需要统计每个部门的销售额排名,或者计算每个用户在平台上的消费次数排序,如果没有掌握分组加排序的复合操作,你可能需要写多行循环代码,效率极低且容易出错。

分组排序(GroupBy + Sort)是Pandas库中最实用的复合技能之一,它允许你:
- 按某一列(如“城市”)分组
- 在每个组内,按另一列(如“销售额”)进行排序
- 甚至可以为每个组的数据打上“组内排名”
本文将从一个完整的Python案例出发,结合搜索引擎中常见的痛点问题(如“为什么我排序后组内顺序还是乱的?”“如何保留Top 3数据?”),带你彻底掌握这一技能。
问答:Pandas的分组排序与Excel的排序有什么本质区别?
- Excel:需要先排序整个表,然后手动按分组筛选,操作繁琐且无法自动化。
- Pandas:通过
groupby()与sort_values()组合,可将分组逻辑与排序逻辑完全编程化,支持大数据量(百万行级)实时处理。
第一部分:Pandas数据分组核心函数groupby详解
1 基础结构
Pandas的groupby操作遵循“分割-应用-合并”(Split-Apply-Combine)三阶段:
- 分割:根据指定的列(或列组合),将DataFrame按值拆分为多个子集。
- 应用:对每个子集独立执行某种操作(如求和、求均值、排序等)。
- 合并:将处理后的结果合并为新的DataFrame。
2 典型分组场景
import pandas as pd
# 创建示例数据
df = pd.DataFrame({
'部门': ['销售A', '销售B', '销售A', '销售B', '销售A', '销售B'],
'姓名': ['张三', '李四', '王五', '赵六', '陈七', '周八'],
'销售额': [50000, 60000, 48000, 55000, 62000, 59000]
})
代码演示:
# 按部门分组并计算总销售额
grouped_total = df.groupby('部门')['销售额'].sum().reset_index()
print(grouped_total)
输出:
部门 销售额
0 销售A 160000
1 销售B 174000
问答:groupby之后的结果为什么有时候是Series,有时候是DataFrame?
- 当
groupby后只选一列(如['销售额'])且聚合函数返回标量值时,返回的是Series;若要保留列名结构,需用reset_index()或传入列名为列表形式(如[['销售额']])。
第二部分:排序操作:从单列到多级排序
1 单列排序:sort_values()
语法:df.sort_values(by='列名', ascending=True)
ascending=True表示升序(默认),False表示降序。
df_sorted = df.sort_values(by='销售额', ascending=False) print(df_sorted)
输出:
部门 姓名 销售额
4 销售A 陈七 62000
1 销售B 李四 60000
5 销售B 周八 59000
3 销售B 赵六 55000
0 销售A 张三 50000
2 销售A 王五 48000
2 多级排序:多列组合排序
当一列有重复值时,可通过多列控制排序优先级:
df_multi_sort = df.sort_values(by=['部门', '销售额'], ascending=[True, False]) print(df_multi_sort)
输出:
部门 姓名 销售额
4 销售A 陈七 62000
0 销售A 张三 50000
2 销售A 王五 48000
1 销售B 李四 60000
5 销售B 周八 59000
3 销售B 赵六 55000
问答:sort_values和sorted函数有什么区别?
sorted()是Python内置函数,只能处理列表、元组等基础容器;sort_values()是Pandas DataFrame的方法,支持缺失值处理、多级排序、inplace原址修改等高级功能。
第三部分:经典案例实战:销售数据分组排名
1 场景描述
某电商公司有月度订单数据,需要:
- 按“区域”分组
- 在每个区域内按“销售额”降序排序
- 为每个区域内的数据添加“排名”列(排名从1开始)
- 最后只保留每个区域排名前3的记录
2 数据准备
# 生成模拟数据
import numpy as np
np.random.seed(42)
sales_data = pd.DataFrame({
'区域': np.random.choice(['华东', '华南', '华北', '西南'], size=20),
'产品': ['产品A', '产品B', '产品C', '产品D', '产品E'] * 4,
'销售额': np.random.randint(5000, 20000, size=20),
'月份': np.random.choice(['1月', '2月', '3月'], size=20)
})
3 逐步实现分组排序
核心代码:
# 第一步:按区域分组并对每组内部按销售额降序排序
grouped_sorted = sales_data.sort_values(
by=['区域', '销售额'],
ascending=[True, False]
).groupby('区域').apply(lambda x: x.reset_index(drop=True))
# 第二步:在每个组内添加排名列
grouped_sorted['排名'] = grouped_sorted.groupby('区域').cumcount() + 1
# 第三步:筛选每个区域排名前三的记录
top3_per_region = grouped_sorted[grouped_sorted['排名'] <= 3]
查看结果:
print(top3_per_region)
输出示例(因随机数据,每次结果不同):
区域 产品 销售额 月份 排名
区域
华东 0 华东 产品A 18000 3月 1
1 华东 产品B 15000 1月 2
2 华东 产品D 12000 2月 3
华南 0 华南 产品C 19000 1月 1
1 华南 产品E 16500 2月 2
2 华南 产品A 14000 3月 3
...
4 更简洁的替代方案:使用rank()函数
如果不需要手动排序,可直接用groupby + rank()生成排名:
sales_data['全局排名'] = sales_data.groupby('区域')['销售额'].rank(
method='dense', ascending=False
)
top3_rank = sales_data[sales_data['全局排名'] <= 3]
问答:
rank()函数的method='dense'和'min'有何不同?
method='min':相同值并列排名时,取最小排名(如两个第2名,则下一个是第4名);dense:并列时排名连续(如两个第2名,下一个仍第3名),业务排行常用dense。
第四部分:高频问答区(FAQ)
Q1:分组排序后,为什么我的索引还是乱序?
原因:groupby操作默认不会重置索引。
解决方法:在apply内部使用reset_index(drop=True),确保每个子组的索引从0重新开始。
Q2:数据有几百万行,分组排序太慢怎么办?
优化策略:
- 使用
sort_values的kind='mergesort'参数(稳定排序,对大数据友好) - 先对分组列建立索引:
df.set_index('分组列', inplace=True),再使用groupby+sort_index组合 - 考虑使用
dask.dataframe或modin进行分布式加速
Q3:如何对分组后的数据进行多列排名(如按销售额降序,再按日期升序)?
# 自定义排名逻辑
def custom_rank(group):
return group.sort_values(
by=['销售额', '日期'],
ascending=[False, True]
).assign(自定义排名=range(1, len(group)+1))
result = df.groupby('区域', group_keys=False).apply(custom_rank)
Q4:分组排序后,如何获取每个组的最大值/最小值记录?
# 获取每个区域销售额最高的记录
max_by_group = df.loc[df.groupby('区域')['销售额'].idxmax()]
Q5:非数值列能参与排序吗?
可以,字符串列按字母顺序排序,日期列按时间顺序排序,若需自定义顺序(如固定“大-中-小”),需先转换为Categorical类型。
第五部分:性能优化与最佳实践
1 避免在循环中使用groupby
错误示例:
for region in df['区域'].unique():
subset = df[df['区域'] == region]
subset.sort_values(...)
推荐写法:一次性对整个DataFrame操作,利用groupby的向量化特性。
2 使用pipe()链式调用提升可读性
def add_ranking(df):
return df.assign(
排名=df.groupby('区域')['销售额'].rank(method='dense', ascending=False)
)
def filter_top3(df):
return df[df['排名'] <= 3]
result = (
sales_data
.pipe(add_ranking)
.pipe(filter_top3)
.sort_values(['区域', '排名'])
)
3 内存优化技巧
当使用groupby().apply()时,若apply函数返回整个DataFrame,Pandas会创建大量中间对象,建议:
- 改用
transform(返回与原DataFrame相同形状的结果)避免数据膨胀 - 或使用
groupby().agg()聚合到较小的结果
掌握分组排序的思维模型
通过本文的案例,你应该已经理解:分组排序的本质是“先聚拢同类,再内部排序”,这个思维不仅适用于Pandas,也适用于SQL的PARTITION BY ORDER BY、R语言的dplyr::group_by + arrange等工具。
核心口诀:
- 先
groupby划分“领地” - 再
sort_values或rank明确“内部秩序” - 用
.apply或.transform执行自定义逻辑 - 最后用筛选器(如
[df['排名']<=3])提取目标数据
最后送你一个检测理解的小测验:
假设你有用户行为日志(user_id, event_time, event_type),如何找到每个用户最新的3条行为记录?
(提示:按user_id分组,然后按event_time降序排序,再取组内前3行)
如果你能用Pandas写出这个逻辑,恭喜你,已经彻底掌握了分组排序的精髓!