Условие
В Excel прислали отчёт: колонки user_id, plan, 2024-01, 2024-02, ..., 2024-12 — по месяцам в каждой колонке выручка. Нужно перевести в «длинный» (tidy) формат: user_id, plan, month, revenue.
Решение
Подход
pd.melt (или метод df.melt) переводит набор «широких» колонок в две: имя переменной (var_name) и значение (value_name). Остальные колонки идут в id_vars.
Реализация
import pandas as pd
long = df.melt(
id_vars=['user_id', 'plan'],
value_vars=[c for c in df.columns if c.startswith('2024-')],
var_name='month',
value_name='revenue',
)
# Превратить строку '2024-03' в дату:
long['month'] = pd.to_datetime(long['month'] + '-01')
# Убрать пустые ячейки (если в широком формате они были NaN):
long = long.dropna(subset=['revenue'])Обратная операция
wide = long.pivot_table(
index=['user_id', 'plan'],
columns='month',
values='revenue',
aggfunc='sum',
).reset_index()Подводные камни
- Если
value_varsне задан —meltрасплавит все не-id_varsколонки, в том числе те, что не должны были туда попасть. - Типы данных в широких колонках смешанные (int+NaN → float) — после
meltколонкаrevenueтоже станет float; нужноastypeруками. - Имя месяца как строка плохо сортируется (
'2024-10' < '2024-2'), всегда переводите вdatetime. - Дубли по
(user_id, plan, month)после melt означают, что в широком формате не была обеспечена уникальность — проверятьduplicated().sum(). - Для нескольких метрик (revenue + clicks) удобнее
pd.wide_to_longс stub-именами.
Эталонный ответ
df.melt(id_vars=[...], value_vars=[...], var_name='month', value_name='revenue') — переводит «колонки-месяцы» в строки.