R: почему group_by все еще требует "do" даже при использовании кавычек - PullRequest
0 голосов
/ 21 октября 2018

Как заставить пользовательскую функцию хорошо работать с 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 без необходимости использовать «do»?
Или предложения не имеют ничего общего с вопросом, а group_by просто требует использования «do», потому что функция в примере имеет несколько аргументов?

Ответы [ 2 ]

0 голосов
/ 21 октября 2018

Это не зависит от предложений.Вот та же проблема в отсутствии нестандартной оценки в 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

РЕДАКТИРОВАТЬ: Я хочу указать, что другая альтернатива group_by + do - вместо этого использовать tidyr::nest + purrr::map.Возвращаясь к исходному определению функции, которое принимает два аргумента:

fhead <- function(.df, .var) { .df %>% select(!!ensym(.var)) %>% head(1) }

Следующие две цепочки эквивалентны (вплоть до упорядочения строк, поскольку group_by сортирует по переменной группировки, а nest не делает 'т):

# Option 1: group_by + do
mtcars %>% group_by(cyl) %>% do( fhead(., mpg) ) %>% ungroup

# Option 2: nest + map
mtcars %>% nest(-cyl) %>% mutate_at( "data", map, fhead, "mpg" ) %>% unnest
0 голосов
/ 21 октября 2018

Как вы уже написали, функция выбирает column из df, затем принимает head, что является первой строкой df (head не является функцией обратного хода и не являетсяв курсе любой группировки).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
...