多类型相似名称列中替换NA值并保留列类型的技术问题
问题与解决:保留列类型替换NA为对应类型的0
问题场景
我有一个数据集,包含多列名称相似但数据类型不同的字段,需要把所有NA值替换成0,同时必须保留各列的原有数据类型——因为后续还要给不同字符串分配其他数值。
示例数据集:
qdf = data.frame(bicep_wt = c("black band", "5", NA), tricep_wt = c(2,NA,3))
尝试了两段代码替换NA,结果都报错:
代码1:
mutate(qdf, across(contains("wt"), ~case_when(is.numeric(.x) ~ tidyr::replace_na(., 0), is.character(.x) ~ tidyr::replace_na(., "0"))))
代码2:
mutate(qdf, across(contains("wt"), ~case_when(is.numeric(.x) ~ tidyr::replace_na(.x, 0), is.character(.x) ~ tidyr::replace_na(.x, "0"))))
错误信息:
Error in `mutate()`: i In argument: `across(...)`. Caused by error in `across()`: ! Can't compute column `bicep_wt`. Caused by error in `case_when()`: ! Failed to evaluate the right-hand side of formula 1. Caused by error in `vec_assign()`: ! Can't convert `replace` <double> to match type of `data` <character>.
错误原因
case_when的特性是会提前计算所有分支的表达式,然后统一返回类型。拿字符型的bicep_wt列来说,第一个分支is.numeric(.x)结果是FALSE,但R还是会先执行replace_na(.x, 0),这就试图把数值型的0塞进字符型列,直接触发类型不匹配的报错。
可行解决方案
放弃case_when,改用分支判断或者更贴合场景的函数:
方法1:用if/else分支判断列类型
library(dplyr) library(tidyr) qdf %>% mutate(across(contains("wt"), ~{ if (is.numeric(.x)) { replace_na(.x, 0) } else if (is.character(.x)) { replace_na(.x, "0") } else { .x # 其他类型的列保持原样 } }))
方法2:用coalesce简化代码
coalesce会直接返回第一个非NA值,结合类型判断动态生成替换值:
qdf %>% mutate(across(contains("wt"), ~coalesce(., if (is.numeric(.)) 0 else "0")))
方法3:基础replace函数实现
qdf %>% mutate(across(contains("wt"), ~{ replace(., is.na(.), if (is.numeric(.)) 0 else "0") }))
最终结果
运行上述任一代码后,得到的结果如下,各列类型完全保留,NA被替换为对应类型的0:
bicep_wt tricep_wt 1 black band 2 2 5 0 3 0 3
内容的提问来源于stack exchange,提问作者Mark Davies
相关产品推荐
相关产品推荐

