Урок по треку Python для анализа данных 6 мин

pivot, melt и сводные таблицы

pivot и pivot_table, melt, stack и unstack, crosstab, широкий и длинный формат.

Одни и те же данные можно хранить «длинными» и «широкими». Умение быстро переходить между формами экономит половину времени на live-задаче.

Длинный и широкий формат

Длинный (long, tidy): одна строка на одно наблюдение.

datecityrevenue
01.10Москва100
01.10Казань40
02.10Москва120

Широкий (wide): значения одной переменной разложены по колонкам.

dateМоскваКазань
01.1010040
02.10120NaN

Длинный удобен для groupby, merge и seaborn. Широкий удобен для отчёта, корреляций между колонками и тепловой карты.

pivot: длинный в широкий без агрегации

wide = df.pivot(index='date', columns='city', values='revenue')

pivot только переставляет значения. Если пара (date, city) встречается дважды, он выбросит ошибку ValueError: Index contains duplicate entries: непонятно, какое из двух значений поставить в ячейку.

pivot_table: со сводкой

pd.pivot_table(df, index='date', columns='city', values='revenue',
               aggfunc='sum', fill_value=0, margins=True)
  • aggfunc по умолчанию 'mean'. Частая ошибка: хотели сумму выручки, получили среднее.
  • fill_value заменяет пустые ячейки.
  • margins=True добавляет строку и колонку All с итогами.

Дубликаты для pivot_table не проблема, он их агрегирует.

melt: широкий в длинный

long = wide.reset_index().melt(id_vars='date', var_name='city', value_name='revenue')

id_vars остаются как есть, все остальные колонки «расплавляются» в пары (var_name, value_name). Типичный случай: выгрузка из Excel с колонками-месяцами, которую нужно превратить в строки для groupby.

stack и unstack

Работают с индексом:

  • unstack() переносит последний уровень индекса строк в колонки;
  • stack() делает обратное.
s = df.groupby(['date', 'city'])['revenue'].sum()  # Series с MultiIndex
s.unstack('city', fill_value=0)                     # то же, что pivot_table

Связка groupby + unstack встречается в реальном коде чаще, чем pivot_table.

crosstab: таблица сопряжённости

pd.crosstab(df['segment'], df['converted'], normalize='index')

Считает частоты по двум признакам. normalize='index' превращает их в доли по строкам: например, конверсию каждого сегмента. Это же вход для теста хи-квадрат.

Когортная таблица

Классика собеса: retention по когортам. Это длинная таблица (cohort_month, months_since, users), развёрнутая через pivot, а потом поделённая на первый столбец:

cohorts = counts.pivot(index='cohort', columns='age', values='users')
retention = cohorts.div(cohorts[0], axis=0).round(3)

Частые ошибки

  • pivot на данных с дублями ключа: нужен pivot_table или предварительный groupby.
  • Забыли aggfunc='sum' в pivot_table и получили среднее.
  • После pivot колонки стали индексом с именем city, и дальше путаница: помогает reset_index() и columns.name = None.
  • После pivot_table с несколькими values колонки становятся MultiIndex.
  • Пустые ячейки NaN ломают суммы по строкам в отчёте, если ожидали 0.

Что спросят на собесе

  • Чем pivot отличается от pivot_table?
  • Как превратить колонки-месяцы в строки?
  • Что делают stack и unstack?
  • Как построить когортную таблицу retention?
  • Какой aggfunc у pivot_table по умолчанию?

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.