rlang :: sym в анонимных функциях

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

require(tidyverse)
data <- tibble(x1 = letters[1:3],
               x2 = letters[4:6],
               val = 1:3)

get_it <- function(a, b){
    data %>%
        mutate(y1 = !!rlang::sym(a)) %>%
        mutate(y2 = !!rlang::sym(b)) %>%
        select(y1, y2, val)
}
get_it("x1", "x2")

Это определяет некоторые данные игрушек и (ужасную) функцию, которая по существу переименовывает столбцы на основе имен столбцов. Теперь я могу сделать то же самое для разных комбинаций a и b:

d <- tibble(x = c("x1", "x2"),
            y = c("x2", "x1"))
d %>% mutate(tmp = map2(x, y, get_it))

Однако, если я попытаюсь сделать то же самое с анонимной функцией, это не сработает:

d %>% mutate(tmp = map2(x, y, function(a, b){
data %>%
    mutate(y1 = !!rlang::sym(a)) %>%
    mutate(y2 = !!rlang::sym(b)) %>%
    select(y1, y2, val)
}))

Это не удается, поскольку object 'a' not found хотя функции точно такие же, как раз здесь, анонимно. Может ли кто-нибудь объяснить, почему?

Ответы

Ответ 1

Обратите внимание, что следующий код имеет rlang::sym() внутри анонимной функции и работает по назначению:

map2(c("x1","x2"), c("x2","x1"), function(a, b){
  data %>%
    mutate(y1 = !!rlang::sym(a)) %>%
    mutate(y2 = !!rlang::sym(b)) %>%
    select(y1, y2, val)})

Оригинальная проблема связана с неуверенным оператором !! и его приоритет относительно создания анонимной функции внутри функций нестандартной оценки (NSE), таких как mutate. Рассмотрим вложенный фрейм данных

XX <- data_frame( dfs = list(
  data_frame( x = letters[1:3], y = 1:3 ),
  data_frame( x = letters[4:6], y = 4:6 )) )

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

XX %>% mutate( dfs1 = map( dfs, function(df) {
  i <- 1
  df %>% select(!!i)} ))

Я получаю object 'i' not found ошибку, потому что !! unquoting происходит относительно внешнего mutate, прежде чем создается среда для анонимной функции (содержащей select). Если я переведу анонимную функцию вне mutate

map( XX$dfs, function(df) {
  i <- 1
  df %>% select(!!i)} )

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


Страница помощи !! говорится, что

Привет! оператор не подтверждает свой аргумент. Он немедленно оценивается в окружающем контексте.

Это означает, что когда вы пишете сложное выражение NSE, например select внутри mutate, unquoting будет происходить в среде выражения в целом. Как отмечается в комментариях @lionel в комментариях, unquoting в NSE имеет приоритет над другими вещами, такими как создание анонимных функциональных сред.