如何用Pandas按ID获取最后一次状态变更的首次日期?
解决方案
问题分析
你需要的是每个ID最后一次状态变更后,该状态的首次出现日期,而非所有历史状态的最早日期。之前的分组取最小日期会保留所有状态的历史记录,无法聚焦到最后一次变更的节点。
实现步骤与代码
import pandas as pd # 原始数据处理(修正日期笔误:'17/01/202'改为'17/01/2020') data = {'id': ['1','1','1','1','2','2','2','2','2','3','3','3','3','3','3','3'], 'status': ['Active','Active','Active','Pending Action','Pending Action','Pending Action','Active','Pending Action','Active','Draft','Active','Draft','Draft','Draft','Active','Draft'], 'calc_date_id':['05/07/2022','07/06/2022','31/08/2021','01/07/2021','20/11/2022','25/10/2022','02/04/2022','28/02/2022','01/07/2021','23/06/2022','15/06/2022','07/04/2022','09/11/2022','18/08/2020','19/03/2020','17/01/2020'] } df = pd.DataFrame(data) # 转换日期(注意日/月/年格式,需指定dayfirst=True) df['calc_date_id'] = pd.to_datetime(df['calc_date_id'], dayfirst=True) # 1. 按ID分组,每组内按日期升序排序,还原时间顺序 df_sorted = df.sort_values(['id', 'calc_date_id'], ascending=[True, True]).reset_index(drop=True) # 2. 标记同ID下状态发生变更的行(当前行与上一行状态不同) df_sorted['status_changed'] = df_sorted.groupby('id')['status'].apply(lambda x: x != x.shift()) # 3. 筛选所有状态变更节点(包括每个ID的初始状态,用fillna(True)处理第一行的NaN) df_changes = df_sorted[df_sorted['status_changed'].fillna(True)] # 4. 取每个ID的最后一次状态变更节点,即为目标结果 result = df_changes.groupby('id').last().reset_index()[['id', 'status', 'calc_date_id']] print(result)
输出结果
id status calc_date_id 0 1 Active 2021-08-31 1 2 Pending Action 2022-10-25 2 3 Draft 2022-06-23
关键逻辑说明
- 排序:确保每个ID的状态按时间顺序排列,才能准确追踪变更节点。
- 标记变更:用
shift()对比当前行与上一行的状态,找出所有状态切换的位置。 - 筛选节点:保留每个状态首次出现的行(包括初始状态),形成状态变更历史序列。
- 取最后一次变更:每个ID的最后一个变更节点,就是最后一次状态切换后的首次日期。
内容的提问来源于stack exchange,提问作者mali
相关产品推荐
相关产品推荐

