如何在dplyr的mutate管道中按行应用匿名函数?
代码报错逻辑说明
第一段代码报错原因
R里的*if()语句*仅支持输入长度为1的逻辑判断条件,传入长度大于1的逻辑向量时,只会读取第一个元素做判断,同时抛出the condition has length > 1的报错。mutate_at(以及替代它的across)在执行时,会把选中的每一列作为完整向量传给你定义的处理函数,也就是说你函数里接收到的x是长度为32(mtcars的总行数)的整列数据。此时写if(x == 1)相当于给if传入了32个逻辑值,完全不符合if的输入要求;另外你写的函数分支没有处理不等于1的情况,就算不报错,未匹配的位置也会返回空值。
第二段代码报错原因
lapply的必填参数有两个:第一个是待遍历的对象X,第二个是要应用到每个元素上的函数FUN,语法为lapply(X, FUN, ...)。你写的lapply(function(x) {if(x == 1){x <- 2}})只传入了匿名函数,没有传入待遍历的对象,R会默认把你传入的匿名函数当成待遍历的X,然后寻找缺失的FUN参数,直接抛出Error in match.fun(FUN) : argument "FUN" is missing, with no default的错误。
另外就算补全lapply的参数,在mutate_at里套lapply也是多余逻辑:mutate_at本身就是逐列传值的设计,不需要你额外手动套遍历函数。
正确实现方式
这个值替换场景不需要逐行写循环,直接用R的向量化操作即可,两种常用写法:
- 用向量级条件判断函数
ifelse(和仅支持单值判断的if不同,ifelse可以逐元素处理向量输入):
# 旧版mutate_at写法 mtcars %>% mutate_at(vars(vs,am), function(x) ifelse(x == 1, 2, x)) # 目前官方推荐的across写法(mutate_at已被标记为过时) mtcars %>% mutate(across(c(vs, am), ~ifelse(.x == 1, 2, .x)))
- 直接用逻辑索引替换目标值,执行效率更高:
mtcars %>% mutate(across(c(vs, am), ~{.x[.x == 1] <- 2; .x}))
内容的提问来源于stack exchange,提问作者Chris Oosthuizen
相关产品推荐
相关产品推荐

