如何用R语言MatchIt包基于年龄与多ICD10编码匹配病例与对照
问题:基于多ICD10编码和年龄的病例对照匹配(MatchIt包)
需求说明
需要使用R语言的MatchIt包为病例患者匹配对照,要求:
- 对照与病例年龄完全一致
- 对照的ICD10编码至少包含病例的所有ICD10编码(对照可拥有更多编码)
当前问题:单个患者对应多条ICD10编码记录,现有匹配逻辑会按单条ICD记录分别匹配,导致病例匹配到多个仅满足部分ICD条件的对照,而非同时满足所有ICD条件的目标对照。
数据示例
library(tibble) df <- tribble( ~patient_id, ~diagnosis, ~age, ~status, 1001, "Z34", 20, "case", 1001, "A24", 20, "case", 1002, "N39", 22, "case", 1002, "Z3A", 22, "case", 1003, "N89", 23, "case", 1003, "Z34", 23, "case", 1004, "Z34", 20, "control", 1004, "A24", 20, "control", 1005, "D50", 23, "control", 1005, "F41", 23, "control", 1005, "N89", 23, "control", 1005, "Z11", 23, "control", 1005, "Z34", 23, "control", 1006, "Z12", 22, "control", 1006, "Z34", 22, "control", 1006, "N39", 22, "control", 1007, "E66", 20, "control", 1007, "Z11", 20, "control", 1007, "Z12", 20, "control", 1007, "Z34", 20, "control" )
尝试的代码及问题
library(MatchIt) library(dplyr) m.out <- matchit(I(status == "case") ~ age, data = df, exact = ~age + diagnosis, method = "optimal", distance = "glm", ratio = 1) m.data <- match.data(m.out, subclass = "matched_id") print(m.data)
问题:病例1001同时匹配了1007(仅含Z34)和1004(含Z34+A24),但我们只需要匹配同时包含病例所有ICD编码的1004。
解决方案
核心思路是先将数据按患者聚合,统一管理每个患者的ICD编码集合,再基于集合包含关系筛选合格对照,最后完成匹配。
步骤1:聚合患者数据
将每个患者的多条ICD记录合并为一行,存储其所有ICD编码的集合:
library(dplyr) library(purrr) # 聚合每个患者的ICD编码为集合 patient_data <- df %>% group_by(patient_id, age, status) %>% summarise(icd_codes = list(diagnosis), .groups = "drop") %>% mutate(icd_set = map(icd_codes, ~sort(.x))) # 排序后方便后续判断包含关系
步骤2:筛选合格对照
为每个病例筛选同年龄且ICD集合包含病例所有编码的对照:
# 分离病例和对照数据集 cases <- patient_data %>% filter(status == "case") controls <- patient_data %>% filter(status == "control") # 为每个病例匹配符合条件的对照 matched_pairs <- cases %>% rowwise() %>% mutate( eligible_controls = list( controls %>% filter(age == !!age) %>% filter(map_lgl(icd_set, ~all(!!icd_set %in% .x))) %>% pull(patient_id) ) ) %>% ungroup() # 查看每个病例的合格对照列表 print(matched_pairs)
步骤3:完成匹配并整合数据
从合格对照中选择匹配对象(示例为随机选1个,也可根据需求用最优匹配逻辑),并整合回原始数据:
# 为每个病例分配1个合格对照(无合格对照则标记为NA) final_matches <- matched_pairs %>% rowwise() %>% mutate(matched_control = ifelse(length(eligible_controls) > 0, sample(eligible_controls, 1), NA)) %>% ungroup() %>% select(patient_id, matched_control, age) # 整合回原始数据,查看匹配后的所有记录 matched_data <- df %>% filter(patient_id %in% c(final_matches$patient_id, final_matches$matched_control)) %>% left_join(final_matches, by = c("patient_id", "age")) print(matched_data)
扩展说明
- 如果需要使用MatchIt的专业匹配算法(如最优匹配、卡钳匹配),可基于筛选出的合格对照池,在
matchit函数中通过subclass或自定义距离函数实现更精准的匹配控制。 - 若需匹配多个对照,只需调整
sample函数的参数或改用MatchIt的ratio参数。
内容的提问来源于stack exchange,提问作者EpidemicRx
相关产品推荐
相关产品推荐

