R: почему group_by все еще требует "do" даже при использовании кавычек

Как заставить пользовательскую функцию хорошо работать с pipe и group_by? Вот простая функция:

 library(tidyverse)

 fun_head <- function(df, column) {
 column <- enquo(column)
 df %>% select(!!column) %>% head(1)
 }

Функция прекрасно работает с каналами и позволяет фильтровать по другому столбцу:

 mtcars %>% filter(cyl == 4) %>% fun_head(mpg)

 >    mpg
   1 22.8

Тем не менее, тот же конвейер не работает с group_by

mtcars %>% group_by(cyl) %>% fun_head(mpg)

Adding missing grouping variables: `cyl`
# A tibble: 1 x 2
# Groups:   cyl [1]
     cyl   mpg
     <dbl> <dbl>
1     6    21

Использование "do" после group_by заставляет его работать:

 > mtcars %>% group_by(cyl) %>% do(fun_head(., mpg))
 # A tibble: 3 x 2
 # Groups:   cyl [3]
    cyl   mpg
   <dbl> <dbl>
1     4  22.8
2     6  21  
3     8  18.7

Как изменить функцию так, чтобы она работала равномерно с фильтрами и group_by без необходимости делать?
Или предложения не имеют ничего общего с вопросом, а group_by просто требует использования "do", потому что функция в примере имеет несколько аргументов?

2 ответа

Решение

Это не зависит от предложений. Вот такая же проблема при отсутствии нестандартной оценки в fun_head():

fun_head <- function(df) {df %>% select(mpg) %>% head(1)}
mtcars %>% group_by( cyl ) %>% fun_head()
# Adding missing grouping variables: `cyl`
# # A tibble: 1 x 2
# # Groups:   cyl [1]
#     cyl   mpg
#   <dbl> <dbl>
# 1     6    21

Как объяснено в других вопросах здесь и здесь, do это соединитель, который позволяет применять произвольные функции к каждой группе. Причина dplyr глаголы, такие как mutate а также filter не требует do потому что они обрабатывают сгруппированные кадры данных внутри как особые случаи (см., например, реализацию mutate). Если вы хотите, чтобы ваша собственная функция эмулировала это поведение, вам необходимо различать сгруппированные и не сгруппированные фреймы данных:

fun_head2 <- function( df )
{
  if( !is.null(groups(df)) )
    df %>% do( fun_head2(.) )
  else
    df %>% select(mpg) %>% head(1)
}

mtcars %>% group_by(cyl) %>% fun_head2()
# # A tibble: 3 x 2
# # Groups:   cyl [3]
#     cyl   mpg
#   <dbl> <dbl>
# 1     4  22.8
# 2     6  21  
# 3     8  18.7

Как вы уже написали, функция выбирает column от df, затем принимает head, который является первым рядом df (head не является функцией Tidyverse и не знает ни о какой группировке). dplyr::slice(1) занимает первый ряд каждой группы, что вы хотите. Ты можешь использовать

 fun_head <- function(df, column) {
 column <- enquo(column)
 df %>% slice(1) %>% select(!!column)
 }

 mtcars %>% group_by(cyl) %>% fun_head(mpg)

# # A tibble: 3 x 2
# # Groups:   cyl [3]
#     cyl   mpg
#   <dbl> <dbl>
# 1     4  22.8
# 2     6  21  
# 3     8  18.7
Другие вопросы по тегам