Нашли ошибку?
продвинутыйpandas 3.0.6

MultiIndex

Индекс из нескольких уровней — город и месяц, магазин и товар — и как по нему выбирать данные.

Нужно знать:set_index и reset_indexГруппировка: groupby

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

Коротко

  • MultiIndex — метка строки из нескольких частей (уровней). Появляется после set_index([a, b]), groupby([a, b]), pivot_table, stack.
  • df.loc["Омск"] — все строки внешнего уровня; полная метка — кортеж df.loc[("Омск", "фев")].
  • xs("янв", level="month") — срез по любому уровню.
  • pd.IndexSlice — срезы по нескольким уровням сразу; индекс должен быть отсортирован (sort_index()).
  • swaplevel, droplevel, get_level_values — работа с уровнями; reset_index() — обратно в плоскую таблицу.

Синтаксис

df.set_index(["a", "b"])
df.loc["x"], df.loc[("x", "y")], df.loc[("x", "y"), "col"]
df.xs("y", level="b")
idx = pd.IndexSlice; df.sort_index().loc[idx[:, ["y1", "y2"]], :]
df.swaplevel(), df.droplevel("b"), df.index.get_level_values("a")

Параметры

ПараметрЧто делаетПо умолчанию
levelуровень по имени или номеру (в xs, droplevel, groupby, reset_index)обязательный
drop_levelв xs: убрать уровень, по которому сделан срезTrue

Примеры

Данные для примеровиспользуются во всех примерах ниже
sales = pd.DataFrame({
    "city":  ["Омск", "Омск", "Омск", "Тула", "Тула", "Сочи"],
    "month": ["янв", "фев", "мар", "янв", "фев", "янв"],
    "cups":  [120, 130, 110, 95, 105, 60],
    "rating": [4.5, 4.6, 4.4, 4.0, 4.1, 4.8],
}).set_index(["city", "month"])
Таблица с двумя уровнями
sales
Вывод
            cups  rating
city month
Омск янв     120     4.5
     фев     130     4.6
     мар     110     4.4
Тула янв      95     4.0
     фев     105     4.1
Сочи янв      60     4.8
Внешний уровень
sales.loc["Омск"]                          # все строки внешнего уровня
Вывод
       cups  rating
month
янв     120     4.5
фев     130     4.6
мар     110     4.4
Полная метка — кортеж
sales.loc[("Тула", "фев"), "cups"]         # полная метка — кортеж
Вывод
np.int64(105)
Срез по внутреннему уровню
sales.xs("янв", level="month")             # срез по внутреннему уровню
Вывод
      cups  rating
city
Омск   120     4.5
Тула    95     4.0
Сочи    60     4.8
IndexSlice
idx = pd.IndexSlice
sales.sort_index().loc[idx[:, ["янв", "фев"]], "cups"]   # любые города, два месяца
Вывод
city  month
Омск  янв      120
Сочи  янв       60
Тула  янв       95
Омск  фев      130
Тула  фев      105
Name: cups, dtype: int64
Имена и значения уровней
print(sales.index.names)
sales.index.get_level_values("city").unique()
Вывод
['city', 'month']
Index(['Омск', 'Тула', 'Сочи'], dtype='str', name='city')
Поменять или убрать уровень
print(sales.swaplevel().sort_index().head(3))
sales.droplevel("month").head(3)
Вывод
            cups  rating
month city
мар   Омск   110     4.4
фев   Омск   130     4.6
      Тула   105     4.1
      cups  rating
city
Омск   120     4.5
Омск   130     4.6
Омск   110     4.4
Группировка по уровню
sales.groupby(level="city")["cups"].sum()  # группировка по уровню индекса
Вывод
city
Омск    360
Сочи     60
Тула    200
Name: cups, dtype: int64

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

Срезы по неотсортированному индексу

Срез по меткам уровня (loc["Омск":"Тула"], IndexSlice) требует отсортированного индекса, иначе UnsortedIndexError. Выбор одной метки работает и без сортировки. Вызывайте sort_index() перед срезами.

Срез по неотсортированному индексуошибка: UnsortedIndexError
sales.loc["Омск":"Тула"]                  # «Сочи» стоит после «Тулы» — индекс не отсортирован
Ошибка
UnsortedIndexError: 'Key length (1) was greater than MultiIndex lexsort depth (0)'
После sort_index
sales.sort_index().loc["Омск":"Тула"]
Вывод
            cups  rating
city month
Омск мар     110     4.4
     фев     130     4.6
     янв     120     4.5
Сочи янв      60     4.8
Тула фев     105     4.1
     янв      95     4.0

MultiIndex там, где проще плоская таблица

Для экспорта, merge и большинства операций удобнее обычные столбцы. Если уровни мешают — reset_index().

Обратно в столбцы
sales.reset_index().head(3)                # обратно в обычные столбцы
Вывод
   city month  cups  rating
0  Омск   янв   120     4.5
1  Омск   фев   130     4.6
2  Омск   мар   110     4.4