如何高效筛选DataFrame中各patient_id窗口内的最新alert?
高效实现按患者ID的滑动时间窗口筛选最新告警
原始数据
| patient_id | alert_id | alert_timestamp |
|---|---|---|
| 3 | xyz | 2022-10-10 |
| 1 | anp | 2022-10-12 |
| 1 | gfe | 2022-10-10 |
| 2 | fgy | 2022-10-02 |
| 2 | gpl | 2022-10-03 |
| 1 | gdf | 2022-10-13 |
| 2 | mkd | 2022-10-23 |
| 1 | liu | 2022-10-01 |
需求说明
针对每个patient_id,仅保留给定时间窗口(如window_size = 7天)内的最新告警(即最后一条):
- 窗口为连续日期范围:从某起始日期到
起始日期+window_size - 每个患者的告警时间跨度通常远大于窗口大小
- 实际数据包含更多患者ID,且告警时间、告警ID的顺序是混乱的
处理逻辑
从每个patient_id的最晚alert_timestamp倒推,用window_size筛选该窗口内的最后一条告警,通过滑动窗口遍历所有时间戳。
预期输出(window_size=7时)
| patient_id | alert_id | alert_timestamp |
|---|---|---|
| 1 | liu | 2022-10-01 |
| 1 | gdf | 2022-10-13 |
| 2 | gpl | 2022-10-03 |
| 2 | mkd | 2022-10-23 |
| 3 | xyz | 2022-10-10 |
高效实现方案
第一步:数据预处理
先把时间列转成datetime类型,确保能做时间计算;然后按患者ID分组,每组内的记录按时间从晚到早排序——这样处理时,每组第一条就是该时间段内的最新告警,方便从后往前推窗口:
import pandas as pd # 转换时间格式 df['alert_timestamp'] = pd.to_datetime(df['alert_timestamp']) # 按患者ID升序、告警时间降序排序 sorted_df = df.sort_values(['patient_id', 'alert_timestamp'], ascending=[True, False])
第二步:核心筛选逻辑
对于每个患者的排序后数据,从最晚的告警时间开始,先保留这条最新记录,然后把下一个窗口的结束时间设为当前记录时间减去窗口大小,跳过该窗口内的所有记录,再找下一个窗口的最新记录,以此类推。这种方法避免了重复遍历,效率很高:
window_size = pd.Timedelta(days=7) keep_indices = [] # 逐个处理每个患者的告警组 for _, patient_group in sorted_df.groupby('patient_id', sort=False): # 初始窗口结束时间设为该患者最晚的告警时间 current_window_end = patient_group['alert_timestamp'].iloc[0] # 遍历该患者的每条告警 for idx, row in patient_group.iterrows(): # 如果当前告警在窗口范围内 if row['alert_timestamp'] >= current_window_end - window_size: # 保留这条窗口内的最新告警 keep_indices.append(idx) # 更新窗口结束时间,跳到当前告警时间往前推7天的位置 current_window_end = row['alert_timestamp'] - window_size # 筛选出保留的记录,并按患者ID和时间排序 result_df = df.loc[keep_indices].sort_values(['patient_id', 'alert_timestamp'])
方案优势
- 提前排序后,每组内记录从晚到早排列,取完窗口内最新记录后直接跳转到窗口外,减少不必要的遍历
- 避免使用滚动窗口(rolling)计算,滚动计算会遍历每个时间点,大数据量下效率极低
- 分组时使用
sort=False,利用之前的全局排序结果,节省额外的排序开销
内容的提问来源于stack exchange,提问作者Ossz
相关产品推荐
相关产品推荐

