R语言多列多条件下ifelse、coalesce、str_detect函数失效排查
问题分析与解决方案
核心问题
你代码里的判断逻辑存在两个关键错误:
coalesce函数误用:coalesce的作用是返回第一个非NA的值,但如果前面的str_detect返回FALSE(不是NA),函数就会直接返回FALSE,不会继续检查后面的列——哪怕后面的列存在目标编码,也会被忽略,这是导致判断不一致的根本原因。- 仅取
first()丢失数据:分组后只保留每列的第一个值,会丢掉同一患者其他行的编码信息,可能漏检跨多行的目标编码。
修正方案
替换coalesce为any()逻辑,同时在分组内检查所有行的编码列,确保只要患者任一相关列(或行)存在目标编码,就标记为1。另外可以简化正则表达式,减少重复代码。
修正后的代码
test_ipd <- test_ipd %>% group_by(ID_NO) %>% mutate( # 糖尿病:检查所有行的指定列是否有匹配 has_diabetes_byicd = as.integer(any( str_detect(SDIAG_CODE, "^250|^E1[0-4]"), str_detect(ICD9_CODE, "^250|^E1[0-4]"), str_detect(ICD10_CODE, "^250|^E1[0-4]"), str_detect(MDIAG_CODE, "^250|^E1[0-4]"), str_detect(CONFIRM_MDIAG, "^250|^E1[0-4]"), na.rm = TRUE )), # 肝癌:简化正则,检查所有行的指定列(包括CASITE) has_livercancer_byicd = as.integer(any( str_detect(SDIAG_CODE, "^155(\\.0)?|^C22(\\.|[0-9])+"), str_detect(ICD9_CODE, "^155(\\.0)?|^C22(\\.|[0-9])+"), str_detect(ICD10_CODE, "^155(\\.0)?|^C22(\\.|[0-9])+"), str_detect(MDIAG_CODE, "^155(\\.0)?|^C22(\\.|[0-9])+"), str_detect(CONFIRM_MDIAG, "^155(\\.0)?|^C22(\\.|[0-9])+"), str_detect(CASITE, "155(\\.0)?|C22(\\.|[0-9])+"), na.rm = TRUE )), # 慢性肝炎 has_chronic_hepatitis_byicd = as.integer(any( str_detect(SDIAG_CODE, "^070\\.[23]|^B18\\.[0-2]"), str_detect(ICD9_CODE, "^070\\.[23]|^B18\\.[0-2]"), str_detect(ICD10_CODE, "^070\\.[23]|^B18\\.[0-2]"), str_detect(MDIAG_CODE, "^070\\.[23]|^B18\\.[0-2]"), str_detect(CONFIRM_MDIAG, "^070\\.[23]|^B18\\.[0-2]"), na.rm = TRUE )), # 酒精性肝病 has_alcohol_liver_disease_byicd = as.integer(any( str_detect(SDIAG_CODE, "^571\\.0|^K70"), str_detect(ICD9_CODE, "^571\\.0|^K70"), str_detect(ICD10_CODE, "^571\\.0|^K70"), str_detect(MDIAG_CODE, "^571\\.0|^K70"), str_detect(CONFIRM_MDIAG, "^571\\.0|^K70"), na.rm = TRUE )), # NAFLD has_nafld_byicd = as.integer(any( str_detect(SDIAG_CODE, "^571\\.8|^K76\\.0"), str_detect(ICD9_CODE, "^571\\.8|^K76\\.0"), str_detect(ICD10_CODE, "^571\\.8|^K76\\.0"), str_detect(MDIAG_CODE, "^571\\.8|^K76\\.0"), str_detect(CONFIRM_MDIAG, "^571\\.8|^K76\\.0"), na.rm = TRUE )), # 肥胖 has_obesity_byicd = as.integer(any( str_detect(SDIAG_CODE, "^278\\.0|^E66"), str_detect(ICD9_CODE, "^278\\.0|^E66"), str_detect(ICD10_CODE, "^278\\.0|^E66"), str_detect(MDIAG_CODE, "^278\\.0|^E66"), str_detect(CONFIRM_MDIAG, "^278\\.0|^E66"), na.rm = TRUE )), # 肝硬化 has_cirrhosis_byicd = as.integer(any( str_detect(SDIAG_CODE, "^571\\.5|^K74(\\.[0-6])?"), str_detect(ICD9_CODE, "^571\\.5|^K74(\\.[0-6])?"), str_detect(ICD10_CODE, "^571\\.5|^K74(\\.[0-6])?"), str_detect(MDIAG_CODE, "^571\\.5|^K74(\\.[0-6])?"), str_detect(CONFIRM_MDIAG, "^571\\.5|^K74(\\.[0-6])?"), na.rm = TRUE )) ) %>% ungroup() # 处理完记得取消分组
额外说明
any(..., na.rm = TRUE)会忽略NA值,只要有一个str_detect返回TRUE,就会整体返回TRUE,符合“任一匹配即标记”的需求。as.integer()把逻辑值TRUE/FALSE转换成1/0,和原代码的输出格式一致。- 正则表达式做了简化,比如肝癌的
^C22(\\.|[0-9])+可以匹配C220、C22.0、C22.1等所有C22开头的编码,避免重复写多个选项。 - 去掉了原代码里取
first()的步骤,直接在分组内检查所有行的列,不会丢失数据。
内容的提问来源于stack exchange,提问作者Deniz Taş
相关产品推荐
相关产品推荐

