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