Есть ли ограничения на аккуратный объем оценки?

Я пытаюсь использовать аккуратную оценку, как определено в dplyr 0.7.0 .

Однако во время вызова функции в mutate() я получаю сообщение об ошибке. Похоже, что переменные не оцениваются, как я ожидал.

library(dplyr)
library(tibble)
library(tidyr)

myCor <- function(col1, col2) {
  col1 <- enquo(col1)
  col2 <- enquo(col2)

  mtcars %>%
    rownames_to_column("vehicle") %>%
    select(vehicle, !!col1, !!col2) %>%
    mutate(correlation=cor(!!col1, !!col2))
}

myCor("mpg", "disp")
# Error in mutate_impl(.data, dots) : 
#   Evaluation error: 'x' must be numeric.

Вместо этого я должен использовать этот неаккуратный синтаксис eval, чтобы получить желаемый результат.

myCor <- function(col1, col2) {
  col1_tidy <- enquo(col1)
  col2_tidy <- enquo(col2)

  mtcars %>%
    rownames_to_column("vehicle") %>%
    select(vehicle, !!col1_tidy, !!col2_tidy) %>%
    mutate(correlation=cor(eval(parse(text=col1)), eval(parse(text=col2))))
}

myCor("mpg", "disp")
# vehicle  mpg  disp correlation
# 1            Mazda RX4 21.0 160.0  -0.8475514
# 2        Mazda RX4 Wag 21.0 160.0  -0.8475514
# 3           Datsun 710 22.8 108.0  -0.8475514
# 4       Hornet 4 Drive 21.4 258.0  -0.8475514
# ...

Есть ли способ использовать аккуратную оценку в этом примере?


person Megatron    schedule 11.05.2018    source источник
comment
Вызовите свою первую функцию с этим myCor(mpg, disp) (кавычки не нужны, это самое интересное с nse :))   -  person kath    schedule 11.05.2018
comment
Сравните myCor(mpg, disp) с myCor("mpg", "disp") Цитаты имеют большое значение. С ними у вас есть значение символа, без них у вас есть символы. NSE обычно предполагает, что у вас есть символы.   -  person MrFlick    schedule 11.05.2018
comment
Если вам действительно нужно преобразовать строки в символьные значения в символы, лучше использовать as.name() или rlang::sym(), а не parse(text=))   -  person MrFlick    schedule 11.05.2018
comment
Спасибо. Я вижу, что цитирование имеет значение, когда у меня есть вызов функции cor(). Но если вы опустите строку mutate(), вызов select() будет вести себя идентично со строками или выражениями NSE. Почему это?   -  person Megatron    schedule 11.05.2018
comment
select может обрабатывать строки как входные данные, mutate не может - он ожидает измененных имен столбцов. select там более гибкий   -  person kath    schedule 11.05.2018
comment
rlang::sym() может обрабатывать как строки, так и пустые имена столбцов, что является приятным бонусом по сравнению с quo и enquo. Похоже, что select использует sym или его форму множественного числа syms, а mutate использует enquo.   -  person Nate    schedule 11.05.2018


Ответы (2)


Используйте ensym вместо enquo, если вы хотите передавать строки в качестве аргументов ...

library(dplyr)
library(rlang)
library(tibble)

myCor <- function(col1, col2) {
  col1 <- ensym(col1)
  col2 <- ensym(col2)

  mtcars %>%
    rownames_to_column("vehicle") %>%
    select(vehicle, !!col1, !!col2) %>%
    mutate(correlation=cor(!!col1, !!col2))
}

# both of these will work now
myCor("mpg", "disp")
myCor(mpg, disp)
person CJ Yetman    schedule 12.05.2018
comment
Версия CRAN dplyr импортирует rlang, поэтому я не думаю, что это технически необходимо - person CJ Yetman; 13.05.2018

Из виньетки Программирование с dplyr:

Большинство аргументов dplyr не являются ссылочно прозрачными. Это означает, что вы не можете заменить значение кажущимся эквивалентным объектом, который вы определили в другом месте.

Следовательно, вам необходимо передать в функцию имена столбцов без кавычек, так как тогда с enquo среда захватывается правильно, а !! возвращает имена столбцов без кавычек, как mutate ожидает.

Чтобы превратить обычный mutate-вызов в функцию с нестандартной оценкой, может быть более интуитивно понятным начать вот так.
Сначала запишите вызов, как если бы вы делали это без функции:

mtcars %>%
    rownames_to_column("vehicle") %>%
    select(vehicle, mpg, disp) %>%
    mutate(correlation = cor(mpg, disp))

Это работает (и вызовет ошибку при использовании кавычек mpg и disp!).
Теперь вытащите переменные, которые вы хотите изменить перед вызовом, и замените их:

col1 <- quo(mpg)
col2 <- quo(disp)

mtcars %>%
  rownames_to_column("vehicle") %>%
  select(vehicle, !!col1, !!col2) %>%
  mutate(correlation=cor(!!col1, !!col2))

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

myCor <- function(var1, var2) {
  col1 <- enquo(var1)
  col2 <- enquo(var2)

  mtcars %>%
    rownames_to_column("vehicle") %>%
    select(vehicle, !!col1, !!col2) %>%
    mutate(correlation=cor(!!col1, !!col2))
}

Я использовал разные имена для аргументов функции, а затем "цитируемый" объект (с enquo), чтобы сделать различие более ясным, но, конечно, он также работает с col1 и col2.

person kath    schedule 11.05.2018