Динамически предоставлять аргумент для функции внутри 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)))