Pandas如何筛选DataFrame近1年未入选的组内最小rank对应ticker
pandas实现组内新晋最小rank标的筛选
核心逻辑
需要筛选的ticker需同时满足两个判断条件:
- 当期所在分组内rank为最小值(即rank==1)
- 回溯过去12个月的时间窗口,该ticker在同分组内从未拿到过rank最小值
前置要求
你的DataFrame需要包含以下关键字段,字段名可根据实际数据集调整:
date:时间维度字段,标记每条记录对应的统计周期group_id:分组字段,标记每个ticker所属的对比组(比如行业、风格板块等)ticker:标的代码字段rank:组内排名字段,组内最小值为1
实现步骤
1. 基础数据预处理
首先把时间字段转为pandas标准时间格式,为后续时间窗口计算做准备:
import pandas as pd import numpy as np # 转换日期格式 df['date'] = pd.to_datetime(df['date'])
2. 小数据量易读版实现
如果数据集行数在10万以内,用apply写法逻辑直观、容易调试,适合初学者:
# 第一步:先筛出所有当期rank为组内第一的候选标的 candidate_df = df[df['rank'] == 1].copy() # 第二步:逐行判断候选标的过去12个月是否拿过组内第一 def is_new_min_rank(row): # 匹配同标的、同分组,时间落在过去12个月且早于当期的历史记录 history_match = df[ (df['ticker'] == row['ticker']) & (df['group_id'] == row['group_id']) & (df['date'] >= row['date'] - pd.DateOffset(months=12)) & (df['date'] < row['date']) ] # 历史记录里没有rank=1的情况则符合要求 return (history_match['rank'] == 1).sum() == 0 candidate_df['qualified'] = candidate_df.apply(is_new_min_rank, axis=1) # 提取最终符合要求的结果 final_result = candidate_df[candidate_df['qualified']][['date', 'group_id', 'ticker']]
3. 大数据量高性能版实现
如果数据集行数超过10万,apply循环速度会很慢,可以用时间滚动窗口的向量化写法,性能提升10倍以上:
# 先按标的、分组、时间升序排序,保证时间顺序正确 df = df.sort_values(['ticker', 'group_id', 'date']).reset_index(drop=True) # 标记每条记录是否是当期组内rank第一 df['is_rank1'] = (df['rank'] == 1).astype(int) # 按标的+分组分组,计算12个月窗口内(不含当期)历史上拿过多少次组内第一 df['past_12m_rank1_cnt'] = df.groupby(['ticker', 'group_id'])['is_rank1'].rolling( window='365D', # 12个月按自然日计算,如果你是固定月度/季度数据可以换成固定周期数窗口 on='date', closed='left' # 窗口不包含当期记录 ).apply(lambda x: x.iloc[:-1].sum()).reset_index(drop=True) # 筛选当期rank=1且过去12个月没拿过第一的标的 final_result = df[ (df['rank'] == 1) & (df['past_12m_rank1_cnt'] == 0) ][['date', 'group_id', 'ticker']]
注意事项
如果你的分组规则是动态调整的(比如标的所属行业会随时间变化),请保证每条记录的
group_id是统计当期的所属分组,不要用最新分组信息回溯历史,否则会出现匹配错误。如果你的数据是固定频率更新的(比如每月初算一次rank),也可以不用时间窗口,排序后按固定周期数回溯(比如月度数据就回溯过去12条记录),逻辑更简单计算更快。
内容的提问来源于stack exchange,提问作者Zachary Marx
相关产品推荐
相关产品推荐

