基于索引合并Pandas多DataFrame:保留共通患者时序数据
首先明确:你直接赋值complete["B"] = B["value2"]的方式完全不能保证值与ID正确匹配,这是按DataFrame的行索引硬塞数据,和患者ID、时序顺序无关,一旦两个DataFrame行顺序不对应,数据就会全部错位。
正确解决方案
问题根源是仅按id合并时,每个id对应多条记录,Pandas会执行笛卡尔积,导致重复。解决核心是给每个患者的时序记录添加组内序号,标记该患者的第1/2条测量值,再用id+序号作为合并键,精准匹配同患者同时序的记录。
步骤分解(以A、B为例,扩展到3个DataFrame逻辑一致):
给每个DataFrame添加组内序号
按id分组后,为每组的行生成从1开始的序号,对应测量时序:import pandas as pd # 还原你的示例数据(注意你提供的df1对应B,df2对应A) df_A = pd.DataFrame({"id":[1,1,2,2],"value1":[80,78,76,79]}) df_B = pd.DataFrame({"id":[2,2,3,3], "value2":[65,67,74,65]}) # 添加组内时序序号 df_A["seq"] = df_A.groupby("id").cumcount() + 1 # 从1开始计数,对应第1/2次测量 df_B["seq"] = df_B.groupby("id").cumcount() + 1筛选三个DataFrame共有的患者ID
取所有DataFrame中ID的交集,只保留这些患者的记录:# 若有第三个DataFrame df_C,只需加入intersection参数即可 common_ids = set(df_A["id"]).intersection(df_B["id"])过滤数据,仅保留共同ID的记录
df_A_filtered = df_A[df_A["id"].isin(common_ids)] df_B_filtered = df_B[df_B["id"].isin(common_ids)]按
id+seq合并数据
此时合并不会产生笛卡尔积,精准匹配同患者同时序的记录:merged_df = pd.merge(df_A_filtered, df_B_filtered, on=["id", "seq"], how="inner") # 若有第三个DataFrame,继续执行合并: # merged_df = pd.merge(merged_df, df_C_filtered, on=["id", "seq"], how="inner") # 不需要seq列的话可以删除 merged_df = merged_df.drop("seq", axis=1)
最终得到的结果与预期一致:
| id | value1 | value2 |
|---|---|---|
| 2 | 76 | 65 |
| 2 | 79 | 67 |
方法原理
每个患者的记录是按时间排序的,cumcount()生成的序号对应了该患者的第n次测量,用id+seq作为合并键,彻底避免了同患者多条记录交叉匹配的问题,完美解决笛卡尔积重复。
内容的提问来源于stack exchange,提问作者aegis301
相关产品推荐
相关产品推荐

