Динамически предоставлять аргумент для функции внутри mutate

Прежде всего - мои извинения, если об этом уже спрашивали, я посмотрел и не смог найти ничего, что соответствует тому, что я пытаюсь сделать.

Я пытаюсь создать функцию, которая связывает данные в соответствии с пользовательским столбцом во фрейме данных. Для этого я использую функцию mutate() из dplyr и cut() из базы R. Однако я не могу понять, как использовать имя столбца, которое передается через функцию внутри функции cut() (которая появляется внутри мутировать).

Я провел несколько часов, просматривая это и это, но все еще не понял это. Насколько я понимаю, foo(), bar() и последняя строка в приведенном ниже коде должны давать одинаковый результат. Тем не менее, я получаю две ошибки для функций, и та, которая не заключена в функцию и просто использует жестко закодированное имя столбца, работает нормально.

Что тут происходит? Почему foo () производит вывод, отличный от bar()? И как правильно использовать lazyeval, чтобы разрешить правильное поведение в функции?

library(dplyr)
library(lazyeval)

foo <- function(data, col, bins){
    by = lazyeval::interp(quote(x), x = as.name(col))
    print(paste0("typeof(by): ", typeof(by)))
    print(paste0(" by: ", by))

    df <- data %>%
      dplyr::mutate(bins = cut(by,
        breaks = bins,
        dig.lab = 5,
        include.lowest = T))
    df
}

bar <- function(data, col, bins){
  df <- data %>%
    dplyr::mutate(bins = cut(lazyeval::interp(quote(x), x = as.name(col)),
      breaks = bins,
      dig.lab = 5,
      include.lowest = T))
  df
}

#produce sample data and bins list
df <- expand.grid(temp=0:8,precip=seq(0.7,1.3,by=0.1))
df$rel <- seq(40,100,length=63)
bins <- seq(40,100,by=10)

foo(df,"rel",bins) # produces "Error: 'rel' not found"
bar(df,"rel",bins) # produces "Error: 'x' must be numeric"

# but this works
dplyr::mutate(df, bins = cut(rel, breaks = bins, dig.lab = 5, include.lowest = T))

1 ответ

Решение

Как отметил @aosmith в своем комментарии, решение заключается в использовании mutate_(bins = interp(~cut(x, bins, dig.lab = 5, include.lowest = TRUE), x = as.name(col))), С помощью mutate_ вместо mutate позволяет нам использовать стандартную оценку.

Проще всего увидеть, что происходит interp а также cut если мы позвоним interp вне mutate_, (Это выполняется в любом случае.) Предполагая, col == "rel",

call = interp(~cut(x, bins, dig.lab = 5, include.lowest = TRUE), x = as.name(col))) 

дам

~cut(rel, bins, dig.lab = 5, include.lowest = TRUE)

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

muatate_(bins = call)

Дает правильный результат.

Вы также можете разрешить пользователю указывать имя столбца, которое заменяет "корзины":

dplyr::mutate_(.dots = setNames(call, c(binName)))
Другие вопросы по тегам