r - 使用 dplyr (tidyeval) 遍历选定列的函数
问题描述
一段时间以来,我一直在努力实现 Hadley 的https://dplyr.tidyverse.org/articles/programming.html,但我无法让它为我的目的工作。
在我的玩具示例中,我想编写一个函数,它将两列(列名已知)作为参数并运行 t.test。问题是我似乎无法将列名传递给 t.test。这里是:
df <- tibble(
names = LETTERS[1:10],
colA = rnorm(10),
colB = rnorm(10, 1, 2),
colC = rnorm(10, 3, 4)
)
这就是我的想法(请注意,我使用 magrittr 直接将列提取为向量):
myFun <- function(data, column_name1, column_name2) {
data %$%
t.test(column_name1, column_name2)$p.value
}
myFun(df, colA, colB) # doesn't work
Hadley 的指南说,要正确解析列名,我们需要使用 quosure 来“引用”变量名,然后在函数中使用它们时“取消引用”它们(他使用的词“dark magic”对我来说听起来很准确)。我是这样解释的:
myFun <- function(data, column_name1, column_name2) {
col1 <- enquo(column_name1)
col2 <- enquo(column_name2)
data %$%
t.test(!!col1, !!col2)$p.value
}
myFun(df, colA, colB)
但是,这会导致错误:
Error: Quosures can only be unquoted within a quasiquotation context.
# Bad:
list(!!myquosure)
# Good:
dplyr::mutate(data, !!myquosure)
我认为这意味着我不能将 quosure 与 t.test (非 tidyverse 函数)一起使用?所以我现在放弃了我的 t.test 并试图看看是否只是选择列有效:
myFun <- function(data, column_name1, column_name2) {
col1 <- enquo(column_name1)
col2 <- enquo(column_name2)
data %>%
dplyr::select(!!col1, !!col2)
}
myFun(df, colA, colB)
这行得通。但后来这个:
myFun <- function(data, column_name1, column_name2) {
col1 <- enquo(column_name1)
col2 <- enquo(column_name2)
data %>%
dplyr::select(!!col1, !!col2) %$%
t.test(col1, col2)$p.value
}
myFun(df, colA, colB)
没有,有错误Error in t.test.formula(colA, colB) : 'formula' missing or incorrect
(即使我没有为 t.test 使用公式表示法)。为了清楚起见,这很好用:df %$% t.test(colA, colB)$p.value
.
我想了解为什么传递列名(选择正确!)在此函数中不起作用,如果这是运行成对 t.tests 的绝望想法,那么更好的解决方案是什么?这样做的最终目标是一个更大的函数,它将采用任意一组数字列并运行所有可能的成对 t.tests。
谢谢!
千焦
解决方案
几点:
本指南是关于与 tidy eval 函数的接口。相反,您正在尝试创建一个新的 tidy eval 函数。
这是一个不会完成的 WIP,因为我们已经让 tidy eval 变得更简单,它不再需要一本书。我建议使用 dplyr 编程小插图的下一个版本:https ://dplyr.tidyverse.org/dev/articles/programming.html (
/dev
一旦 dplyr 1.0 发布,请从 URL 中删除)。通过
transmute()
或select()
接受输入然后使用数据框是一种不错的方法。但是,
select()
当您使用多个变量时,最好使用接口。在这里,您要选择一个,即未经select()
. 例如,用户可以提供starts_with()
作为输入。您的函数中缺少的要点是您没有为列指定常量名称,以便您可以将它们拾取并提供给
t.test()
.
我建议类似(未经测试):
t_test <- function(data, col1, col2) {
data <- data %>% transmute(
col1 = {{ col1 }},
col2 = {{ col2 }}
)
test <- t.test(data$col1, data$col2)
test$p.value
}