Как создать pivot table в pandas
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это нужно
Pivot table — наследие Excel, которое в pandas стало ещё мощнее. Строки → колонки: из длинного long-формата (transaction per row) в wide (month × category). Это нужно для читаемых отчётов, heatmap retention-таблиц и feature engineering в ML.
В аналитике pivot используется постоянно: выручка по месяцам × категориям, conversion по устройствам × каналам, retention по cohort × period. Без pivot эти данные остаются таблицей длиннющих записей, которую сложно читать и визуализировать.
В статье — три функции pandas и когда что выбрать:
pivot_table— с агрегацией (самое частое)pivot— без агрегации (если уже уникальные)crosstab— упрощение для frequency tables- Несколько агрегатов и несколько values
- Итоги (margins) и fill_value для NaN
- Unpivot через
melt— обратная операция - Построение heatmap на основе pivot
- Связь с SQL
PIVOT
Три функции
pd.pivot_table— с агрегацией (mean, sum и т.д.)pd.pivot— без агрегации (чистая перекладка)pd.crosstab— упрощённый pivot для частот
1. pivot_table — самый частый
import pandas as pd
df = pd.DataFrame({
'month': ['Jan', 'Jan', 'Feb', 'Feb', 'Jan'],
'category': ['A', 'B', 'A', 'B', 'A'],
'amount': [100, 200, 150, 250, 120]
})
pivot = df.pivot_table(
values='amount',
index='month',
columns='category',
aggfunc='sum'
)Результат:
category A B
month
Jan 220 200
Feb 150 2502. Параметры pivot_table
Несколько values
df.pivot_table(
values=['amount', 'qty'],
index='month',
columns='category',
aggfunc='sum'
)Несколько агрегатов
df.pivot_table(
values='amount',
index='month',
aggfunc=['sum', 'mean', 'count']
)Разные агрегаты на разные колонки
df.pivot_table(
values=['amount', 'qty'],
index='month',
aggfunc={'amount': 'sum', 'qty': 'mean'}
)Заполнить NaN
df.pivot_table(values='amount', index='month', columns='category', fill_value=0)Итоги
df.pivot_table(values='amount', index='month', columns='category',
aggfunc='sum', margins=True, margins_name='Total')3. pivot без агрегации
Если данные уже уникальны (без дублей по (index, columns)):
df = pd.DataFrame({
'month': ['Jan', 'Feb', 'Jan', 'Feb'],
'category': ['A', 'A', 'B', 'B'],
'amount': [100, 150, 200, 250]
})
df.pivot(index='month', columns='category', values='amount')pivot быстрее, но падает при дублях. Если дубли есть — pivot_table с агрегацией.
4. crosstab — упрощение
# подсчёт частот
pd.crosstab(df['country'], df['category'])
# с агрегацией
pd.crosstab(
df['country'],
df['category'],
values=df['amount'],
aggfunc='sum'
)
# нормализация (проценты)
pd.crosstab(df['country'], df['category'], normalize='index') # по строкам
pd.crosstab(df['country'], df['category'], normalize='columns') # по столбцам
pd.crosstab(df['country'], df['category'], normalize='all') # глобально5. Unpivot (обратная операция) — melt
# было:
# month | A | B
# Jan | 220 | 200
# Feb | 150 | 250
pivot.reset_index().melt(
id_vars='month',
var_name='category',
value_name='amount'
)
# стало:
# month | category | amount
# Jan | A | 220
# Jan | B | 200
# Feb | A | 150
# Feb | B | 2506. MultiIndex в pivot
# несколько колонок в index
df.pivot_table(
values='amount',
index=['country', 'month'],
columns='category',
aggfunc='sum'
)Получается hierarchical MultiIndex.
7. Stacked bars / heatmaps
Результат pivot легко визуализируется:
import matplotlib.pyplot as plt
import seaborn as sns
pivot = df.pivot_table(values='amount', index='month', columns='category', aggfunc='sum')
# stacked bar
pivot.plot(kind='bar', stacked=True)
plt.show()
# heatmap
sns.heatmap(pivot, annot=True, cmap='YlOrRd')
plt.show()8. pivot_table vs groupby
# через pivot_table
df.pivot_table(values='amount', index='month', columns='category', aggfunc='sum')
# через groupby + unstack
df.groupby(['month', 'category'])['amount'].sum().unstack('category')Результаты одинаковые. Pivot_table — синтаксический сахар.
9. В SQL эквивалент
-- pivot через CASE в SQL
SELECT
month,
SUM(CASE WHEN category = 'A' THEN amount ELSE 0 END) AS cat_a,
SUM(CASE WHEN category = 'B' THEN amount ELSE 0 END) AS cat_b
FROM df
GROUP BY month;Подробнее: PIVOT UNPIVOT SQL.
10. Типовые use cases
Продажи по месяцам × категориям
df.pivot_table(values='revenue', index='month', columns='category', aggfunc='sum')Retention table
df.pivot_table(values='user_id', index='cohort_month', columns='period', aggfunc='nunique')Conversion по каналам
df.pivot_table(values='converted', index='day', columns='channel', aggfunc='mean')Частые ошибки
1. Дубликаты в pivot
# если есть дубли по (index, columns) — ошибка
df.pivot(index='month', columns='category', values='amount')
# ValueError: Index contains duplicate entries
# правильно
df.pivot_table(values='amount', index='month', columns='category', aggfunc='sum')2. Неправильный aggfunc
По умолчанию aggfunc='mean'. Часто хотят sum или count.
3. NaN в результате
fill_value=0 для замены пропусков.
4. MultiIndex путает
После pivot с несколькими уровнями — reset_index() для плоского DataFrame.
5. Медленно на больших данных
Для огромных DataFrame — делайте pivot в базе (SQL) или через ClickHouse.
Связанные темы
FAQ
pivot или pivot_table?
pivot_table — с агрегацией. pivot — без (если данные уже уникальны).
crosstab зачем?
Упрощённый синтаксис для частот и частот с агрегацией. Меньше кода.
melt — это unpivot?
Да. Превращает wide формат в long.
Можно ли несколько колонок в values?
Да: values=['amount', 'qty'].
Тренируйте pandas — откройте тренажёр с 1500+ вопросами для собесов.