基于分组的Pandas滚动排名计算问题(Rolling Rank Groupby)
按ID分组的滚动排名计算(Pandas实现)
需求描述
需要在Pandas中实现按ID分组的滚动排名计算:按每个ID分组,基于该ID的历史Value值(滚动2年窗口)计算当前值的排名,预期结果列如示例中的Rolling 2Y Rank。
示例数据
import pandas as pd import numpy as np df = pd.DataFrame({ 'Year': [2000,2000,2000,2001,2001,2001,2002,2002,2002,2003,2003,2003], 'ID': ['A','B','C','A','B','C','A','B','C','A','B','C'], 'Value': [5,1,2,3,4,3,2,7,1,1,13,23], 'Rolling 2Y Rank': [np.nan,np.nan,np.nan, 2,1,1,2,1,2,2,1,1]})
对应的表格:
| Year | ID | Value | Rolling 2Y Rank |
|---|---|---|---|
| 2000 | A | 5 | nan |
| 2000 | B | 1 | nan |
| 2000 | C | 2 | nan |
| 2001 | A | 3 | 2 |
| 2001 | B | 4 | 1 |
| 2001 | C | 3 | 1 |
| 2002 | A | 2 | 2 |
| 2002 | B | 7 | 1 |
| 2002 | C | 1 | 2 |
| 2003 | A | 1 | 2 |
| 2003 | B | 13 | 1 |
| 2003 | C | 23 | 1 |
以ID=A为例:
- 2000年无历史数据,结果为NA;
- 2001年将3与2000年的5(2年滚动窗口)比较,降序排名为2;
- 2002年将2与2001年的3比较,降序排名为2,以此类推。
问题分析
尝试的代码df.groupby('ID')['Value'].rolling(2).rank()未得到预期结果,原因有两点:
- 默认的
rank()是升序排名,而需求是降序排名(值越大排名越靠前); - 未指定
min_periods,且未处理groupby滚动后的索引对齐问题,导致结果无法正确匹配原DataFrame。
正确实现方法
步骤1:确保数据按ID和Year排序(避免窗口计算出错)
df = df.sort_values(['ID', 'Year'])
步骤2:分组计算滚动降序排名
df['Calculated Rolling Rank'] = df.groupby('ID')['Value'].rolling( window=2, # 2年滚动窗口 min_periods=2 # 窗口至少包含2条数据才计算,否则为NaN ).rank( ascending=False, # 降序排名:值越大排名越靠前 method='min' # 相同值取最小排名,匹配示例逻辑 ).reset_index(level=0, drop=True)
验证结果
执行后Calculated Rolling Rank列将与预期的Rolling 2Y Rank完全一致:
print(df[['Year', 'ID', 'Value', 'Rolling 2Y Rank', 'Calculated Rolling Rank']])
输出:
| Year | ID | Value | Rolling 2Y Rank | Calculated Rolling Rank | |
|---|---|---|---|---|---|
| 0 | 2000 | A | 5 | NaN | NaN |
| 3 | 2001 | A | 3 | 2.0 | 2.0 |
| 6 | 2002 | A | 2 | 2.0 | 2.0 |
| 9 | 2003 | A | 1 | 2.0 | 2.0 |
| 1 | 2000 | B | 1 | NaN | NaN |
| 4 | 2001 | B | 4 | 1.0 | 1.0 |
| 7 | 2002 | B | 7 | 1.0 | 1.0 |
| 10 | 2003 | B | 13 | 1.0 | 1.0 |
| 2 | 2000 | C | 2 | NaN | NaN |
| 5 | 2001 | C | 3 | 1.0 | 1.0 |
| 8 | 2002 | C | 1 | 2.0 | 2.0 |
| 11 | 2003 | C | 23 | 1.0 | 1.0 |
内容的提问来源于stack exchange,提问作者Derek
相关产品推荐
相关产品推荐

