Использование dplyr `enquo` после вызова аргумента

У меня есть функция, которая примерно соответствует этой структуре:

TestFunc <- function(dat, point) {
    if (!(point %in% c("NW", "SE", "SW"))) 
        stop("point must be one of 'SW', 'SE', 'NW'")

    point <- enquo(point)

    return(dat %>% filter(point == !!point))

Проблема в том, что я получаю следующую ошибку, когда включаю проверку значений:

Error in (function (x, strict = TRUE)  : 
  the argument has already been evaluated

Ошибка исчезает при удалении чека. Как я могу сохранить оба?

3 ответа

Что нужно помнить об инфраструктуре Quosure, так это то, что это очень умный, сложный кусок кода, который отменяет другой очень умный, сложный кусок кода, чтобы вернуть вас туда, откуда вы начали. То, что вы хотите, можно достичь очень простым способом, не переходя в NSE и не возвращаясь снова.

TestFunc <- function(dat, point)
{
    if(!(point %in% c("NW", "SE", "SW")))
        stop("point must be one of 'SW', 'SE', 'NW'")
    dat[dat$point == point, ]
}

(Разница между этим и использованием match.arg, как @Frank предлагает в комментарии, это то, что match.arg будет использовать первое значение в качестве значения по умолчанию, если ввод не предоставлен, тогда как это не удастся.)

Если вы хотите вызвать другие глаголы dplyr/tidyverse, просто сделайте это после фильтрации строк.

По техническим причинам, связанным с тем, как R оптимизирует код, вы можете захватывать только аргументы, которые еще не были оценены.

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

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

оценивать point так filter может сказать разницу между аргументом и столбцом данных

У aosmith есть хорошая идея, поэтому я приведу ее в форме ответа вместе с воспроизводимым примером:

f <- function(dat, point) {
  if (!(point %in% c("NW", "SE", "SW")))
    stop("point must be one of 'SW', 'SE', 'NW'")

  filter(dat, point == !!point)
}

tbl <- tibble(point = c('SE', 'NW'))
f(tbl, 'SE')
f(tbl, 'XX')

Если вы проходите point в виде строки, вам нужно только дифференцировать аргумент point (В данном случае = "SE") и столбец dat$point (или же tbl$point, в зависимости от того, находитесь ли вы внутри или вне функции). Из документа программирования dplyr:

В дплыр (и в тидыевал вообще) ты используешь!! чтобы сказать, что вы хотите снять кавычки для ввода, чтобы он оценивался, а не цитировался.

Вы хотите оценить аргумент point так что "SE" передается filter, сюда filter могу отличить колонку point и аргумент SE,

(Я также пытался использовать filter(dat, .data$point == point), но RHS point по-прежнему относится к колонке, а не к f аргумент.)

Я надеюсь, что этот пример поможет с вашим реальным кодом.

Другие вопросы по тегам