Подсчёт строк по группам: table()
Самый простой вопрос группировки — «сколько каждого?», и базовый R отвечает на него одним вызовом. table() считает, как часто встречается каждое значение:
Один столбец даёт количества по значениям; два столбца дают полную таблицу сопряжённости (регионы по строкам, тарифы по столбцам). Для вопроса «какая доля?» оберните это в prop.table(table(...)). table() — самый быстрый путь к ответу о частотах во всём R; не хватайтесь за что-то тяжелее, когда нужен всего лишь подсчёт.
Одна статистика на группу: tapply()
tapply(values, groups, function) разбивает первый вектор по второму и применяет функцию к каждому куску:
Результат — именованный вектор: метки групп как имена, статистики как значения, — что делает его идеальным для быстрых поисков (means["EU"]). Название расшифровывается как «table apply»: применить функцию вдоль группирующей таблицы. Она принадлежит тому же семейству, что sapply() и lapply(), разобранному в статье о семействе apply.
Ограничение tapply() — форма: именованный вектор неудобно соединять, рисовать и обрабатывать дальше. Когда сводка является промежуточным шагом, а не окончательным ответом, вам нужен датафрейм — а его как раз и возвращает следующий инструмент.
Формульная рабочая лошадка: aggregate()
aggregate() — полноценная группировка базового R: она возвращает датафрейм, а её формульный интерфейс читается как фраза, которую вы бы произнесли. value ~ group означает «значение в разбивке по группе»:
Добавление группирующих переменных — это просто + another_column в формуле; второй вызов суммирует по каждой комбинации региона и квартала. Можно также агрегировать сразу несколько столбцов значений через cbind(a, b) ~ group. Поскольку на выходе получается обычный датафрейм, он сразу вставляется в соединение, сортировку или график — это базовый инструмент по умолчанию для групповых сводок.
Одно тихое поведение стоит знать: формульный интерфейс перед агрегацией отбрасывает строки с NA в любом используемом столбце. Обычно это то, что нужно; иногда это объяснение подозрительно низкого количества.
Способ dplyr: group_by() + summarize()
Современный стандарт — шаблон из двух глаголов dplyr: group_by() объявляет группировку, а summarize() схлопывает каждую группу в одну строку, вычисляя столько статистик, сколько вы назовёте (сниппет статичный; песочница выполняет только базовый R):
library(dplyr)
sales |>
group_by(region) |>
summarize(
n = n(),
total = sum(amount),
avg = mean(amount)
)
Вот где dplyr действительно превосходит базовый R: несколько статистик на группу в одном читаемом вызове — aggregate() требует для этого акробатики, — плюс бесплатный n(), а результат перетекает прямо в следующий пайп. (summarise() — та же функция в британском написании.)
Одно место озадачивает новичков: при нескольких группирующих переменных summarize() снимает только последнюю, оставляя результат по-прежнему сгруппированным, — и печатает об этом сообщение. Говорите явно, чего хотите, через аргумент .groups: summarize(avg = mean(amount), .groups = "drop") возвращает обычный несгруппированный фрейм, и это правильная привычка по умолчанию. Сгруппированный фрейм, просочившийся в дальнейший код, заставляет mutate() и родственников молча работать по группам — классический источник непонятных результатов.
Как выбрать
- Подсчёт —
table(), один вызов не превзойти. - Одна статистика, быстрый взгляд —
tapply(), и считывайте ответ с именованного вектора. - Нужен датафрейм на выходе, окружение только базового R —
aggregate()с формулой. - Несколько статистик, часть конвейера или что-то амбициозное —
group_by() |> summarize().
Неправильного ответа среди них нет — все они считают одни и те же числа, — но подбор инструмента под нужную форму вывода экономит последующий шаг преобразования. Сами средние, медианы и меры разброса разобраны в описательных статистиках.
Разобранный пример: mtcars по числу цилиндров
Всё вместе на встроенном наборе данных: сколько машин приходится на каждое число цилиндров и каковы их средняя экономичность и мощность:
Одиннадцать четырёхцилиндровых машин со средним около 26.7 миль на галлон, семь шестицилиндровых около 19.7, четырнадцать восьмицилиндровых около 15.1 — а мощность движется в противоположную сторону. Два вызова, целая сводная таблица: именно ради таких вопросов групповая агрегация и существует.
Что вы уносите с собой
table()— для количеств,prop.table()— для долей.tapply(values, groups, fn)возвращает именованный вектор — быстрый взгляд, удобный поиск.aggregate(value ~ group, data, FUN)возвращает датафрейм;+добавляет группирующие переменные,cbind()— столбцы значений.group_by() |> summarize(n = n(), avg = mean(x), .groups = "drop")из dplyr — современный стандарт для сводок с несколькими статистиками.- Выбирайте по форме вывода; в числах они все сходятся.
Дальше: объединение таблиц с общим ключом — merge() и семейство соединений dplyr.
Часто задаваемые вопросы
Как посчитать среднее по группам в R?
В базовом R есть два инструмента: tapply(df$value, df$group, mean) возвращает именованный вектор групповых средних, а aggregate(value ~ group, data = df, FUN = mean) возвращает тот же результат датафреймом. В dplyr: df |> group_by(group) |> summarize(avg = mean(value)).
Как посчитать количество по группам в R?
table(df$group) одним вызовом считает строки по каждому значению группы; table(df$a, df$b) строит таблицу сопряжённости по двум столбцам. В dplyr count(df, group) делает то же самое и возвращает датафрейм, который проще обрабатывать дальше.
Что делает aggregate() в R?
aggregate() разбивает датафрейм по одному или нескольким группирующим столбцам, применяет функцию к каждому куску и возвращает датафрейм результатов. Формульный интерфейс читается естественно: aggregate(sales ~ region + quarter, data = df, FUN = mean) означает «продажи, сгруппированные по региону и кварталу, усреднённые».
В чём разница между tapply и aggregate?
Одно и то же вычисление, разная форма вывода. tapply() возвращает именованный вектор (или массив при двух группирующих переменных) — удобно для быстрых поисков. aggregate() возвращает датафрейм — лучше, когда результат идёт в дальнейший анализ, соединение или график. Сомневаетесь — берите aggregate().