разница между !!и eval_tidy в mutate_at - PullRequest
0 голосов
/ 22 февраля 2019

Я изучаю tidyeval семантику от a число из источников , но я получаю результат, который яне могу объяснить.

Я использую mutate_at и case_when для преобразования некоторых переменных путем (1) извлечения их имен с помощью кавычек, (2) изменения их имен с использованием gsub и (3) ссылки на данные, связанные с измененными именами.

В моем минимальном примере я создаю foo$c как преобразование foo$b, которое должно просто принимать значение из foo$a.Шаги (1) и (2) кажутся простыми:

library(tidyverse)
library(rlang)

foo <- data.frame(a = 1, b = 2)

foo %>%
  mutate_at(vars(c = b),
            funs(case_when(
              TRUE ~ gsub("b", "a", as_name(quo(.)))
            )))
#>   a b c
#> 1 1 2 a

foo$c содержит правильное имя переменной, на которую мы хотим посмотреть.Я понимаю, что мне нужно преобразовать строку в symbol, используя sym(), а затем оценить ее.Если бы я использовал простые mutate(), !! и sym(), они работают нормально:

foo %>%
  mutate(c := !!sym(gsub("b", "a", as_name(quo(b)))))
#>   a b c
#> 1 1 2 1

Но когда я делаю это внутри mutate_at(case_when()), я не получаю правильный результат:

foo %>%
  mutate_at(vars(c = b),
            funs(case_when(
              TRUE ~ !!sym(gsub("b", "a", as_name(quo(.))))
            )))
#>   a b c
#> 1 1 2 2

Чтобы увидеть, что происходит, я сделал простую функцию печати.Без !! из распечатки выглядит так, как будто gsub() и sym() дают ожидаемые результаты:

look <- function(x) {
  print(x)
  print(typeof(x))
  return(x)
}

foo %>%
  mutate_at(vars(c = b),
            funs(case_when(
              TRUE ~ look(sym(look(gsub("b", "a", as_name(quo(.))))))
            )))
#> [1] "a"
#> [1] "character"
#> a
#> [1] "symbol"
#> Error in mutate_impl(.data, dots): Evaluation error: object of type 'symbol' is not subsettable.

Как только я поставил !! впереди, распечатка, кажется, показывает, что мывы получаете другой результат для gsub() и sym():

foo %>%
  mutate_at(vars(c = b),
            funs(case_when(
              TRUE ~ !!(look(sym(look(gsub("b", "a", as_name(quo(.)))))))
            )))
#> [1] "."
#> [1] "character"
#> .
#> [1] "symbol"
#> [1] "."
#> [1] "character"
#> .
#> [1] "symbol"
#>   a b c
#> 1 1 2 2

Я не понимаю, как добавление !! может изменить результат по сравнению с вложенным sym(gsub()).Добавление новой операции в конец не должно изменить предыдущий / внутренний результат.Я читал, что !! - это «не вызов функции, а синтаксическая операция», но я не совсем понимаю это различие или то, как это может изменить результат.

Использование eval_tidy вместо !!, кажется, работает нормально, хотя я не могу объяснить, почему:

foo %>%
  mutate_at(vars(c = b),
            funs(case_when(
              TRUE ~ eval_tidy(look(sym(look(gsub("b", "a", as_name(quo(.)))))))
            )))
#> [1] "a"
#> [1] "character"
#> a
#> [1] "symbol"
#>   a b c
#> 1 1 2 1

1 Ответ

0 голосов
/ 22 февраля 2019

Всего несколько комментариев:

(a) Глаголы в области видимости в настоящее время работают со странной заменой местоимения ..Мы движемся к использованию функций (или списков), которые должны работать более принципиально.Я предлагаю использовать функции или purrr лямбды вместо funs(), это должно устранить некоторые странности.

(b) Оператор !! полностью связан с синхронизацией.В вашем случае это funs(), который обрабатывает его в своем непосредственном контексте, прежде чем произойдет подстановка.

(c) При использовании вариантов с областью видимости лучше всего забыть о tidy eval и думать с точки зрения функций отображения, Это означает, что у вас нет доступа к имени столбца, который в настоящее время отображается. Мы могли бы добавить это в качестве функции в будущем, но на данный момент лучше избегать обходных путей.

См. Также https://github.com/tidyverse/dplyr/issues/4199 для недавнего связанного обсуждения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...