Как параметризовать вызовы функций в dplyr 0.7?
Выпуск dplyr 0.7 включает в себя капитальный ремонт программирования с помощью dplyr. Я внимательно прочитал этот документ, и я пытаюсь понять, как это повлияет на мое использование dplyr.
Вот общая идиома, которую я использую при построении функций отчетности и агрегации с помощью dplyr:
my_report <- function(data, grouping_vars) {
data %>%
group_by_(.dots=grouping_vars) %>%
summarize(x_mean=mean(x), x_median=median(x), ...)
}
Здесь grouping_vars
- вектор строк.
Мне нравится эта идиома, потому что я могу передавать строковые векторы из других мест, скажем, файл или интерактивный пользовательский интерфейс Shiny app, но это также не так уж плохо для интерактивной работы.
Однако в новом программировании с dplyr vignette я не вижу примеров того, как что-то подобное можно сделать с помощью нового dplyr. Я вижу только примеры того, как передаваемые строки больше не являются правильным подходом, и вместо этого я должен использовать quosures.
Я рад принять quosures, но как именно я получаю от строк до quosures, ожидаемых здесь dplyr? Не представляется возможным ожидать, что вся экосистема R обеспечит quosures dplyr - много раз мы собираемся получить строки, и они должны быть преобразованы.
Вот пример, показывающий, что вы теперь должны делать, и как моя старая идиома не работает:
library(dplyr)
grouping_vars <- quo(am)
mtcars %>%
group_by(!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
#> # A tibble: 2 × 2
#> am mean_cyl
#> <dbl> <dbl>
#> 1 0 6.947368
#> 2 1 5.076923
grouping_vars <- "am"
mtcars %>%
group_by(!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
#> # A tibble: 1 × 2
#> `"am"` mean_cyl
#> <chr> <dbl>
#> 1 am 6.1875
Ответы
Ответ 1
dplyr
будет иметь специализированную функцию group_by group_by_at
для обработки нескольких переменных группировки. Было бы намного проще использовать новый член семейства _at
:
# using the pre-release 0.6.0
cols <- c("am","gear")
mtcars %>%
group_by_at(.vars = cols) %>%
summarise(mean_cyl=mean(cyl))
# Source: local data frame [4 x 3]
# Groups: am [?]
#
# am gear mean_cyl
# <dbl> <dbl> <dbl>
# 1 0 3 7.466667
# 2 0 4 5.000000
# 3 1 4 4.500000
# 4 1 5 6.000000
Аргумент .vars
принимает имена символов/числовых векторов или столбцов, сгенерированные с помощью vars
:
.vars
Список столбцов, генерируемых vars(), или символьный вектор имена столбцов или числовой вектор столбцов.
Ответ 2
Вот быстрая и грязная ссылка, которую я написал для себя.
# install.packages("rlang")
library(tidyverse)
dat <- data.frame(cat = sample(LETTERS[1:2], 50, replace = TRUE),
cat2 = sample(LETTERS[3:4], 50, replace = TRUE),
value = rnorm(50))
Представление имен столбцов со строками
Преобразование строк в объекты символа с помощью rlang::sym
и rlang::syms
.
summ_var <- "value"
group_vars <- c("cat", "cat2")
summ_sym <- rlang::sym(summ_var) # capture a single symbol
group_syms <- rlang::syms(group_vars) # creates list of symbols
dat %>%
group_by(!!!group_syms) %>% # splice list of symbols into a function call
summarize(summ = sum(!!summ_sym)) # slice single symbol into call
Если вы используете !!
или !!!
вне функций dplyr
, вы получите сообщение об ошибке.
Использование rlang::sym
и rlang::syms
идентично внутри функций.
summarize_by <- function(df, summ_var, group_vars) {
summ_sym <- rlang::sym(summ_var)
group_syms <- rlang::syms(group_vars)
df %>%
group_by(!!!group_syms) %>%
summarize(summ = sum(!!summ_sym))
}
Затем мы можем вызвать summarize_by
со строковыми аргументами.
summarize_by(dat, "value", c("cat", "cat2"))
Использование нестандартной оценки для имен столбцов/переменных
summ_quo <- quo(value) # capture a single variable for NSE
group_quos <- quos(cat, cat2) # capture list of variables for NSE
dat %>%
group_by(!!!group_quos) %>% # use !!! with both quos and rlang::syms
summarize(summ = sum(!!summ_quo)) # use !! both quo and rlang::sym
Внутренние функции используют enquo
, а не quo
. quos
в порядке, хотя!?
summarize_by <- function(df, summ_var, ...) {
summ_quo <- enquo(summ_var) # can only capture a single value!
group_quos <- quos(...) # captures multiple values, also inside functions!?
df %>%
group_by(!!!group_quos) %>%
summarize(summ = sum(!!summ_quo))
}
И тогда наш вызов функции
summarize_by(dat, value, cat, cat2)
Несколько более чистая и приятная версия этого написана здесь.
Ответ 3
Если вы хотите сгруппировать, возможно, более одного столбца, вы можете использовать quos
grouping_vars <- quos(am, gear)
mtcars %>%
group_by(!!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
# am gear mean_cyl
# <dbl> <dbl> <dbl>
# 1 0 3 7.466667
# 2 0 4 5.000000
# 3 1 4 4.500000
# 4 1 5 6.000000
Прямо сейчас, похоже, нет отличного способа превратить строки в quos. Здесь один из способов, который работает, хотя
cols <- c("am","gear")
grouping_vars <- rlang::parse_quosures(paste(cols, collapse=";"))
mtcars %>%
group_by(!!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
# am gear mean_cyl
# <dbl> <dbl> <dbl>
# 1 0 3 7.466667
# 2 0 4 5.000000
# 3 1 4 4.500000
# 4 1 5 6.000000