использование `rlang :: exec` с функциями, которые используют` rlang :: ensym`

Я пытаюсь написать собственную функцию, которая немного сложнее, поэтому для простоты я создал игрушечные примеры.

Скажем, я хочу написать функцию, которая ...

  1. автоматически выбирает подходящую функцию для запуска: например, t-тест или anova.
  2. принимает как "quoted", так и unquoted аргументы

Поэтому я пишу функцию для запуска t-теста (работает, как ожидалось):

set.seed(123)
library(rlang)
library(tidyverse)

# t-test function
fun_t <- function(data, x, y) {
  # make sure both quoted and unquoted arguments work
  x <- rlang::ensym(x)
  y <- rlang::ensym(y)

  # t-test
  broom::tidy(stats::t.test(
    formula = rlang::new_formula({{ y }}, {{ x }}),
    data = data
  ))
}

# works fine
fun_t(mtcars, am, wt)
#> # A tibble: 1 x 10
#>   estimate estimate1 estimate2 statistic p.value parameter conf.low
#>      <dbl>     <dbl>     <dbl>     <dbl>   <dbl>     <dbl>    <dbl>
#> 1     1.36      3.77      2.41      5.49 6.27e-6      29.2    0.853
#> # ... with 3 more variables: conf.high <dbl>, method <chr>,
#> #   alternative <chr>

fun_t(mtcars, "am", "wt")
#> # A tibble: 1 x 10
#>   estimate estimate1 estimate2 statistic p.value parameter conf.low
#>      <dbl>     <dbl>     <dbl>     <dbl>   <dbl>     <dbl>    <dbl>
#> 1     1.36      3.77      2.41      5.49 6.27e-6      29.2    0.853
#> # ... with 3 more variables: conf.high <dbl>, method <chr>,
#> #   alternative <chr>

Затем я пишу функцию для запуска ановы (работает как положено):

# anova function
fun_anova <- function(data, x, y) {
  # make sure both quoted and unquoted arguments work
  x <- rlang::ensym(x)
  y <- rlang::ensym(y)

  # t-test
  broom::tidy(stats::aov(
    formula = rlang::new_formula({{ y }}, {{ x }}),
    data = data
  ))
}

# works fine
fun_anova(mtcars, cyl, wt)
#> # A tibble: 2 x 6
#>   term         df sumsq meansq statistic      p.value
#>   <chr>     <dbl> <dbl>  <dbl>     <dbl>        <dbl>
#> 1 cyl           1  18.2 18.2        47.4  0.000000122
#> 2 Residuals    30  11.5  0.384      NA   NA

fun_anova(mtcars, "cyl", "wt")
#> # A tibble: 2 x 6
#>   term         df sumsq meansq statistic      p.value
#>   <chr>     <dbl> <dbl>  <dbl>     <dbl>        <dbl>
#> 1 cyl           1  18.2 18.2        47.4  0.000000122
#> 2 Residuals    30  11.5  0.384      NA   NA

Затем я пишу мета-функцию, чтобы выбрать соответствующую функцию из приведенных выше -

fun_meta <- function(data, x, y) {
  # make sure both quoted and unquoted arguments work
  x <- rlang::ensym(x)
  y <- rlang::ensym(y)

  # which test to run?
  if (nlevels(data %>% dplyr::pull({{ x }})) == 2L) {
    .f <- fun_t
  } else {
    .f <- fun_anova
  }

  # executing the appropriate function
  rlang::exec(
    .fn = .f,
    data = data,
    x = x,
    y = y
  )
}

# using the meta-function
fun_meta(mtcars, am, wt)
#> Only strings can be converted to symbols

fun_meta(mtcars, "am", "wt")
#> Only strings can be converted to symbols

Но, похоже, это не работает. Есть идеи о том, что я здесь делаю неправильно и как заставить это работать?


person Indrajeet Patil    schedule 02.08.2019    source источник
comment
Я получаю ту же ошибку, если пытаюсь передать rlang::ensym(variable) прямо в вашу функцию t.test: fun_t(mtcars, rlang::ensym(am), wt). Возможно, проблема связана с использованием rlang::ensym() в мета-функции? У меня нет решения, но с этого можно начать.   -  person aosmith    schedule 02.08.2019
comment
Я мог бы догадаться. Попытка использовать as_string() в вашей мета-функции, чтобы превратить аргументы в строки для передачи в выбранную функцию: rlang::exec(.fn = .f,data = data,x = as_string(x), y = as_string(y))   -  person aosmith    schedule 02.08.2019


Ответы (1)


Похоже, проблема связана с передачей того, что составляло, например, x = rlang::ensym(am) вашим индивидуальным функциям через rlang::exec() в вашей мета-функции.

Функция ensym() принимает только строки или символы, поэтому это привело к появлению сообщения об ошибке. Учитывая это, преобразование ваших x и y аргументов в строки должно помочь.

Итак, мета-функция может быть:

fun_meta <- function(data, x, y) {
     # make sure both quoted and unquoted arguments work
     x <- rlang::ensym(x)
     y <- rlang::ensym(y)

     # which test to run?
     if (dplyr::n_distinct(data %>% dplyr::pull({{ x }})) == 2L) {
          .f <- fun_t
     } else {
          .f <- fun_anova
     }

     # executing the appropriate function
     rlang::exec(
          .fn = .f,
          data = data,
          x = rlang::as_string(x),
          y = rlang::as_string(y)
     )
}

(Я переключился на n_distinct() с nlevels, потому что am и cyl не являются факторами, и поэтому я не получил правильных результатов для сравнения с вашими исходными результатами.)

Теперь работают как простые символы, так и строки:

fun_meta(mtcars, am, wt)
    # A tibble: 1 x 10
  estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high
     <dbl>     <dbl>     <dbl>     <dbl>   <dbl>     <dbl>    <dbl>     <dbl>
1     1.36      3.77      2.41      5.49 6.27e-6      29.2    0.853      1.86
# ... with 2 more variables: method <chr>, alternative <chr>
> fun_meta(mtcars, "am", "wt")

fun_meta(mtcars, "am", "wt")
# A tibble: 1 x 10
  estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high
     <dbl>     <dbl>     <dbl>     <dbl>   <dbl>     <dbl>    <dbl>     <dbl>
1     1.36      3.77      2.41      5.49 6.27e-6      29.2    0.853      1.86
# ... with 2 more variables: method <chr>, alternative <chr>
person aosmith    schedule 02.08.2019