Зачем мне нужно заключать в кавычки точки в кавычках при передаче их в сопоставленную функцию в purrr? - PullRequest
2 голосов
/ 04 марта 2020

Как я понимаю, обычно вам не нужно заключать в кавычки или заключать в кавычки точки, когда они не изменяются (например, путем изменения их имен). Однако в этом примере создается впечатление, что я не совсем понимаю, как это работает.

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

library(tidyverse)
dots_fun <- function(df, foo, ...) {
  df %>%
    mutate(foo = foo) %>%
    nest(data = -c(...))
}

dots_fun(mtcars, "a", cyl)
#> # A tibble: 3 x 2
#>     cyl data              
#>   <dbl> <list>            
#> 1     6 <tibble [7 × 11]> 
#> 2     4 <tibble [11 × 11]>
#> 3     8 <tibble [14 × 11]>

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

list_of_foos <- c("a", "b")

mapping_fun1 <- function(df, foos, ...) {
  map(
    .x = foos,
    .f = ~ dots_fun(df = df, foo = .x, ...)
  )
}

mapping_fun1(mtcars, foos = list_of_foos, cyl)
#> Error: Can't subset columns that don't exist.
#> x The column `a` doesn't exist.

Также не помогает, если я просто перемещаю точки за пределы анонимной функции. Он больше не содержит ошибок, но не может вкладываться в cyl, как хотелось бы.

mapping_fun2 <- function(df, foos, ...) {
  map(
    .x = foos,
    .f = ~ dots_fun(df = df, foo = .x),
    ...
  )
}
mapping_fun2(mtcars, foos = list_of_foos, cyl)
#> [[1]]
#> # A tibble: 1 x 1
#>   data              
#>   <list>            
#> 1 <tibble [32 × 12]>
#> 
#> [[2]]
#> # A tibble: 1 x 1
#>   data              
#>   <list>            
#> 1 <tibble [32 × 12]>

Мне удалось заставить его работать, склеив точки в анонимную функцию, но я не совсем понимаю, почему это было необходимо. (Вы также можете заставить его работать, изменив порядок аргументов сопоставленной функции и предоставив все аргументы через ... из map, но тогда dots_fun имеет «неправильный» порядок аргументов. сработает, если вы используете анонимную функцию в стиле function() для изменения порядка аргументов)

mapping_fun3 <- function(df, foos, ...) {
  dots <- enquos(...)
  map(
    .x = foos,
    .f = ~ dots_fun(df = df, foo = .x, !!!dots)
  )
}

mapping_fun3(mtcars, foos = list_of_foos, cyl)
#> [[1]]
#> # A tibble: 3 x 2
#>     cyl data              
#>   <dbl> <list>            
#> 1     6 <tibble [7 × 11]> 
#> 2     4 <tibble [11 × 11]>
#> 3     8 <tibble [14 × 11]>
#> 
#> [[2]]
#> # A tibble: 3 x 2
#>     cyl data              
#>   <dbl> <list>            
#> 1     6 <tibble [7 × 11]> 
#> 2     4 <tibble [11 × 11]>
#> 3     8 <tibble [14 × 11]>

Мой вопрос: в каких условиях / ситуациях вам нужно заключать в кавычки и кавычки ... для передачи их безопасно через функции? и как это условие применяется здесь?

1 Ответ

2 голосов
/ 04 марта 2020

Я думаю, что ваша проблема в том, что вам нужно пройти ... через каждый уровень вызова функции. Таким образом, ... оба должны пройти через map(), а также вашу внутреннюю функцию.

Я не смог заставить ваш пример работать с nest(), поэтому я сделал версию, которая использует select() вместо

dots_fun <- function(df, foo, ...) {
  df %>%
    mutate(foo = foo) %>%
    select(...)
}

А потом, кажется, вы не можете на самом деле используйте синтаксис as_mapper с ... и нестандартную оценку с помощью этой проблемы github , поэтому вам нужно явно создать анонимную функцию, чтобы повторное значение не передавалось второй раз в ... ценности также. Хэдли сказал, синтаксис ~ предназначен только для "простых" функций, а не для тех, которые .... Таким образом, работающая функция отображения может выглядеть следующим образом:

mapping_fun1 <- function(df, foos, ...) {
  map(
    .x = foos,
    .f = function(x, ...) dots_fun(df = df, foo = x, ...),
    ...
  )
}
mapping_fun1(mtcars, foos = list_of_foos, cyl, gear)

Мы передаем ... через map() через нашу анонимную функцию и, наконец, в dots_fun. Если вы в любой момент порвете эту цепь, она развалится.

...