При обработке данных вы должны:
Пакет dplyr делает каждый из этих шагов на столько простым и быстрым на сколько это возможно следующим образом:
Цель этого документа – дать представление о базовых инструментах предоставляемых dplyr, и продемонстрировать их применение к структурам данных типа data.frame.
Также существуют отдельные руководства по следующим темам:
databases*: наравне с объектами в памяти dplyr также может обращаться к данным баз данных. Это позволяет вам работать с удалёнными (в смысле нахождения на расстоянии) данными, используя в точности те же инструменты, потому что dplyr будет транслировать ваш код на R в соответствующие SQL-запросы.benchmark-baseball: рассматривает как dplyr выглядит в сравнении с другими инструментами для манипулирования данными на реалистичной задаче.window-functions*: функции агрегирования по окну – это разновидность функций агрегирования, где функция агрегирования вычисляет по n аргументам 1 значение, а функции агрегирования по окну получает n аргументов и возвращает n значений.Для исследования основных операций манипулирования данными в dplyr мы будем использовать встроенный набор данных hflights. Он содержит данные всех 227496 рейсов из Хьюстона за 2011 год, и предоставлен, как написано в ?hflights, бюро по перемещениям США.
library(hflights)
dim(hflights)
## [1] 227496 21
head(hflights)
## Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier
## 5424 2011 1 1 6 1400 1500 AA
## 5425 2011 1 2 7 1401 1501 AA
## 5426 2011 1 3 1 1352 1502 AA
## 5427 2011 1 4 2 1403 1513 AA
## 5428 2011 1 5 3 1405 1507 AA
## 5429 2011 1 6 4 1359 1503 AA
## FlightNum TailNum ActualElapsedTime AirTime ArrDelay DepDelay Origin
## 5424 428 N576AA 60 40 -10 0 IAH
## 5425 428 N557AA 60 45 -9 1 IAH
## 5426 428 N541AA 70 48 -8 -8 IAH
## 5427 428 N403AA 70 39 3 3 IAH
## 5428 428 N492AA 62 44 -3 5 IAH
## 5429 428 N262AA 64 45 -7 -1 IAH
## Dest Distance TaxiIn TaxiOut Cancelled CancellationCode Diverted
## 5424 DFW 224 7 13 0 0
## 5425 DFW 224 6 9 0 0
## 5426 DFW 224 5 17 0 0
## 5427 DFW 224 9 22 0 0
## 5428 DFW 224 9 9 0 0
## 5429 DFW 224 6 13 0 0
dplyr может работать с data.frame непосредственно, но при работе с большими объёмами данных стоит конвертировать их в tbl_df, обёртку вокруг data.frame которая не станет по случайности выводить кучу данных на экран (это может очень медленным процессом).
hflights_df <- tbl_df(hflights)
hflights_df
## Source: local data frame [227,496 x 21]
##
## Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum
## 1 2011 1 1 6 1400 1500 AA 428
## 2 2011 1 2 7 1401 1501 AA 428
## 3 2011 1 3 1 1352 1502 AA 428
## 4 2011 1 4 2 1403 1513 AA 428
## 5 2011 1 5 3 1405 1507 AA 428
## 6 2011 1 6 4 1359 1503 AA 428
## 7 2011 1 7 5 1359 1509 AA 428
## 8 2011 1 8 6 1355 1454 AA 428
## 9 2011 1 9 7 1443 1554 AA 428
## 10 2011 1 10 1 1443 1553 AA 428
## .. ... ... ... ... ... ... ... ...
## Variables not shown: TailNum (chr), ActualElapsedTime (int), AirTime
## (int), ArrDelay (int), DepDelay (int), Origin (chr), Dest (chr),
## Distance (int), TaxiIn (int), TaxiOut (int), Cancelled (int),
## CancellationCode (chr), Diverted (int)
dplyr предлагает пять основных операций для манипуляции данными применимые к отдельной таблице: filter(), arrange(), select(), mutate() and summarise(). Для тех кто пользовался до этого plyr многие из них будут знакомы.
filter() позволяет вам выбрать подмножество строк из data.frame. Первый аргумент – имя набора данных, второй и последующие – условия фильтра в контексте этого набора данных.
Например мы можем выбрать все вылет 1-го января следующим образом:
filter(hflights_df, Month == 1, DayofMonth == 1)
## Source: local data frame [552 x 21]
##
## Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum
## 1 2011 1 1 6 1400 1500 AA 428
## 2 2011 1 1 6 728 840 AA 460
## 3 2011 1 1 6 1631 1736 AA 1121
## 4 2011 1 1 6 1756 2112 AA 1294
## 5 2011 1 1 6 1012 1347 AA 1700
## 6 2011 1 1 6 1211 1325 AA 1820
## 7 2011 1 1 6 557 906 AA 1994
## 8 2011 1 1 6 1824 2106 AS 731
## 9 2011 1 1 6 654 1124 B6 620
## 10 2011 1 1 6 1639 2110 B6 622
## .. ... ... ... ... ... ... ... ...
## Variables not shown: TailNum (chr), ActualElapsedTime (int), AirTime
## (int), ArrDelay (int), DepDelay (int), Origin (chr), Dest (chr),
## Distance (int), TaxiIn (int), TaxiOut (int), Cancelled (int),
## CancellationCode (chr), Diverted (int)
Что будет эквивалентно более многословному (длиннее на целых 10 символов!) варианту:
hflights[hflights$Month == 1 & hflights$DayofMonth == 1, ]
filter() работает аналогично subset() за тем исключением, что вы можете ему передать любое количество условий для фильтра, которые будут объединены вместе через & (логическое И, но не && с которым можно случайно перепутать). Вы можете также использовать любы другие логически связки:
filter(hflights_df, Month == 1 | Month == 2)
arrange(), в свою очередь, работает аналогично subset() за исключением того, что вместо выбора строк она переупорядочивает их. Функция получает на вход имя набора данных и список имён колонок (или более сложное выражение) для упорядочивания по ним. Если будет указано больше одной колонки, то каждая следующая колонка будет упорядочиваться в пределах каждого отдельного набора значений предыдущих:
arrange(hflights_df, DayofMonth, Month, Year)
## Source: local data frame [227,496 x 21]
##
## Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum
## 1 2011 1 1 6 1400 1500 AA 428
## 2 2011 1 1 6 728 840 AA 460
## 3 2011 1 1 6 1631 1736 AA 1121
## 4 2011 1 1 6 1756 2112 AA 1294
## 5 2011 1 1 6 1012 1347 AA 1700
## 6 2011 1 1 6 1211 1325 AA 1820
## 7 2011 1 1 6 557 906 AA 1994
## 8 2011 1 1 6 1824 2106 AS 731
## 9 2011 1 1 6 654 1124 B6 620
## 10 2011 1 1 6 1639 2110 B6 622
## .. ... ... ... ... ... ... ... ...
## Variables not shown: TailNum (chr), ActualElapsedTime (int), AirTime
## (int), ArrDelay (int), DepDelay (int), Origin (chr), Dest (chr),
## Distance (int), TaxiIn (int), TaxiOut (int), Cancelled (int),
## CancellationCode (chr), Diverted (int)
Необходимо использовать desc() чтобы задать обратный порядок:
arrange(hflights_df, desc(ArrDelay))
## Source: local data frame [227,496 x 21]
##
## Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum
## 1 2011 12 12 1 650 808 AA 1740
## 2 2011 8 1 1 156 452 CO 1
## 3 2011 11 8 2 721 948 MQ 3786
## 4 2011 6 21 2 2334 124 UA 855
## 5 2011 5 20 5 858 1027 MQ 3328
## 6 2011 6 9 4 2029 2243 MQ 3859
## 7 2011 1 20 4 635 807 CO 59
## 8 2011 6 22 3 908 1040 CO 595
## 9 2011 12 13 2 706 824 MQ 3328
## 10 2011 10 25 2 2310 149 DL 1215
## .. ... ... ... ... ... ... ... ...
## Variables not shown: TailNum (chr), ActualElapsedTime (int), AirTime
## (int), ArrDelay (int), DepDelay (int), Origin (chr), Dest (chr),
## Distance (int), TaxiIn (int), TaxiOut (int), Cancelled (int),
## CancellationCode (chr), Diverted (int)
dplyr::arrange() работает так же как и plyr::arrange(). Это простая обёртка над order(), только позволяющая меньше набирать на клавиатуре. Предыдущий пример эквивалентен такому выражению:
hflights[order(hflights$DayofMonth, hflights$Month, hflights$Year), ]
hflights[order(desc(hflights$ArrDelay)), ]
Часто случается что при работе с большим набором данных вам на самом деле интересны только некоторые колонки. select() позволяет вам быстро сфокусироваться на интересующей части колонок, используя при этом символические имена на манер номеров колонок:
# Выбор колонки по имени
select(hflights_df, Year, Month, DayOfWeek)
## Source: local data frame [227,496 x 3]
##
## Year Month DayOfWeek
## 1 2011 1 6
## 2 2011 1 7
## 3 2011 1 1
## 4 2011 1 2
## 5 2011 1 3
## 6 2011 1 4
## 7 2011 1 5
## 8 2011 1 6
## 9 2011 1 7
## 10 2011 1 1
## .. ... ... ...
# Выбор всех колонок между Year и DayOfWeek (включительно)
select(hflights_df, Year:DayOfWeek)
## Source: local data frame [227,496 x 4]
##
## Year Month DayofMonth DayOfWeek
## 1 2011 1 1 6
## 2 2011 1 2 7
## 3 2011 1 3 1
## 4 2011 1 4 2
## 5 2011 1 5 3
## 6 2011 1 6 4
## 7 2011 1 7 5
## 8 2011 1 8 6
## 9 2011 1 9 7
## 10 2011 1 10 1
## .. ... ... ... ...
# Выбор всех колонок за исключением тех что между Year и DayOfWeek (включительно)
select(hflights_df, -(Year:DayOfWeek))
## Source: local data frame [227,496 x 17]
##
## DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime
## 1 1400 1500 AA 428 N576AA 60
## 2 1401 1501 AA 428 N557AA 60
## 3 1352 1502 AA 428 N541AA 70
## 4 1403 1513 AA 428 N403AA 70
## 5 1405 1507 AA 428 N492AA 62
## 6 1359 1503 AA 428 N262AA 64
## 7 1359 1509 AA 428 N493AA 70
## 8 1355 1454 AA 428 N477AA 59
## 9 1443 1554 AA 428 N476AA 71
## 10 1443 1553 AA 428 N504AA 70
## .. ... ... ... ... ... ...
## Variables not shown: AirTime (int), ArrDelay (int), DepDelay (int), Origin
## (chr), Dest (chr), Distance (int), TaxiIn (int), TaxiOut (int),
## Cancelled (int), CancellationCode (chr), Diverted (int)
Эта функция работает подобно base::subset(). Отдельная функция добавлена для поддержания стройности идеологии dplyr, заключающейся в наличии функций каждая из которых делает только одну конкретную операцию и наиболее подходящим образом.
Бывает полезно не только выбрать интересующие колонки, но и вычислить на их основании значения некоторой другой колонки. Для этих целей предназначена mutate():
mutate(hflights_df,
gain = ArrDelay - DepDelay,
speed = Distance / AirTime * 60)
## Source: local data frame [227,496 x 23]
##
## Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum
## 1 2011 1 1 6 1400 1500 AA 428
## 2 2011 1 2 7 1401 1501 AA 428
## 3 2011 1 3 1 1352 1502 AA 428
## 4 2011 1 4 2 1403 1513 AA 428
## 5 2011 1 5 3 1405 1507 AA 428
## 6 2011 1 6 4 1359 1503 AA 428
## 7 2011 1 7 5 1359 1509 AA 428
## 8 2011 1 8 6 1355 1454 AA 428
## 9 2011 1 9 7 1443 1554 AA 428
## 10 2011 1 10 1 1443 1553 AA 428
## .. ... ... ... ... ... ... ... ...
## Variables not shown: TailNum (chr), ActualElapsedTime (int), AirTime
## (int), ArrDelay (int), DepDelay (int), Origin (chr), Dest (chr),
## Distance (int), TaxiIn (int), TaxiOut (int), Cancelled (int),
## CancellationCode (chr), Diverted (int), gain (int), speed (dbl)
dplyr::mutate() работает аналогично plyr::mutate() и аналогично base::transform(). Главное отличие между mutate() and transform() в том что первая может ссылаться в вычислениях на колонку которая создаётся в рамках того же вызова функции:
mutate(hflights_df,
gain = ArrDelay - DepDelay,
gain_per_hour = gain / (AirTime / 60)
)
## Source: local data frame [227,496 x 23]
##
## Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum
## 1 2011 1 1 6 1400 1500 AA 428
## 2 2011 1 2 7 1401 1501 AA 428
## 3 2011 1 3 1 1352 1502 AA 428
## 4 2011 1 4 2 1403 1513 AA 428
## 5 2011 1 5 3 1405 1507 AA 428
## 6 2011 1 6 4 1359 1503 AA 428
## 7 2011 1 7 5 1359 1509 AA 428
## 8 2011 1 8 6 1355 1454 AA 428
## 9 2011 1 9 7 1443 1554 AA 428
## 10 2011 1 10 1 1443 1553 AA 428
## .. ... ... ... ... ... ... ... ...
## Variables not shown: TailNum (chr), ActualElapsedTime (int), AirTime
## (int), ArrDelay (int), DepDelay (int), Origin (chr), Dest (chr),
## Distance (int), TaxiIn (int), TaxiOut (int), Cancelled (int),
## CancellationCode (chr), Diverted (int), gain (int), gain_per_hour (dbl)
transform(hflights,
gain = ArrDelay - DepDelay,
gain_per_hour = gain / (AirTime / 60)
)
#> Error: object 'gain' not found
Последняя рассматриваемая операция – summarise(), она просто сворачивает данные набора в одну колонку. Пока это не слишком полезно:
summarise(hflights_df,
delay = mean(DepDelay, na.rm = TRUE))
## Source: local data frame [1 x 1]
##
## delay
## 1 9.445
Такое поведение в точности эквивалентно поведению plyr::summarise().
У вас может возникнуть впечатление что все перечисленные функции имеют что-то общее:
data.frame.$.data.frame.Все эти свойства вместе позволяют легко комбинировать множество вызовов в одну цепочку для вычисления сложного результата.
Эти пять функций создают основу для языка манипулирования данными. На самом простом уровне вы можете только изменить что-либо в наборе данных пятью способами: изменить порядок строк (arrange()), выбрать наблюдения или атрибуты (filter() и select()), добавить вычисляемые атрибуты (mutate()) или свернуть набор в итог (summarise()). Оставшаяся часть этого языка образуется из применения перечисленных функций к различным типам данных, например группированным данным, как описано ниже.
Вышеописанные функции полезны сами по себе, но особую мощь они приобретают в комбинации с концепцией (нет, не коллективной безопасности (: ) группировки, повторения вычислений для каждой группы наблюдений в отдельности. В dplyr используется функция group_by() для разбиения набора данных на части по строкам. Вы можете использовать вывод функции group_by() непосредственно как первый аргумент перечисленных выше пяти основных функций, они сами обработают правильно разбитый на группы набор данных.
Из функций в предыдущем разделе select() не реагирует на то что его первый аргумент сгруппирован, arrange() просто ведёт себя так, как будто его вторым аргументом указанны атрибуты по которым сгруппирован первый аргумент. Погруппные mutate() и filter() наиболее полезны в совокупности с функциями агрегирования по окну*, но это описано в отдельном руководстве (vignette()). summarise() в данном случае весьма полезна и проста в применении как будет более детально показано далее.
В следующем примере мы разделим набор данных по самолётам и рассчитаем количество вылетов (count = n()) и средние дальность полета (dist = mean(Distance, na.rm = TRUE)) и задержку вылета (delay = mean(ArrDelay, na.rm = TRUE)). Затем построим график при помощи ggplot2.
planes <- group_by(hflights_df, TailNum)
delay <- summarise(planes,
count = n(),
dist = mean(Distance, na.rm = TRUE),
delay = mean(ArrDelay, na.rm = TRUE))
delay <- filter(delay, count > 20, dist < 2000)
# Интересно, на сколько зависит
# задержка от дальности полёта
ggplot(delay, aes(dist, delay)) +
geom_point(aes(size = count), alpha = 1/2) +
geom_smooth() +
scale_size_area()
Вы использовали summarise() с функциями агрегирования, получающими вектор значений и возвращающими скалярное значение. Множество таких функций есть в базовой среде R, например min(), max(), mean(), sum(), sd(), median() и IQR(). dplyr предлагает ещё несколько полезных:
n(): количество наблюдений в группе,n_distinct(x): количество наблюдений с уникальным значением переменной x.first(x), last(x) и nth(x, n) – работают подобно x[1], x[length(x)], и x[n], но дают больше контроля над результатом если значение не может быть получено.Например, мы можем использовать их чтобы найти номера самолётов и количество вылетов во все возможные пункты назначения:
destinations <- group_by(hflights_df, Dest)
summarise(destinations,
planes = n_distinct(TailNum),
flights = n()
)
## Source: local data frame [116 x 3]
##
## Dest planes flights
## 1 ABQ 716 2812
## 2 AEX 215 724
## 3 AGS 1 1
## 4 AMA 158 1297
## 5 ANC 38 125
## 6 ASE 60 125
## 7 ATL 983 7886
## 8 AUS 1015 5022
## 9 AVL 142 350
## 10 BFL 70 504
## .. ... ... ...
Также вы можете применить собственную функцию. Для повышения производительности многие функции dplyr написаны на C++. Если вы хотите использовать собственную функцию на C++ обратите внимание на соответствующее руководство* для уточнения деталей.
При группировке по нескольким переменным вы можете также получить “послойные” итоги для каждого уровня в отдельности и по очереди. Этот приём позволяет кумулятивно получать свёртку набора данных:
daily <- group_by(hflights_df, Year, Month, DayofMonth)
(per_day <- summarise(daily, flights = n()))
## Source: local data frame [365 x 4]
## Groups: Year, Month
##
## Year Month DayofMonth flights
## 1 2011 1 1 552
## 2 2011 1 2 678
## 3 2011 1 3 702
## 4 2011 1 4 583
## 5 2011 1 5 590
## 6 2011 1 6 660
## 7 2011 1 7 661
## 8 2011 1 8 500
## 9 2011 1 9 602
## 10 2011 1 10 659
## .. ... ... ... ...
(per_month <- summarise(per_day, flights = sum(flights)))
## Source: local data frame [12 x 3]
## Groups: Year
##
## Year Month flights
## 1 2011 1 18910
## 2 2011 2 17128
## 3 2011 3 19470
## 4 2011 4 18593
## 5 2011 5 19172
## 6 2011 6 19600
## 7 2011 7 20548
## 8 2011 8 20176
## 9 2011 9 18065
## 10 2011 10 18696
## 11 2011 11 18021
## 12 2011 12 19117
(per_year <- summarise(per_month, flights = sum(flights)))
## Source: local data frame [1 x 2]
##
## Year flights
## 1 2011 227496
Однако необходимо проявлять осторожность при таком методе получения итогов: он подойдёт для сумм и количеств, но нужно задуматься при использовании средних и дисперсий, и уж точно не применять с медианой.
API (тот самый мощный ЭПиАй) dplyr функционален, в том смысле, что вызовы функций не имеют побочных эффектов, и вы должны сами сохранять результаты. Это обстоятельство не способствует написанию элегантного кода если вам нужно выполнить множество операций подряд. Вы, конечно, можете выполнять их шаг за шагом:
a1 <- group_by(hflights, Year, Month, DayofMonth)
a2 <- select(a1, Year:DayofMonth, ArrDelay, DepDelay)
a3 <- summarise(a2,
arr = mean(ArrDelay, na.rm = TRUE),
dep = mean(DepDelay, na.rm = TRUE))
a4 <- filter(a3, arr > 30 | dep > 30)
Или, если вы не желаете сохранять промежуточные результаты, можно передавать вызовы функций как аргументы, вкладывая один в другой:
filter(
summarise(
select(
group_by(hflights, Year, Month, DayofMonth),
Year:DayofMonth, ArrDelay, DepDelay
),
arr = mean(ArrDelay, na.rm = TRUE),
dep = mean(DepDelay, na.rm = TRUE)
),
arr > 30 | dep > 30
)
## Source: local data frame [14 x 5]
## Groups: Year, Month
##
## Year Month DayofMonth arr dep
## 1 2011 2 4 44.08 47.17
## 2 2011 3 3 35.13 38.20
## 3 2011 3 14 46.64 36.14
## 4 2011 4 4 38.72 27.95
## 5 2011 4 25 37.80 22.26
## 6 2011 5 12 69.52 64.52
## 7 2011 5 20 37.03 26.55
## 8 2011 6 22 65.52 62.31
## 9 2011 7 29 29.56 31.87
## 10 2011 9 29 39.20 32.50
## 11 2011 10 9 61.90 59.53
## 12 2011 11 15 43.68 39.23
## 13 2011 12 29 26.30 30.79
## 14 2011 12 31 46.48 54.17
Что создаёт сложности с чтением, так как порядок выполнения начинается из середины выражения и аргументы отдаляются от текста вызова функции. Чтобы обойти это проблему dplyr использует оператор %>%. x %>% f(y) преобразуется в f(x, y), что можно использовать для преобразования кода вызовов в удобный для чтения слева направо, сверху вниз:
hflights %>%
group_by(Year, Month, DayofMonth) %>%
select(Year:DayofMonth, ArrDelay, DepDelay) %>%
summarise(
arr = mean(ArrDelay, na.rm = TRUE),
dep = mean(DepDelay, na.rm = TRUE)
) %>%
filter(arr > 30 | dep > 30)
## Source: local data frame [14 x 5]
## Groups: Year, Month
##
## Year Month DayofMonth arr dep
## 1 2011 2 4 44.08 47.17
## 2 2011 3 3 35.13 38.20
## 3 2011 3 14 46.64 36.14
## 4 2011 4 4 38.72 27.95
## 5 2011 4 25 37.80 22.26
## 6 2011 5 12 69.52 64.52
## 7 2011 5 20 37.03 26.55
## 8 2011 6 22 65.52 62.31
## 9 2011 7 29 29.56 31.87
## 10 2011 9 29 39.20 32.50
## 11 2011 10 9 61.90 59.53
## 12 2011 11 15 43.68 39.23
## 13 2011 12 29 26.30 30.79
## 14 2011 12 31 46.48 54.17
Также как с data.frame, dplyr может работать с данными представленными другими способами, такими как data tables, базы данных и многомерные массивы.
dplyr также предлагает все перечисленные методы манипулирования данным и для объектов типа data.tables, вы просто должны заменить набор данных на data.table.
data.table может оказаться быстрее во многих случаях, потому что возможно выполнение нескольких операций одновременно. Например, вы можете выполнить mutate и select за один вызов, и data.table сообразит что нет нужды рассчитывать новую переменную в строках которые должны быть отфильтрованы.
Преимущества при использовании data.tables следующие:
data.tables изолирует вас от данных непосредственно, и защищает их таким образом от непреднамеренного изменения.[, предлагается множество относительно простых методов.dplyr позволяет использовать удалённые базы данных так же как data.frame. Избавляя таким образом от необходимости постоянно переключать мышление между языками. Для уточнения деталей необходимо обратиться к соответствующему руководству*.
В сравнении с другим вариантами использования баз данных:
tbl_cube() предлагает экспериментальный интерфейс к многомерным массивам или кубам (как OLAP-кубы) . Если вы используете подобное представление в R, пожалуйста, свяжитесь с автором чтобы он лучше понял ваши потребности.
В сравнение со всеми существующими альтернативами, dplyr:
data.frame, data.tables и удалёнными базами данных. Это позволяет думать только о том что вы хотите сделать с данными, а не об устройстве хранилища данныхprint(), которые не распечатает случайно несколько страниц данных на экран (вдохновение было почерпнуто в data.tables).В сравнении с функциями из основной поставки:
dplyr намного более строен; функции имеют идентичный интерфейс, так что освоив одну вы разбираетесь и в другихdplyr концентрируется на наборах данныхВ сравнении с plyr:
dplyr намного намного быстрееdata.frame (т.о. большая часть dplyr эквивалентна ddply() с добавлением некоторых функций, do() эквивалентна dlply())В сравнении с использованием виртуальных data.frame:
data.frame не предполагает что у вас есть данные, так что если необходимо выполнить lm() понадобится получить данные вручнуюdata.frame не предоставляет методов для агрегатных функций (т.е. mean() или sum())