Нашли ошибку?
среднийpandas 3.0.6

melt и pivot

Переложить таблицу из широкого формата (месяцы в столбцах) в длинный (месяц — значение в столбце) и обратно.

Нужно знать:DataFrameset_index и reset_index

Содержание статьи

Коротко

  • Широкий формат: у каждой сущности одна строка, показатели по периодам — в отдельных столбцах. Длинный: одна строка на пару «сущность — период».
  • melt(id_vars=…) — из широкого в длинный: остальные столбцы превращаются в пары «имя столбца (var_name) — значение (value_name)».
  • pivot(index, columns, values) — из длинного в широкий, без агрегации: каждому сочетанию должно соответствовать одно значение.
  • Если значений на сочетание несколько, нужен pivot_table с aggfunc.
  • Длинный формат удобен для groupby, фильтров и графиков; широкий — для чтения человеком.

Синтаксис

df.melt(id_vars=None, value_vars=None, var_name="variable", value_name="value")
df.pivot(index=None, columns=None, values=None)

Параметры

ПараметрЧто делаетПо умолчанию
id_varsв melt: столбцы, которые остаются как есть (идентификаторы)None
value_varsв melt: какие столбцы «расплавить»; по умолчанию все, кроме id_varsNone
var_name / value_nameв melt: имена новых столбцов для имён и значений"variable" / "value"
index / columns / valuesв pivot: что станет строками, столбцами и значениямиобязательный

Примеры

Данные для примеровиспользуются во всех примерах ниже
wide = pd.DataFrame({
    "city": ["Омск", "Тула"],
    "jan": [120, 95],
    "feb": [130, 105],
})
melt: из широкого в длинный
long = wide.melt(id_vars="city", var_name="month", value_name="cups")
long
Вывод
   city month  cups
0  Омск   jan   120
1  Тула   jan    95
2  Омск   feb   130
3  Тула   feb   105
pivot: из длинного в широкий
long = wide.melt(id_vars="city", var_name="month", value_name="cups")
long.pivot(index="city", columns="month", values="cups")
Вывод
month  feb  jan
city
Омск   130  120
Тула   105   95
Только часть столбцов
wide.melt(id_vars="city", value_vars=["feb"])    # только часть столбцов
Вывод
   city variable  value
0  Омск      feb    130
1  Тула      feb    105
Обратно в обычную таблицу
long = wide.melt(id_vars="city", var_name="month", value_name="cups")
back = long.pivot(index="city", columns="month", values="cups").reset_index()
back.columns.name = None                          # убрать имя «month» над столбцами
back
Вывод
   city  feb  jan
0  Омск  130  120
1  Тула  105   95
Зачем длинный формат
long = wide.melt(id_vars="city", var_name="month", value_name="cups")
long.groupby("month")["cups"].sum()               # длинный формат удобен для groupby и графиков
Вывод
month
feb    235
jan    215
Name: cups, dtype: int64

Подводные камни

pivot и повторяющиеся сочетания

pivot не знает, что делать с двумя значениями для одной ячейки.

Два значения на ячейкуошибка: ValueError
dup = pd.DataFrame({"city": ["Омск", "Омск"], "month": ["jan", "jan"], "cups": [120, 80]})
dup.pivot(index="city", columns="month", values="cups")   # два значения на одну ячейку
Ошибка
ValueError: Index contains duplicate entries, cannot reshape
pivot_table с агрегацией
dup = pd.DataFrame({"city": ["Омск", "Омск"], "month": ["jan", "jan"], "cups": [120, 80]})
dup.pivot_table(index="city", columns="month", values="cups", aggfunc="sum")
Вывод
month  jan
city
Омск   200

pivot сортирует столбцы

Новые столбцы идут по алфавиту: в примере выше feb оказался раньше jan. Нужный порядок задайте явно: wide[["city", "jan", "feb"]], или сделайте столбец месяца упорядоченной категорией.