使用mutate_if或mutate(across())转换字符列至数值列时遇错求助
需求与数据背景
需求:将除b$Gene.names外,所有列名包含Intensity的字符列通过as.numeric转换为数值列。
数据结构:
str(b) tibble [3 × 3] (S3: tbl_df/tbl/data.frame) $ Intensity.DISEASE5: chr [1:3] "27.37" ... $ Intensity.DISEASE6: chr [1:3] "27.47" ... $ Gene.names : chr [1:3] "IGLL5;IGLC1" ...
数据定义:
b <- structure(list(Intensity.DISEASE5 = c("27.3736820220947", "23.7670879364014", "24.8043975830078"), Intensity.DISEASE6 = c("27.4711933135986", "26.1416263580322", "25.3245735168457"), Gene.names = c("IGLL5;IGLC1", "QSOX1", "GFRA2")), row.names = c(NA, -3L), class = c("tbl_df", "tbl", "data.frame"))
方案一:mutate_if写法问题
原代码:
b %>% mutate_if((str_detect(colnames(.), "Intensity") & is.character), as.numeric)
报错信息:
Error in str_detect(colnames(.), "Intensity") & is.character :
operations are possible only for numeric, logical or complex types
失效原因
mutate_if的第一个参数要求是作用于列值的逻辑判断函数,但这里直接将str_detect(colnames(.), "Intensity")(一个长度等于列数的逻辑向量)和is.character(一个函数)做逻辑与运算,属于向量与函数的非法操作,导致类型不匹配报错。此外,str_detect判断列名的逻辑,不能直接放在mutate_if的谓词参数中——mutate_if的谓词是针对列值而非列名的。
修正写法
推荐使用tidyverse新版本的across函数,或者用mutate_at指定列名匹配规则:
# 推荐:across写法 b %>% mutate(across(contains("Intensity"), as.numeric)) # 兼容旧版本:mutate_at写法 b %>% mutate_at(vars(contains("Intensity")), as.numeric)
如果需要额外确保列是字符类型再转换(避免误操作非字符列),可以补充判断:
b %>% mutate(across(where(~ str_detect(colnames(.), "Intensity") & is.character(.)), as.numeric))
方案二:across用法错误
原代码:
b %>% mutate(across(where(contains("Intensity")), as.character))
报错信息:
Error in
mutate(): ! Problem while computing..1 = across(where(contains("Intensity")), as.character). Caused by error
inacross(): ! Can't convertfn, an integer vector, to a function.
失效原因
across的第一个参数是列选择器,where()接收的是针对列值的判断函数,但contains("Intensity")本身就是列名匹配选择器,不需要嵌套在where()中。把contains放进where()后,where()会将列名作为参数传给contains,返回的是整数向量而非逻辑判断结果,across无法识别该类型,因此报错。另外原需求是转数值型,代码中写as.character属于目标函数写错,但核心报错原因是列选择器的用法错误。
修正写法
去掉多余的where(),直接用contains匹配列名,并将转换函数改为as.numeric:
b %>% mutate(across(contains("Intensity"), as.numeric))
内容的提问来源于stack exchange,提问作者cmirian

