[Перевод] Агрегирующие функции в dplyr

summarise() используется с агрегирующими функциями, которые принимают на вход вектор значений, а возвращают одно. Функция summarise_each() предлагает другой подход к summarise() с такими же результатами.

Цель этой статьи — сравнить поведение summarise() и summarise_each(), учитывая два фактора, которыми мы можем управлять:

1. Сколькими переменными оперировать

  • 1А, одна переменная
  • 1В, более одной переменной


2. Сколько функций применять к каждой переменной

  • 2А, одна функция
  • 2В, более одной функции


Получается четыре варианта:

  • Вариант 1: применить одну функцию к одной переменной
  • Вариант 2: применить много функций к одной переменной
  • Вариант 3: применить одну функцию к многим переменным
  • Вариант 4: применить много функций к многим переменным


Также проверим эти четыре случая с и без опции group_by().

Пакет данных mtcars


Для этой статьи мы используем хорошо известный пакет данных mtcars.

Сначала мы преобразуем его в объект tbl_df. Со стандартным объектом data.frame ничего не произойдет, зато будет доступен гораздо лучший метод вывода.

Наконец, для того, чтобы было легко ориентироваться, выделим только четыре переменных, с которыми будем работать:

mtcars <- mtcars   %>% 
  tbl_df() %>% 
  select(cyl , mpg, disp)


Вариант 1: применить одну функцию к одной переменной


В этом случае summarise() выдаст простой результат:

# без группировки
mtcars %>% 
  summarise (mean_mpg = mean(mpg))

## Source: local data frame [1 x 1]
## 
##   mean_mpg
##      (dbl)
## 1 20.09062

# с группировкой
mtcars %>% 
  group_by(cyl) %>% 
  summarise (mean_mpg = mean(mpg))

## Source: local data frame [3 x 2]
## 
##     cyl mean_mpg
##   (dbl)    (dbl)
## 1     4 26.66364
## 2     6 19.74286
## 3     8 15.10000


Можно было использовать и функцию summarise_each(), но ее использование менее обоснованно с точки зрения понятности кода.

# без группировки
mtcars %>% 
  summarise_each (funs(mean) , mean_mpg = mpg)

## Source: local data frame [1 x 1]
## 
##   mean_mpg
##      (dbl)
## 1 20.09062

# с группировкой
mtcars %>% 
  group_by(cyl) %>% 
  summarise_each (funs(mean) , mean_mpg = mpg)

## Source: local data frame [3 x 2]
## 
##     cyl mean_mpg
##   (dbl)    (dbl)
## 1     4 26.66364
## 2     6 19.74286
## 3     8 15.10000


Вариант 2: применить много функций к одной переменной


В этом случае можно применить обе функции, и summarise(), и summarise_each().

У функции summarise() более интуитивно понятный синтаксис:

# без группировки
mtcars %>% 
  summarise (min_mpg = min(mpg), max_mpg = max(mpg))

## Source: local data frame [1 x 2]
## 
##   min_mpg max_mpg
##     (dbl)   (dbl)
## 1    10.4    33.9

# с группировкой
mtcars %>% 
  group_by(cyl) %>% 
  summarise (min_mpg = min(mpg), max_mpg = max(mpg))

## Source: local data frame [3 x 3]
## 
##     cyl min_mpg max_mpg
##   (dbl)   (dbl)   (dbl)
## 1     4    21.4    33.9
## 2     6    17.8    21.4
## 3     8    10.4    19.2


Можно просто задавать имена выходных переменных:

max_mpg = max(mpg)


Когда к одной переменной применяется много функций, summarise_each() использует более компактный и аккуратный синтаксис:

# без группировки
mtcars %>% 
  summarise_each (funs(min, max), mpg)

## Source: local data frame [1 x 2]
## 
##     min   max
##   (dbl) (dbl)
## 1  10.4  33.9

# с группировкой
mtcars %>% 
  group_by(cyl) %>% 
  summarise_each (funs(min, max), mpg)

## Source: local data frame [3 x 3]
## 
##     cyl   min   max
##   (dbl) (dbl) (dbl)
## 1     4  21.4  33.9
## 2     6  17.8  21.4
## 3     8  10.4  19.2


Имена выходных переменных задаются именами функций: min и max. В этом случае мы теряем имя переменной, к которой применяется функция. Если нужно что-то вроде min_mpg и max_mpg, нужно переименовать функции внутри funs():

# без группировки
mtcars %>% 
  summarise_each (funs(min_mpg = min, max_mpg = max), mpg)

## Source: local data frame [1 x 2]
## 
##   min_mpg max_mpg
##     (dbl)   (dbl)
## 1    10.4    33.9

# с группировкой
mtcars %>% 
  group_by(cyl) %>% 
  summarise_each (funs(min_mpg = min, max_mpg = max), mpg)

## Source: local data frame [3 x 3]
## 
##     cyl min_mpg max_mpg
##   (dbl)   (dbl)   (dbl)
## 1     4    21.4    33.9
## 2     6    17.8    21.4
## 3     8    10.4    19.2


Вариант 3: применить одну функцию к многим переменным


Этот вариант очень похож на предыдущий. Можно использовать обе функции: и summarise(), и summarise_each().

Функция summarise() снова имеет более интуитивный синтаксис, и имена выходных переменных можно задавать в обычной простой форме:

max_mpg = max(mpg)

# без группировки
mtcars %>% 
  summarise(mean_mpg = mean(mpg), mean_disp = mean(disp))

## Source: local data frame [1 x 2]
## 
##   mean_mpg mean_disp
##      (dbl)     (dbl)
## 1 20.09062  230.7219

# с группировкой
mtcars %>% 
  group_by(cyl) %>% 
  summarise(mean_mpg = mean(mpg), mean_disp = mean(disp))

## Source: local data frame [3 x 3]
## 
##     cyl mean_mpg mean_disp
##   (dbl)    (dbl)     (dbl)
## 1     4 26.66364  105.1364
## 2     6 19.74286  183.3143
## 3     8 15.10000  353.1000


Когда ко многим переменным применяется одна функция, summarise_each() использует более компактный и аккуратный синтаксис:

# без группировки
mtcars %>% 
  summarise_each(funs(mean) , mpg, disp)

## Source: local data frame [1 x 2]
## 
##        mpg     disp
##      (dbl)    (dbl)
## 1 20.09062 230.7219

# с группировкой
mtcars %>% 
  group_by(cyl) %>% 
  summarise_each (funs(mean), mpg, disp)

## Source: local data frame [3 x 3]
## 
##     cyl      mpg     disp
##   (dbl)    (dbl)    (dbl)
## 1     4 26.66364 105.1364
## 2     6 19.74286 183.3143
## 3     8 15.10000 353.1000


Имена выходных переменных определяется именами переменных: mpg и disp. В этом случае мы теряем имя функции, примененной к переменным — mean(). Вероятно, хотелось бы что-то вроде mean_mpg и mean_disp. Для того, чтобы этого достичь, нужно соответственно переименовать переменные, передающиеся в »…» внутри summarise_each():

# без группировки
mtcars %>% 
  summarise_each(funs(mean) , mean_mpg = mpg, mean_disp = disp)

## Source: local data frame [1 x 2]
## 
##   mean_mpg mean_disp
##      (dbl)     (dbl)
## 1 20.09062  230.7219

# с группировкой
mtcars %>% 
  group_by(cyl) %>% 
  summarise_each(funs(mean) , mean_mpg = mpg, mean_disp = disp)

## Source: local data frame [3 x 3]
## 
##     cyl mean_mpg mean_disp
##   (dbl)    (dbl)     (dbl)
## 1     4 26.66364  105.1364
## 2     6 19.74286  183.3143
## 3     8 15.10000  353.1000


Вариант 4: применить много функций к многим переменным


Как и в предыдущих случаях, обе функции, и summarise(), и summarise_each(), имеют свои преимущества.

Функция summarise() снова имеет более интуитивный синтаксис, и имена выходных переменных можно задавать в обычной простой форме:

max_mpg = max(mpg)

# без группировки
mtcars %>% 
  summarise(min_mpg = min(mpg) , min_disp = min(disp), max_mpg = max(mpg) , max_disp = max(disp))

## Source: local data frame [1 x 4]
## 
##   min_mpg min_disp max_mpg max_disp
##     (dbl)    (dbl)   (dbl)    (dbl)
## 1    10.4     71.1    33.9      472

# с одной группой
mtcars %>% 
  group_by(cyl) %>% 
  summarise(min_mpg = min(mpg) , min_disp = min(disp), max_mpg = max(mpg) , max_disp = max(disp))

## Source: local data frame [3 x 5]
## 
##     cyl min_mpg min_disp max_mpg max_disp
##   (dbl)   (dbl)    (dbl)   (dbl)    (dbl)
## 1     4    21.4     71.1    33.9    146.7
## 2     6    17.8    145.0    21.4    258.0
## 3     8    10.4    275.8    19.2    472.0


Когда ко многим переменным применяется много функций, summarise_each() использует более компактный и аккуратный синтаксис:

# без группировки
mtcars %>% 
  summarise_each(funs(min, max) , mpg, disp)

## Source: local data frame [1 x 4]
## 
##   mpg_min disp_min mpg_max disp_max
##     (dbl)    (dbl)   (dbl)    (dbl)
## 1    10.4     71.1    33.9      472

# с одной группой
mtcars %>% 
  group_by(cyl) %>% 
  summarise_each(funs(min, max) , mpg, disp)

## Source: local data frame [3 x 5]
## 
##     cyl mpg_min disp_min mpg_max disp_max
##   (dbl)   (dbl)    (dbl)   (dbl)    (dbl)
## 1     4    21.4     71.1    33.9    146.7
## 2     6    17.8    145.0    21.4    258.0
## 3     8    10.4    275.8    19.2    472.0


Имена выходных переменных можно задать так: variable_function, т.е. mpg_min, disp_min и т.д.

Обратное именование переменных, т.е. function_variable, невозможно при вызове summarise_each(). Это можно реализовать с помощью отдельной команды.

# без группировки
mtcars %>% 
  summarise_each(funs(min, max) , mpg, disp) %>%
  setNames(c("min_mpg", "min_disp", "max_mpg", "max_disp"))

## Source: local data frame [1 x 4]
## 
##   min_mpg min_disp max_mpg max_disp
##     (dbl)    (dbl)   (dbl)    (dbl)
## 1    10.4     71.1    33.9      472

# с группировкой
mtcars %>% 
  group_by(cyl) %>% 
  summarise_each(funs(min, max) , mpg, disp) %>%
  setNames(c("gear", "min_mpg", "min_disp", "max_mpg", "max_disp"))

## Source: local data frame [3 x 5]
## 
##    gear min_mpg min_disp max_mpg max_disp
##   (dbl)   (dbl)    (dbl)   (dbl)    (dbl)
## 1     4    21.4     71.1    33.9    146.7
## 2     6    17.8    145.0    21.4    258.0
## 3     8    10.4    275.8    19.2    472.0


Выводы


При использовании функций, возвращающих результат единичной длины, есть два основных кандидата:

  • summarise()
  • summarise_each()


Функция summarise() имеет более простой синтаксис, а функция summarise_each() — более компактный.

Вследствие этого, summarise() больше подходит для одной переменной единственной функции. Чем больше количество переменных или функций, тем более оправдано применение summarise_each().

У функции summarise_each() свой способ именования выходных переменных:

Вариант 2: применить много функций к одной переменной
Имена выходных переменных определяются именами функций. В этом случае мы теряем имя переменной, к которой применяются функции.Вариант 3: применить одну функцию к многим переменным
Имена выходных переменных определяются именами переменных. В этом случае мы теряем имя функции, применяемой к переменным.Вариант 4: применить много функций к многим переменным
Имена выходных переменных определяются нотацией variable_function. Внутри вызова summarise_each() другое именование невозможно.

© Habrahabr.ru