Группировка редко бывает последним шагом. Её результат обычно нужно отсортировать, отфильтровать, соединить с другой таблицей или показать в отчёте. Для этого удобнее обычная таблица — с названиями групп в столбце, а не в индексе.
Из индекса — в столбец
Результат группировки — Series: города стали метками индекса.
import pandas as pd
orders = pd.read_csv("data/shop_orders.csv")
orders["revenue"] = orders["price"] * orders["quantity"]
by_city = orders.groupby("city")["revenue"].sum()
by_citycity Екатеринбург 423350 Казань 440150 Москва 1310110 Новосибирск 324090 Санкт-Петербург 803720 Name: revenue, dtype: int64
Знакомый reset_index без drop=True не выбрасывает старый индекс, а делает его обычным столбцом:
by_city.reset_index()| city | revenue | |
|---|---|---|
| 0 | Екатеринбург | 423350 |
| 1 | Казань | 440150 |
| 2 | Москва | 1310110 |
| 3 | Новосибирск | 324090 |
| 4 | Санкт-Петербург | 803720 |
Получилась таблица из двух столбцов с обычным индексом 0, 1, 2… Того же добиваются сразу — параметром
as_index=False в groupby («не делать ключ индексом»):
orders.groupby("city", as_index=False)["revenue"].sum()| city | revenue | |
|---|---|---|
| 0 | Екатеринбург | 423350 |
| 1 | Казань | 440150 |
| 2 | Москва | 1310110 |
| 3 | Новосибирск | 324090 |
| 4 | Санкт-Петербург | 803720 |
У результата size() нет названия, и столбец с числами получил бы имя 0. Название задаёт параметр
name в reset_index:
orders.groupby("city").size().reset_index(name="lines")| city | lines | |
|---|---|---|
| 0 | Екатеринбург | 312 |
| 1 | Казань | 341 |
| 2 | Москва | 948 |
| 3 | Новосибирск | 258 |
| 4 | Санкт-Петербург | 589 |
Сохраните в category_table таблицу из двух столбцов — category и revenue (сумма выручки по категории).
Строки — по убыванию выручки, индекс — с нуля подряд.
orders.groupby("category", as_index=False)["revenue"].sum()..sort_values("revenue", ascending=False).reset_index(drop=True) — здесь уже с drop=True: старые номера строк не нужны.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
category_table = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
category_table = orders.groupby("category", as_index=False)["revenue"].sum()
category_table = category_table.sort_values("revenue", ascending=False).reset_index(drop=True)Два ключа — плоская таблица
Особенно удобен as_index=False при группировке по двум столбцам: вместо индекса из двух уровней получается
простая «длинная» таблица — по строке на каждую пару.
pairs = orders.groupby(["city", "channel"], as_index=False)["revenue"].sum()
pairs.head(4)| city | channel | revenue | |
|---|---|---|---|
| 0 | Екатеринбург | маркетплейс | 83080 |
| 1 | Екатеринбург | приложение | 150240 |
| 2 | Екатеринбург | сайт | 190030 |
| 3 | Казань | маркетплейс | 113670 |
С ней работают обычными приёмами — например, фильтром:
pairs[pairs["revenue"] > 300000]| city | channel | revenue | |
|---|---|---|---|
| 6 | Москва | маркетплейс | 343390 |
| 7 | Москва | приложение | 334240 |
| 8 | Москва | сайт | 632480 |
| 14 | Санкт-Петербург | сайт | 339660 |
Что выведет
print(orders.groupby(["city", "channel"], as_index=False)["revenue"].sum().shape)?
city и channel стали обычными столбцами благодаря as_index=False, третий — revenue.Сохраните в pair_table таблицу по парам «город, канал» со столбцами city, channel, total (выручка)
и orders (число разных заказов). В weak сохраните пары, где заказов меньше 55, — от меньшего числа
заказов к большему.
orders.groupby(["city", "channel"], as_index=False).agg(total=("revenue", "sum"), orders=("order_id", "nunique")).pair_table[pair_table["orders"] < 55].sort_values("orders").Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
pair_table = ...
weak = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
pair_table = orders.groupby(["city", "channel"], as_index=False).agg(total=("revenue", "sum"), orders=("order_id", "nunique"))
weak = pair_table[pair_table["orders"] < 55].sort_values("orders")Лучшие в каждой группе
«Два самых продаваемых товара в каждой категории» — частый вопрос. nlargest(2) здесь не подходит: он
вернёт два товара на всю таблицу. Решение — в два шага.
Шаг первый: посчитать выручку каждого товара и отсортировать всю таблицу по убыванию.
products = orders.groupby(["category", "product"], as_index=False)["revenue"].sum()
products = products.sort_values("revenue", ascending=False)
products.head(5)| category | product | revenue | |
|---|---|---|---|
| 7 | Кофе | Эфиопия 250 г | 426600 |
| 3 | Кофе | Бразилия 1 кг | 375390 |
| 6 | Кофе | Эспрессо-смесь 1 кг | 372650 |
| 4 | Кофе | Колумбия 250 г | 372600 |
| 5 | Кофе | Кофе без кофеина 250 г | 367200 |
Шаг второй: сгруппировать по категории и взять head(2). После groupby метод head берёт первые строки
каждой группы — а раз таблица отсортирована, первые и есть лучшие:
products.groupby("category").head(2)| category | product | revenue | |
|---|---|---|---|
| 7 | Кофе | Эфиопия 250 г | 426600 |
| 3 | Кофе | Бразилия 1 кг | 375390 |
| 18 | Чай | Улун 100 г | 194940 |
| 16 | Чай | Зелёный чай 100 г | 167700 |
| 9 | Посуда | Турка медная | 139200 |
| 10 | Посуда | Френч-пресс | 111510 |
| 0 | Аксессуары | Кофемолка ручная | 102400 |
| 13 | Сладости | Миндаль в шоколаде | 82880 |
| 12 | Сладости | Зефир | 53760 |
| 1 | Аксессуары | Термокружка | 48300 |
Строки остались в порядке общей сортировки. Чтобы категории шли подряд, результат сортируют ещё раз:
products.groupby("category").head(2).sort_values(["category", "revenue"], ascending=[True, False])| category | product | revenue | |
|---|---|---|---|
| 0 | Аксессуары | Кофемолка ручная | 102400 |
| 1 | Аксессуары | Термокружка | 48300 |
| 7 | Кофе | Эфиопия 250 г | 426600 |
| 3 | Кофе | Бразилия 1 кг | 375390 |
| 9 | Посуда | Турка медная | 139200 |
| 10 | Посуда | Френч-пресс | 111510 |
| 13 | Сладости | Миндаль в шоколаде | 82880 |
| 12 | Сладости | Зефир | 53760 |
| 18 | Чай | Улун 100 г | 194940 |
| 16 | Чай | Зелёный чай 100 г | 167700 |
Обратите внимание: head после groupby не сворачивает группы в одну строку, как sum, а возвращает
сами строки таблицы. Парный метод tail(n) берёт последние строки каждой группы.
Таблица products отсортирована по убыванию выручки. Сохраните в worst товар с наименьшей выручкой
в каждой категории: пять строк, отсортированных по категории, с индексом с нуля.
products.groupby("category").tail(1)..sort_values("category").reset_index(drop=True).Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
worst = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
worst = products.groupby("category").tail(1).sort_values("category").reset_index(drop=True)Сохраните в customers таблицу со столбцами city, customer_id, revenue — выручку по каждому покупателю
с его городом. В vip сохраните двух покупателей с наибольшей выручкой в каждом городе: десять строк,
отсортированных по городу, а внутри города — по убыванию выручки; индекс с нуля.
orders.groupby(["city", "customer_id"], as_index=False)["revenue"].sum()..groupby("city").head(2); в конце .sort_values(["city", "revenue"], ascending=[True, False]).reset_index(drop=True).Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
customers = ...
vip = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
customers = orders.groupby(["city", "customer_id"], as_index=False)["revenue"].sum()
vip = customers.sort_values("revenue", ascending=False).groupby("city").head(2)
vip = vip.sort_values(["city", "revenue"], ascending=[True, False]).reset_index(drop=True)Итоги
reset_index()после группировки илиas_index=Falseвgroupby— ключи становятся столбцами.- С двумя ключами
as_index=Falseдаёт плоскую таблицу: строка на каждую пару. - Лучшие в каждой группе: отсортировать всю таблицу, затем
groupby("ключ").head(n). headиtailпослеgroupbyвозвращают строки, а не итоги.