Menu

Группировка и сводки в R (aggregate, tapply, dplyr)

Все способы посчитать статистики по группам в R: подсчёт через table(), tapply() для одной статистики на группу, формульный интерфейс aggregate() и group_by() с summarize() из dplyr.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Подсчёт строк по группам: 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(), и считывайте ответ с именованного вектора.
  • Нужен датафрейм на выходе, окружение только базового Raggregate() с формулой.
  • Несколько статистик, часть конвейера или что-то амбициозное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().

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ