为何需将返回字符向量的函数用c()包裹后传入tidyr的...参数?
关于tidyr::gather中使用自定义函数选择列的问题
这个问题我之前也碰到过,其实是dplyr/tidyr的列选择语法和R基础向量运算的小冲突导致的,咱们一步步拆解来看:
首先先还原你的示例代码和场景:
df <- data.frame(month=rep(1:3,2), student=rep(c("Amy", "Bob"), each=3), A=c(9, 7, 6, 8, 6, 9), B=c(6, 7, 8, 5, 6, 7)) cnames<-function(){c(month="month",student="student")}
成功的写法
当你用c()包裹自定义函数的返回值时,tidyr::gather可以正常运行:
df2 <- df %>% gather(variable, value, -c(cnames()))
失败的写法
但直接使用-(cnames())时就会报错:
df2 <- df %>% gather(variable, value, -(cnames()))
为什么会这样?
核心原因在于你的cnames()返回的是命名字符向量。当你直接写-(cnames())时,R的基础运算会先对这个命名向量的每个元素执行“取负”操作——这会把c(month="month", student="student")变成c(month = -"month", student = -"student"),这显然不是gather需要的列选择格式(它需要的是要排除的列名字符向量)。
而用c(cnames())时,虽然结果还是命名向量,但tidyr::gather底层的列选择逻辑会自动忽略向量的命名,只提取向量的值作为列名,所以能正常工作。
更清晰的解决方案
除了你已经发现的-c(cnames())写法,还有几种更规范的方式:
- 方式一:使用
all_of()明确指定列名向量(dplyr 1.0.0+推荐)
先把函数返回的列名存为变量,再用all_of()告诉函数这是字符向量格式的列名:keep_cols <- cnames() df2 <- df %>% gather(variable, value, -all_of(keep_cols)) - 方式二:修改函数返回未命名向量
让cnames()直接返回不带命名的字符向量,这样-(cnames())就能正常解析:cnames<-function(){c("month","student")} df2 <- df %>% gather(variable, value, -(cnames())) - 方式三:使用非标准求值运算符
!!
用!!把函数返回的向量注入到表达式中,让gather正确识别为列名:df2 <- df %>% gather(variable, value, -!!cnames())
内容的提问来源于stack exchange,提问作者witek
相关产品推荐
相关产品推荐

