基于阶段排序过滤分组数据:寻求无循环实现方案
问题:高效过滤ID阶段回退前的数据
数据记录了各ID在指定日期所处的Stage,理想状态下每个ID应向Stage 4定向推进(无需经过所有阶段),但有时会回退至之前的阶段(包括同一阶段)。数据已按ID内的日期降序排序:
import pandas as pd df = pd.DataFrame( { "ID": ["A","A","A","B","B","B","B","B","B","C","C","C","C","C","C"], "Stage":[4,2,1,4,3,4,3,2,1,4,3,2,1,2,1], "Date":['2022-09-18','2022-09-17','2022-09-16','2022-09-20','2022-09-19','2022-09-18','2022-09-17','2022-09-16','2022-09-15', '2022-09-20','2022-09-19','2022-09-18','2022-09-17','2022-09-16','2022-09-15'] } ) print(df)
需求:删除每个ID在最近一次阶段回退之前的所有行,得到目标过滤结果。请问是否存在无需使用for循环的简洁方法实现从df到df_filtered的转换?
解决方案:纯向量化实现
完全可以用Pandas的分组和窗口函数实现,不需要for循环,步骤如下:
- 标记回退行为:对每个ID,比较当前Stage与下一条记录的Stage(数据是日期降序,下一条对应更早的日期),如果当前Stage < 下一条Stage,说明发生了回退(因为理想是向Stage4推进,数值越大阶段越靠后,更早日期的Stage更大意味着从高阶段回退到低阶段)。
- 定位最近一次回退的起始点:对每个ID的回退标记从后往前(最早日期到最新日期)累积,找到最后一次回退的位置,保留该位置及之后的所有行(即最新日期方向的行)。
具体代码:
# 1. 按ID分组,标记回退行为 df['is_backward'] = df.groupby('ID')['Stage'].apply(lambda x: x < x.shift(1)).fillna(False) # 2. 按ID分组,计算需要保留的行标记 df['keep_flag'] = df.groupby('ID')['is_backward'].transform(lambda x: x[::-1].cumsum() > 0) # 3. 过滤数据:保留回退起始点之后的行,同时保留无回退记录的ID的所有行 df_filtered = df[df['keep_flag'] | (~df.groupby('ID')['is_backward'].transform('any'))].drop(columns=['is_backward', 'keep_flag']) print(df_filtered)
结果说明
运行后得到的df_filtered会精准保留每个ID最近一次回退及之后的所有行:
- ID A:所有行都在回退序列内,完整保留
- ID B:保留从2022-09-20到2022-09-17的记录(最近一次回退的起始点)
- ID C:保留从2022-09-20到2022-09-15的记录(包含最后一次回退及后续行为)
内容的提问来源于stack exchange,提问作者freesoup
相关产品推荐
相关产品推荐

