首页 > 解决方案 > 使用 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。

谢谢!

千焦

标签: rfunctiondplyrtidyeval

解决方案


几点:

  • 本指南是关于与 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
}

推荐阅读