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

stack и unstack

Перенести уровень столбцов в индекс строк и обратно — основной приём для таблиц с MultiIndex.

Нужно знать:melt и pivotMultiIndex

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

Коротко

  • stack() переносит заголовки столбцов во внутренний уровень индекса: таблица становится длиннее, из DataFrame часто получается Series с MultiIndex.
  • unstack() делает обратное: внутренний уровень индекса становится столбцами.
  • level= выбирает уровень по номеру или имени; fill_value= заполняет появившиеся пустые ячейки.
  • Частый приём: groupby([a, b]).sum().unstack() — из двухуровневого результата сделать таблицу a × b.
  • В отличие от melt / pivot, здесь всё строится вокруг индекса, а не обычных столбцов.

Синтаксис

df.stack(level=-1)
s.unstack(level=-1, fill_value=None)
df.groupby(["a", "b"])["x"].sum().unstack(fill_value=0)

Параметры

ПараметрЧто делаетПо умолчанию
levelкакой уровень переносить: номер или имя; по умолчанию внутренний-1
fill_valueв unstack: чем заполнить сочетания, которых не былоNone

Примеры

Данные для примеровиспользуются во всех примерах ниже
wide = pd.DataFrame(
    {"jan": [120, 95], "feb": [130, 105]},
    index=pd.Index(["Омск", "Тула"], name="city"),
)
wide.columns.name = "month"
stack: столбцы в индекс
wide.stack()                             # столбцы → внутренний уровень индекса
Вывод
city  month
Омск  jan      120
      feb      130
Тула  jan       95
      feb      105
dtype: int64
unstack: индекс в столбцы
wide.stack().unstack()                   # внутренний уровень индекса → столбцы
Вывод
month  jan  feb
city
Омск   120  130
Тула    95  105
Другой уровень
wide.stack().unstack(level="city")       # в столбцы можно перенести любой уровень
Вывод
city   Омск  Тула
month
jan     120    95
feb     130   105
unstack после groupby
sales = pd.DataFrame({
    "city": ["Омск", "Омск", "Тула", "Тула", "Омск"],
    "drink": ["латте", "чай", "латте", "латте", "латте"],
    "cups": [30, 12, 18, 25, 20],
})
sales.groupby(["city", "drink"])["cups"].sum().unstack(fill_value=0)   # частый приём после groupby
Вывод
drink  латте  чай
city
Омск      50   12
Тула      43    0

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

stack сохраняет пропуски

stack с пропуском
with_gap = wide.astype(float)
with_gap.loc["Тула", "feb"] = np.nan
with_gap.stack()                         # pandas 3: строка с NaN сохраняется
Вывод
city  month
Омск  jan      120.0
      feb      130.0
Тула  jan       95.0
      feb        NaN
dtype: float64
Параметр dropnaошибка: ValueError
wide.stack(dropna=True)
Ошибка
ValueError: dropna must be unspecified as the new implementation does not introduce rows of NA values. This argument will be removed in a future version of pandas.

Чтобы убрать пропуски, вызовите .dropna() после stack().