Как параметризовать вызовы функций в 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, но это также не так уж плохо для интерактивной работы.

Однако в новом программировании с использованием dplyr vignette я не вижу примеров того, как что-то подобное можно сделать с помощью нового dplyr. Я вижу только примеры того, как передача строк больше не является правильным подходом, и вместо этого я должен использовать кавычки.

Я рад принять кавычки, но как мне перейти от строк к квестам, ожидаемым здесь dplyr? Кажется нецелесообразным ожидать, что вся экосистема R предоставит предложения для 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

3 ответа

Решение

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(), или символьный вектор имен столбцов, или числовой вектор позиций столбцов.

Вот быстрый и грязный справочник, который я написал для себя.

# 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)

Если вы хотите сгруппировать по нескольким столбцам, вы можете использовать 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

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

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
Другие вопросы по тегам