Группировка данных (groupby) и агрегатные функции

Мы уже научились выбирать нужные столбцы и фильтровать строки, чтобы отсечь лишнее. Но в реальной работе аналитика редко просят просто показать список заказов. Чаще бизнес хочет знать: «Какова выручка по городам?» или «Какой средний чек у разных групп клиентов?». Для ответов на такие вопросы мы используем группировку — мощный инструмент, который превращает тысячи строк сырых данных в понятные и компактные отчеты.

От Сводных таблиц к логике Pandas

Если мы раньше работали со Сводными таблицами в Excel, то концепция группировки нам уже знакома. В Excel мы перетаскиваем поле в «Строки», а другое — в «Значения», выбирая тип вычисления (сумма, среднее). В Pandas этот процесс описывается концепцией Split-Apply-Combine (Разбить — Применить — Объединить).

  1. Split (Разбить): Данные разделяются на группы по определенному признаку (например, по категории товара).
  2. Apply (Применить): К каждой группе применяется функция (вычисляем сумму, среднее или количество).
  3. Combine (Объединить): Результаты вычислений собираются в новую таблицу.

Схема Split-Apply-Combine Визуализация процесса: данные «схлопываются» до уникальных значений группирующего ключа.

Базовый синтаксис: Метод groupby()

Для начала работы мы используем метод .groupby(). Важно понимать, что сам по себе этот метод не возвращает таблицу. Он создает промежуточный объект GroupBy — это своего рода «инструкция», которая подготовила данные к расчетам, но еще не выполнила их.

# Группируем данные по столбцу 'category'
grouped = df.groupby('category')

# Если мы попробуем вывести grouped, мы увидим лишь техническое описание объекта.
# Чтобы получить результат, нужно добавить агрегатную функцию.
result = df.groupby('category')['sales'].sum()

Важное замечание: При группировке по умолчанию Pandas делает столбец, по которому мы группировали, индексом (жирным заголовком строки). Чтобы вернуть его в обычные столбцы, мы используем метод .reset_index(). Это критически важно для дальнейшей работы с данными.

Именованная агрегация (Named Aggregation)

В 2026 году стандартом индустрии считается написание чистого и читаемого кода. Раньше аналитики часто использовали сложные цепочки переименований столбцов после группировки. Сейчас мы используем Named Aggregation — это позволяет задать имя новому столбцу и выбрать функцию для расчета в один шаг.

Рассмотрим пример анализа продаж. Мы хотим узнать общую выручку и количество уникальных покупателей для каждого региона.

Пример кода, который неудобно читать и поддерживать:

# Не самый эффективный подход
res = df.groupby('region')['sales'].agg(['sum', 'nunique'])
res.columns = ['total_revenue', 'unique_customers'] # Ручное переименование

Современный и надежный подход:

# Именованная агрегация — чисто и понятно
region_report = df.groupby('region').agg(
    total_revenue=('sales', 'sum'),
    unique_clients=('client_id', 'nunique'),
    avg_check=('sales', 'mean')
)

# Сбрасываем индекс, чтобы превратить 'region' обратно в колонку
region_report = region_report.reset_index()

Агрегатные функции и их выбор

Мы уже знакомы с основами статистики, поэтому при выборе функции агрегации мы должны учитывать особенности данных:

  • sum() — общие итоги (выручка, количество проданных штук).
  • mean() — среднее значение. Осторожно: оно чувствительно к выбросам!
  • median() — медиана. Лучше использовать для типичного чека или зарплаты, если в данных есть аномалии.
  • count() — количество строк (включая пустые значения, если не указано иное).
  • size() — общее количество строк в группе.
  • nunique() — количество уникальных элементов (например, сколько уникальных пользователей совершили покупки).

Типичные ошибки при группировке

Одной из частых проблем является попытка применить математическую функцию к нечисловым данным.

Проблемный код:

# Если в таблице есть текстовые столбцы, sum() может вызвать ошибку или предупреждение
df.groupby('city').sum() 

Правильный подход: Всегда явно указывайте, какие столбцы вы хотите агрегировать, или используйте параметр numeric_only=True.

# Указываем конкретно, что суммируем
df.groupby('city')[['sales', 'profit']].sum()

Также стоит помнить про пропуски (NaN). По умолчанию groupby исключает строки, где в ключевом столбце (по которому группируем) есть пустое значение. Если мы хотим увидеть группу с «неизвестными» значениями, нужно добавить параметр dropna=False.

Практический кейс: Анализ маркетинговых каналов

Представим, что у нас есть данные о заказах из разных рекламных источников. Нам нужно понять, какой канал приносит больше денег, а какой — более лояльных клиентов.

marketing_analysis = df.groupby('source').agg(
    total_sales=('revenue', 'sum'),
    orders_count=('order_id', 'count'),
    loyal_customers=('customer_id', 'nunique')
).reset_index()

# Рассчитаем средний доход на одного уникального клиента
marketing_analysis['revenue_per_client'] = (
    marketing_analysis['total_sales'] / marketing_analysis['loyal_customers']
)

Использование таких конструкций позволяет нам за несколько секунд превратить сырой лог транзакций в готовую таблицу для бизнес-отчета.


Теперь мы умеем «схлопывать» данные и получать из них ключевые метрики. Но часто бывает так, что нужная информация (например, имена менеджеров или названия брендов) хранится в другой таблице. Чтобы собрать полную картину, нам нужно научиться соединять таблицы между собой. В следующей теме мы разберем, как работает «склейка» данных с помощью Merge и Concat.