工作中,我们可能会遇到这样的需求:按汇总指标A列排名,指标A列值相同,则按指标B列排名
本文将通过一个小实验介绍如何使用Pandas在多个列上进行排序排名操作
import numpy as np
import pandas as pd
import glob
import os
files = glob.glob(os.path.join(r'C:\Users\cc\Desktop\Test', '*.xlsx'))
# 读取并合并Excel
df_res = pd.DataFrame()
for file in files:
# 读取
df = pd.read_excel(file)
# 合并
df_res = pd.concat([df_res, df], ignore_index=True)
print(df_res.to_string())
'''
姓名 月度积分 月度评分
0 张三 5 9
1 李四 4 9
2 赵六 4 8
3 王五 5 10
4 张三 4 8
5 李四 5 10
6 王五 5 10
7 赵六 4 9
8 张三 4 8
9 李四 4 8
10 赵六 4 9
11 王五 4 9
12 张三 5 7
13 李四 5 9
14 王五 5 9
15 赵六 4 9
'''
实验需求: 将每个人的积分、评分汇总,并按总积分排名,总积分一致时,按总评分排名,最终结果按排名升序
# 计算总积分总评分
df = df_res.\
groupby('姓名', as_index=False).agg({'月度积分': 'sum', '月度评分': 'sum'}).\
rename(columns={'月度积分': '总积分', '月度评分': '总评分'})
# 指定姓名、总积分列去重,保留第一个
df.drop_duplicates(subset=['姓名', '总积分'], keep='first', inplace=True)
print(df.to_string())
'''
姓名 总积分 总评分
0 张三 18 32
1 李四 18 36
2 王五 19 38
3 赵六 16 35
'''
1)方式1
# 按总积分进行排序,若总积分相同则按照总评分排序
df = df.sort_values(by=['总积分', '总评分'], ignore_index=True, ascending=[False, False])
print(df.to_string())
'''
姓名 总积分 总评分
0 王五 19 38
1 李四 18 36
2 张三 18 32
3 赵六 16 35
'''
df['总排名'] = df[['总积分', '总评分']].rank(method='dense', ascending=[False, False])
print(df.to_string())
'''
ValueError: Cannot set a DataFrame with multiple columns to the single column 总排名
'''
上述方式多列排序没有问题,但多列排名报错
2)方式2
# 按总积分排名
df['总排名'] = df['总积分'].rank(method='dense', ascending=False)
print(df.to_string())
'''
姓名 总积分 总评分 总排名
0 张三 18 32 2.0
1 李四 18 36 2.0
2 王五 19 38 1.0
3 赵六 16 35 3.0
'''
# 按总积分进行排序,若总积分相同则按照总评分排序
df.sort_values(by=['总积分', '总评分'], ascending=[False, False], inplace=True)
print(df.to_string())
'''
姓名 总积分 总评分 总排名
2 王五 19 38 1.0
1 李四 18 36 2.0
0 张三 18 32 2.0
3 赵六 16 35 3.0
'''
上述方式不会报错,但最终结果按“1 2 2 3 ...”
(dense_rank()
)方式进行了排名,没有达到按总积分排名,总积分一致时,按总评分排名
的要求
相当于实现了按总积分进行排序,若总积分相同则按照总评分排序的需求
3)方式3
# 使用辅助列
df['辅助列'] = df.eval('总积分*100 + 总评分')
# print(df.to_string())
df['总排名'] = df['辅助列'].rank(method='dense', ascending=False)
df.sort_values(by='总排名', ignore_index=True, ascending=True, inplace=True)
# 删除辅助列
df.drop(axis=1, columns='辅助列', inplace=True)
print(df.to_string())
'''
姓名 总积分 总评分 总排名
0 王五 19 38 1.0
1 李四 18 36 2.0
2 张三 18 32 3.0
3 赵六 16 35 4.0
'''
上述方式可以实现按总积分进行排名,若总积分相同则按照总评分排名的需求(row_number()
),需要借助辅助列进行操作
补充: rank()方法的pct参数使用
pct:返回相对排名(每个值在数据中的位置的百分比),百分比表示每个元素在数据集中的相对位置,默认False
# 按照总评分计算相对排名
# 例如一个相对排名为0.75的人,他的评分高于75%的人
df['相对排名'] = df['总评分'].rank(pct=True)
print(df.to_string())
'''
姓名 总积分 总评分 总排名 相对排名
0 王五 19 38 1.0 1.00
1 李四 18 36 2.0 0.75
2 张三 18 32 3.0 0.25
3 赵六 16 35 4.0 0.50
'''
# 获取相对排名前50%的人
print(df.query('相对排名 > 0.50'))
'''
姓名 总积分 总评分 总排名 相对排名
0 王五 19 38 1.0 1.00
1 李四 18 36 2.0 0.75
'''
① 通过本次小实验,结合Hive的排序函数,我们发现:
rank()
:值相同时重复,总数不变,相当于Pandas的rank(method='min')
,如1 2 2 4 ...
dense_rank()
:值相同时重复,总数减少,相当于Pandas的rank(method='dense')
,如1 2 2 3 ...
row_number()
:始终按顺序排序,相当于Pandas的rank(method='first')
,如1 2 3 4 ...
② 另外,Pandas的rank()
函数不支持多列排名,需要通过辅助列实现;但支持sort_values()
多列排序