С индексом мы встречаемся с первого урока, но до сих пор он был в основном номерами строк. Иногда метки
оказывались осмысленными — города после группировки, названия показателей в describe, — и тогда по ним
было удобно искать. Пора взять это в свои руки: назначать индекс самим.
Индекс как ключ
В справочнике товаров у каждой строки есть естественный «адрес» — код товара. Но сейчас он лежит в обычном столбце, а в индексе — номера:
import pandas as pd
products = pd.read_csv("data/products.csv")
products.head(3)| product_id | name | category | price | cost | |
|---|---|---|---|---|---|
| 0 | P01 | Эспрессо-смесь 1 кг | Кофе | 1450 | 870 |
| 1 | P02 | Колумбия 250 г | Кофе | 690 | 380 |
| 2 | P03 | Эфиопия 250 г | Кофе | 790 | 430 |
Чтобы найти товар по коду, нужен фильтр:
products[products["product_id"] == "P05"]| product_id | name | category | price | cost | |
|---|---|---|---|---|---|
| 4 | P05 | Кофе без кофеина 250 г | Кофе | 720 | 410 |
Метод set_index делает столбец индексом:
catalog = products.set_index("product_id")
catalog.head(3)| name | category | price | cost | |
|---|---|---|---|---|
| product_id | ||||
| P01 | Эспрессо-смесь 1 кг | Кофе | 1450 | 870 |
| P02 | Колумбия 250 г | Кофе | 690 | 380 |
| P03 | Эфиопия 250 г | Кофе | 790 | 430 |
Столбец product_id переехал влево: теперь это подписи строк, а его название стоит над индексом. Искать
по метке можно напрямую — через loc:
print(catalog.loc["P05", "price"])
catalog.loc["P05"]720
name Кофе без кофеина 250 г category Кофе price 720 cost 410 Name: P05, dtype: object
catalog.loc["P05", "price"] читается как адрес в электронной таблице: строка P05, столбец price.
В таблице products пять столбцов. На сколько столбцов меньше в catalog = products.set_index("product_id")?
catalog их четыре. Индекс в число столбцов не входит.Из этого следует ловушка: как к столбцу к индексу обратиться уже нельзя.
catalog["product_id"]KeyError: 'product_id'
print(catalog.index.name)
print(list(catalog.index[:4]))
print("P05" in catalog.index, "P99" in catalog.index)product_id ['P01', 'P02', 'P03', 'P04'] True False
Как и остальные методы, set_index возвращает новую таблицу — products не изменилась.
То же — со справочником клиентов. Сохраните в clients таблицу customers с customer_id в индексе.
Достаньте город клиента C012 и сохраните в c012_city. В picked сохраните строки клиентов C005, C100,
C200 — только столбцы name и segment.
customers.set_index("customer_id"); ячейка — clients.loc["C012", "city"].clients.loc[["C005", "C100", "C200"], ["name", "segment"]].Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
customers = pd.read_csv("data/customers.csv")
clients = ...
c012_city = ...
picked = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
customers = pd.read_csv("data/customers.csv")
clients = customers.set_index("customer_id")
c012_city = clients.loc["C012", "city"]
picked = clients.loc[["C005", "C100", "C200"], ["name", "segment"]]Метки, которой нет, loc не находит:
catalog.loc["P99"]KeyError: 'P99'
Туда и обратно
Вернуть индекс в обычные столбцы умеет знакомый reset_index:
catalog.reset_index().head(2)| product_id | name | category | price | cost | |
|---|---|---|---|---|---|
| 0 | P01 | Эспрессо-смесь 1 кг | Кофе | 1450 | 870 |
| 1 | P02 | Колумбия 250 г | Кофе | 690 | 380 |
Это тот же приём, что после группировки: там ключи группировки тоже оказывались в индексе.
А назначить индекс можно сразу при чтении файла — параметром index_col:
pd.read_csv("data/products.csv", index_col="product_id").head(2)| name | category | price | cost | |
|---|---|---|---|---|
| product_id | ||||
| P01 | Эспрессо-смесь 1 кг | Кофе | 1450 | 870 |
| P02 | Колумбия 250 г | Кофе | 690 | 380 |
Срез по меткам
Индексом может быть любой столбец. Возьмём название товара:
named = products.set_index("name")
named.loc["Зефир", "price"]np.int64(210)
Срезы по меткам вы знаете с урока о loc. Если индекс отсортирован, границами среза могут быть
и значения, которых в индексе нет, — например, отдельные буквы. Срез от "Т" до "Ф" вернёт все названия,
которые по алфавиту лежат между этими буквами:
named = named.sort_index()
named.loc["Т":"Ф", "price"]name Термокружка 1150 Травяной сбор 50 г 260 Турка медная 2400 Улун 100 г 540 Name: price, dtype: int64
«Френч-пресс» и «Фильтры» не попали: по алфавиту они идут после строки "Ф" — как в словаре слово
«фильтр» стоит после буквы «ф». Поэтому, чтобы захватить все названия на «Ф», правой границей берут
следующую букву — "Х".
На неотсортированном индексе такой срез вызовет ошибку — pandas не сможет определить, где начинается и где
кончается диапазон. Сначала sort_index().
Сохраните в by_name таблицу products с названием товара в индексе, отсортированную по индексу.
В k_products сохраните товары, названия которых начинаются на «К», — срезом меток. В k_mean сохраните
их среднюю цену.
products.set_index("name").sort_index().Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
by_name = ...
k_products = ...
k_mean = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
by_name = products.set_index("name").sort_index()
k_products = by_name.loc["К":"Л"]
k_mean = k_products["price"].mean()Когда метки повторяются
Метки индекса не обязаны быть уникальными. Сделаем индексом номер заказа — а в заказе бывает несколько строк:
orders = pd.read_csv("data/shop_orders.csv").set_index("order_id")
print(orders.index.is_unique)
orders.loc[10001]False
| date | customer_id | city | channel | category | product | price | quantity | |
|---|---|---|---|---|---|---|---|---|
| order_id | ||||||||
| 10001 | 2025-01-01 | C095 | Москва | маркетплейс | Аксессуары | Кофемолка ручная | 3200 | 2 |
| 10001 | 2025-01-01 | C095 | Москва | маркетплейс | Чай | Травяной сбор 50 г | 260 | 1 |
loc[10001] вернул таблицу из двух строк — всех с этой меткой. Удобно: «покажи заказ 10001» — одна
короткая запись. Но есть подвох: результат меняет тип в зависимости от данных.
print(type(orders.loc[10001]))
print(type(orders.loc[11576]))<class 'pandas.DataFrame'> <class 'pandas.Series'>
Если метка встречается один раз, loc возвращает строку как Series, если несколько — таблицу. Код, написанный
в расчёте на одно, сломается на другом. Поэтому индекс с повторами хорош для просмотра, а для расчётов
надёжнее уникальный ключ — или обычные столбцы и groupby.
Сохраните в order_10140 строки заказа с номером 10140, в n_lines — их число, в order_sum — сумму заказа
(цена × количество по всем его строкам). Затем верните номер заказа в столбцы: сохраните в flat таблицу
orders с обычным индексом.
orders.loc[10140] — все строки с этой меткой.orders.reset_index() — без drop=True, иначе номера заказов пропадут.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
order_10140 = ...
n_lines = ...
order_sum = ...
flat = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
order_10140 = orders.loc[10140]
n_lines = len(order_10140)
order_sum = (order_10140["price"] * order_10140["quantity"]).sum()
flat = orders.reset_index()Итоги
set_index("столбец")делает столбец индексом;reset_index()возвращает его в столбцы.таблица.loc[метка]— строка по метке;таблица.loc[метка, "столбец"]— ячейка.index_col="столбец"вread_csvназначает индекс при загрузке.- Срез по значениям, которых нет среди меток (буквы, неполные названия), работает только на отсортированном индексе; конец среза входит.
- Индекс может содержать повторы — тогда
locвозвращает несколько строк.