pivot_table в Pandas — полный гайд с примерами

Коротко

pivot_table — аналог сводной таблицы из Excel в pandas. Берёт «длинные» данные и разворачивает их в таблицу, где строки и столбцы — категории, а значения — агрегаты. На собеседованиях аналитиков встречается в задачах на трансформацию данных и часто идёт в паре с groupby.

Базовый синтаксис

import pandas as pd

df = pd.DataFrame({
    'month': ['янв', 'янв', 'фев', 'фев', 'янв', 'фев'],
    'category': ['еда', 'транспорт', 'еда', 'транспорт', 'еда', 'транспорт'],
    'amount': [500, 150, 600, 200, 450, 180]
})

pd.pivot_table(
    df,
    values='amount',       # что агрегируем
    index='month',         # строки
    columns='category',    # столбцы
    aggfunc='sum'          # функция агрегации
)
# category   еда  транспорт
# month
# фев        600        380
# янв        950        150

Четыре ключевых параметра: values — столбец с числами, index — по чему группировать в строках, columns — по чему раскладывать в столбцы, aggfunc — как агрегировать.

aggfunc — функция агрегации

По умолчанию aggfunc='mean'. Это частый источник ошибок — многие ожидают сумму.

# Сумма
pd.pivot_table(df, values='amount', index='month', columns='category', aggfunc='sum')

# Количество
pd.pivot_table(df, values='amount', index='month', columns='category', aggfunc='count')

# Несколько функций сразу
pd.pivot_table(df, values='amount', index='month', columns='category',
               aggfunc=['sum', 'mean', 'count'])

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

pd.pivot_table(df, index='month',
               aggfunc={'amount': 'sum', 'category': 'count'})

Также принимает любую функцию: aggfunc=np.median, aggfunc=lambda x: x.quantile(0.9).

margins — итоговая строка и столбец

margins=True добавляет строку и столбец «All» с общими итогами — как в Excel.

pd.pivot_table(df, values='amount', index='month', columns='category',
               aggfunc='sum', margins=True, margins_name='Итого')
# category   еда  транспорт  Итого
# month
# фев        600        380    980
# янв        950        150   1100
# Итого     1550        530   2080

fill_value — заполнение пропусков

Если для какой-то комбинации строка×столбец нет данных, в ячейке будет NaN. Параметр fill_value заменяет пропуски:

pd.pivot_table(df, values='amount', index='month', columns='category',
               aggfunc='sum', fill_value=0)

Multi-index: несколько уровней

Можно передать список столбцов в index или columns для создания многоуровневой сводной:

orders = pd.DataFrame({
    'region': ['МСК', 'МСК', 'СПБ', 'СПБ', 'МСК', 'СПБ'],
    'channel': ['web', 'app', 'web', 'app', 'web', 'app'],
    'device': ['desktop', 'mobile', 'desktop', 'mobile', 'mobile', 'mobile'],
    'revenue': [1000, 800, 600, 500, 900, 700]
})

pd.pivot_table(orders, values='revenue', index=['region', 'channel'],
               columns='device', aggfunc='sum', fill_value=0)
# device              desktop  mobile
# region channel
# МСК    app                0     800
#        web             1000     900
# СПБ    app                0    1200
#        web              600       0

pivot vs pivot_table

pivot — простая перестановка без агрегации. Работает только если для каждой комбинации index×columns ровно одно значение. Если есть дубликаты — упадёт с ошибкой.

pivot_table — то же самое, но с агрегацией. Дубликаты не проблема — они просто агрегируются выбранной функцией.

# pivot — упадёт, если есть дубликаты
df.pivot(index='month', columns='category', values='amount')  # ValueError!

# pivot_table — агрегирует дубликаты
pd.pivot_table(df, index='month', columns='category', values='amount', aggfunc='sum')  # OK

Правило простое: если данные уже уникальны по ключу — pivot. Во всех остальных случаях — pivot_table.

pivot_table vs groupby

pivot_table — это по сути groupby + unstack. Результат одинаковый, но формат разный:

# groupby — длинный формат
df.groupby(['month', 'category'])['amount'].sum()
# month  category
# фев    еда            600
#        транспорт      380
# янв    еда            950
#        транспорт      150

# pivot_table — широкий формат
pd.pivot_table(df, values='amount', index='month', columns='category', aggfunc='sum')
# category   еда  транспорт
# фев        600        380
# янв        950        150

Когда что использовать: groupby удобнее для дальнейших вычислений и цепочек. pivot_table — когда нужна читаемая таблица для отчёта или визуализации.

Практические примеры

Выручка по месяцам и категориям

sales = pd.DataFrame({
    'date': pd.date_range('2025-01-01', periods=120, freq='D'),
    'category': ['подписка', 'разовая', 'подписка', 'разовая'] * 30,
    'revenue': [500, 200, 550, 180] * 30
})
sales['month'] = sales['date'].dt.to_period('M')

report = pd.pivot_table(sales, values='revenue', index='month',
                         columns='category', aggfunc='sum', margins=True)

Конверсия по каналу и устройству

events = pd.DataFrame({
    'channel': ['organic', 'paid', 'organic', 'paid', 'organic', 'paid'],
    'device': ['desktop', 'desktop', 'mobile', 'mobile', 'desktop', 'mobile'],
    'sessions': [1000, 800, 1200, 600, 950, 700],
    'purchases': [50, 60, 30, 25, 45, 20]
})

conv = pd.pivot_table(events, values=['sessions', 'purchases'],
                       index='channel', columns='device', aggfunc='sum')
conv['cr'] = conv['purchases'] / conv['sessions']

Типичные ошибки

Забыть, что aggfunc по умолчанию — mean. Пишут pivot_table(df, index='month', columns='cat', values='amount') и удивляются, что числа не сходятся. Всегда указывайте aggfunc явно.

Путать pivot и pivot_table. pivot не агрегирует, и при дубликатах бросает ValueError. Если не уверены в уникальности — используйте pivot_table.

Игнорировать NaN в результате. Комбинации без данных дают NaN. Если дальше идут вычисления — используйте fill_value=0 или fillna().

Не делать reset_index после pivot_table. Результат имеет index из группирующего столбца, а columns — из разворачиваемого. Для дальнейших операций часто нужен reset_index().

Вопросы с собеседований

Чем отличается pivot от pivot_table? pivot — простая перестановка без агрегации, требует уникальных комбинаций index×columns. pivot_table — с агрегацией, обрабатывает дубликаты через aggfunc. По сути pivot_table — это groupby + unstack.

Какой aggfunc по умолчанию в pivot_table? mean. Это частый подвох на собеседовании. Если нужна сумма — указывайте aggfunc='sum' явно.

Как добавить итоги в сводную таблицу? margins=True добавляет строку и столбец с общими итогами. Имя по умолчанию — «All», можно задать своё через margins_name.

Когда лучше pivot_table, а когда groupby? pivot_table — когда нужна читаемая двумерная таблица: строки одна категория, столбцы другая. groupby — когда нужен длинный формат для дальнейших вычислений, джойнов или визуализации. Результат один и тот же, разница в форме.

Как применить несколько агрегирующих функций? Передать список в aggfunc: aggfunc=['sum', 'mean', 'count']. Результат будет с MultiIndex в столбцах. Или передать словарь для разных столбцов: aggfunc={'revenue': 'sum', 'orders': 'count'}.


Потренируйтесь решать задачи — откройте тренажёр с 1500+ вопросами для подготовки к собеседованиям аналитиков.

FAQ

Как убрать MultiIndex из столбцов после pivot_table?

После pivot_table столбцы часто имеют MultiIndex. Чтобы «сплющить»: df.columns = ['_'.join(col).strip() for col in df.columns.values]. Или используйте droplevel(), если лишний уровень не нужен.

Можно ли развернуть pivot_table обратно в длинный формат?

Да — melt(). Это обратная операция к pivot_table: pd.melt(pivot_df, ignore_index=False).reset_index().

Как отсортировать результат pivot_table?

sort_values() по нужному столбцу или sort_index() по индексу. Например: result.sort_values(('revenue', 'sum'), ascending=False).

Где потренировать pivot_table на задачах?

В тренажёре Карьерник — задачи по pandas из реальных собеседований. Также смотрите groupby в pandas, шпаргалку по pandas и примеры вопросов.