如何用dplyr基于df2批量替换df1多列中的NA值
问题
现有两个大型数据框df1(155列×3966行)与df2,二者首列均为ID,其余列名重叠。需求为:将df1中的NA值用df2对应位置的非NA值替换,无对应值则保留df1原数据。
示例数据
df1
> df1 ID col1 col2 ...... col154 1 AMM115 C A ...... A+ 2 ADM107 NA NA ...... B 3 AGM041 B C ...... C+ 4 AGM132 A NA ...... A+ 5 AQM007 NA A ...... B+ 6 ARM028 NA B+ ...... A- 7 ASM019 A A+ ...... NA
df2
> df2 ID col1 col2 ...... col154 1 ADM107 A+ B ...... B 2 AGM041 C A ...... B+ 3 ARM028 A+ B+ ...... NA 4 AQM007 B+ A ...... B+
期望结果df3
> df3 ID col1 col2 ...... col154 1 AMM115 C A ...... A+ 2 ADM107 A+ B ...... B 3 AGM041 B C ...... C+ 4 AGM132 A NA ...... A+ 5 AQM007 B+ A ...... B+ 6 ARM028 A+ B+ ...... A- 7 ASM019 A A+ ...... NA
已尝试的代码
仅能处理单列,无法批量处理除ID外的所有列:
library(dplyr) df1 %>% left_join(df2, by = "ID") %>% mutate(var2 = coalesce(var2.x, var2.y)) %>% select(-var2.x, -var2.y)
解决方案
方法1:dplyr + across 批量处理
利用dplyr的across函数对所有非ID列批量应用coalesce,结合关联后的列名规则实现批量替换:
library(dplyr) library(stringr) # 关联两个数据框 df_joined <- df1 %>% left_join(df2, by = "ID") # 批量替换并整理结果 df3 <- df_joined %>% mutate( across( .cols = matches("_x$"), .fns = ~ coalesce(., get(str_replace(cur_column(), "_x$", "_y"))), .names = "{str_remove(.col, '_x$')}" ) ) %>% select(ID, all_of(setdiff(names(df1), "ID")))
方法2:data.table 高效处理(适合大型数据)
针对大尺寸数据,data.table的操作速度更优:
library(data.table) # 转换为data.table格式 setDT(df1) setDT(df2) # 按ID关联并批量替换NA df3 <- df1[df2, on = "ID"] for (col in setdiff(names(df1), "ID")) { set(df3, i = which(is.na(df3[[col]])), j = col, value = df3[[paste0(col, ".i")]] ) } # 清理临时列 df3[, paste0(setdiff(names(df1), "ID"), ".i") := NULL]
方法3:base R 原生实现
无需额外依赖包,用原生函数完成替换:
# 匹配df2对应df1的行索引 matched_rows <- match(df1$ID, df2$ID) # 遍历所有非ID列替换NA for (col in setdiff(names(df1), "ID")) { na_idx <- is.na(df1[[col]]) df1[na_idx, col] <- df2[matched_rows[na_idx], col] } df3 <- df1
内容的提问来源于stack exchange,提问作者Roq
相关产品推荐
相关产品推荐

