Pandas groupby и merge: как собрать аналитический отчёт из таблиц
Практический разбор groupby, merge и pivot_table в pandas: как агрегировать заказы, присоединять справочники и не посчитать выручку дважды.

В рабочей папке редко лежит одна идеальная таблица. Заказы находятся отдельно, пользователи — в справочнике, а маркетинговый канал приходит ещё одним файлом. В pandas аналитический отчёт обычно собирается в три шага: понять зерно каждой таблицы, агрегировать факты и только потом соединить их по ключу.
Коротко
`groupby` отвечает на вопрос “как посчитать показатель по группам”. `merge` соединяет таблицы по ключу. `pivot_table` превращает длинный результат в компактную матрицу. Все три операции простые по синтаксису, но ошибки в зерне легко превращают 10 заказов в 100 или незаметно теряют пользователей.
- Перед операцией запиши, что означает одна строка в каждой таблице.
- Сначала агрегируй факты до нужного уровня, потом присоединяй справочники.
- Для контроля соединения проверяй количество строк и долю пропусков после merge.
- Не используй `sum` для показателя, который уже был агрегирован выше.
Начни с зерна таблиц
Представим учебный набор: в `orders` одна строка — один заказ, в `order_items` одна строка — одна товарная позиция, в `users` одна строка — один пользователь. Если присоединить `users` к `order_items`, строки не должны умножиться: для каждого user_id в справочнике должна быть максимум одна запись.
| Таблица | Одна строка | Ключ для соединения |
|---|---|---|
| orders | один заказ | order_id, user_id |
| order_items | одна позиция внутри заказа | order_id, product_id |
| users | один пользователь | user_id |
| products | один товар | product_id |
groupby: агрегируй факты по смысловой группе
В `groupby` сначала укажи измерение, затем выбери поля и агрегаты. Для продуктового отчёта часто нужны не только сумма выручки, но и количество заказов, уникальные покупатели и средний чек. Явные имена агрегатов читаются лучше, чем цепочка из нескольких промежуточных таблиц.
Пример показывает форму результата, а не реальные данные. Для решения добавь период и размер сегмента.
channel_report = (
orders.groupby('channel', as_index=False)
.agg(
orders=('order_id', 'nunique'),
buyers=('user_id', 'nunique'),
revenue=('revenue', 'sum'),
average_order=('revenue', 'mean'),
)
.sort_values('revenue', ascending=False)
)
channel_report['revenue_share'] = (
channel_report['revenue'] / channel_report['revenue'].sum()
)merge: соединяй таблицы после агрегации
Если тебе нужен канал в отчёте по заказам, сначала собери одну строку на заказ, а затем добавь атрибуты пользователя. `how="left"` сохраняет все строки левой таблицы и позволяет заметить пользователей, для которых справочник не нашёлся. `validate="many_to_one"` полезен как предохранитель: он упадёт, если справа ключ оказался неуникальным.
order_report = (
orders[['order_id', 'user_id', 'channel', 'revenue']]
.merge(
users[['user_id', 'plan', 'country']],
on='user_id',
how='left',
validate='many_to_one',
indicator=True,
)
)
missing_users = (
order_report['_merge'].eq('left_only').mean()
)Сравни количество строк до и после, долю _merge == left_only и уникальность ключа справа. Если число строк выросло неожиданно, это почти всегда сигнал о дубликатах в ключе или неверном зерне.
pivot_table: сделать отчёт читаемым
После группировки данные часто удобны для кода, но неудобны для просмотра. `pivot_table` раскладывает измерение по строкам и колонкам: например, выручку по каналам и планам. Это хороший последний шаг перед графиком или выгрузкой, но не замена проверке исходного зерна.
revenue_matrix = pd.pivot_table(
order_report,
index='channel',
columns='plan',
values='revenue',
aggfunc='sum',
fill_value=0,
)
revenue_matrix = revenue_matrix.sort_values('pro', ascending=False)Как не посчитать выручку дважды
Самая дорогая ошибка возникает, когда заказ присоединяют к таблице позиций, а затем суммируют сумму заказа. Один заказ с четырьмя позициями появится четыре раза, и revenue вырастет в четыре раза. Если нужна аналитика товаров, агрегируй `order_items` по order_id или используй сумму позиций, но не смешивай её с готовой суммой заказа.
- Сохрани контрольные числа: заказы, покупатели, выручка до соединения.
- После merge сравни `nunique(order_id)` с исходным значением.
- Для one-to-many заранее реши, нужен ли разворот на уровень позиции.
- Не заполняй пропуски нулём, пока не понял, означает ли пропуск отсутствие факта.
Материалы по теме
Как тестировать аналитические расчёты на Python и pandas
Практический гайд по тестам для аналитика: проверить метрики на маленьком датасете, поймать регрессию и защитить расчёт от тихих изменений.
Читать материал
Как ускорить pandas: память, типы и обработка больших файлов
Что делать, если pandas медленно работает или не помещает файл в память: категории, downcast, chunksize, Parquet и контроль размера данных.
Читать материал
Pandas apply и векторизация: как писать быстрее и понятнее
Когда использовать apply, почему векторные операции быстрее и как переписать медленный построчный расчёт в pandas.
Читать материал