Один показатель редко рассказывает всё. О городе хочется знать и выручку, и число заказов, и средний чек. Можно написать три группировки подряд — а можно одну.
Несколько итогов одного столбца
Метод agg (aggregate, «свести») после группировки принимает список названий функций — строками:
import pandas as pd
orders = pd.read_csv("data/shop_orders.csv")
orders["revenue"] = orders["price"] * orders["quantity"]
orders.groupby("city")["revenue"].agg(["sum", "mean", "count"])| sum | mean | count | |
|---|---|---|---|
| city | |||
| Екатеринбург | 423350 | 1356.891026 | 312 |
| Казань | 440150 | 1290.762463 | 341 |
| Москва | 1310110 | 1381.972574 | 948 |
| Новосибирск | 324090 | 1256.162791 | 258 |
| Санкт-Петербург | 803720 | 1364.550085 | 589 |
Получилась таблица: строки — группы, столбцы — показатели. С ней работают как с любой таблицей:
stats = orders.groupby("city")["revenue"].agg(["sum", "mean", "count"])
print(stats.loc["Казань", "sum"])
print(stats["mean"].round(1).tolist())440150 [1356.9, 1290.8, 1382.0, 1256.2, 1364.6]
Названия функций — те же, что у методов: "sum", "mean", "median", "min", "max", "count",
"nunique", "std", "size".
Сохраните в price_stats наименьшую, наибольшую и среднюю цену (price) по каждой категории — столбцы
min, max, mean. В widest сохраните название категории с наибольшей разностью между наибольшей
и наименьшей ценой.
orders.groupby("category")["price"].agg(["min", "max", "mean"]).price_stats["max"] - price_stats["min"]; метка наибольшего — idxmax().Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
price_stats = ...
widest = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
price_stats = orders.groupby("category")["price"].agg(["min", "max", "mean"])
widest = (price_stats["max"] - price_stats["min"]).idxmax()Разные столбцы — свои названия
Чаще показатели считаются по разным столбцам: сумма выручки, число разных заказов, средняя цена. Для этого
есть именованная агрегация: в agg перечисляют новые столбцы в виде
название=("исходный столбец", "функция").
orders.groupby("city").agg(
total=("revenue", "sum"),
orders=("order_id", "nunique"),
avg_price=("price", "mean"),
)| total | orders | avg_price | |
|---|---|---|---|
| city | |||
| Екатеринбург | 423350 | 203 | 691.153846 |
| Казань | 440150 | 214 | 719.090909 |
| Москва | 1310110 | 627 | 695.253165 |
| Новосибирск | 324090 | 163 | 689.534884 |
| Санкт-Петербург | 803720 | 369 | 658.981324 |
Столбец после groupby здесь не выбирают — он указан внутри каждой пары. Названия новых столбцов вы
придумываете сами; пишутся они без кавычек, как имена параметров.
Результат — обычная таблица, в которую можно добавить вычисляемый столбец:
cities = orders.groupby("city").agg(total=("revenue", "sum"), orders=("order_id", "nunique"))
cities["check"] = (cities["total"] / cities["orders"]).round(1)
cities.sort_values("check", ascending=False)| total | orders | check | |
|---|---|---|---|
| city | |||
| Санкт-Петербург | 803720 | 369 | 2178.1 |
| Москва | 1310110 | 627 | 2089.5 |
| Екатеринбург | 423350 | 203 | 2085.5 |
| Казань | 440150 | 214 | 2056.8 |
| Новосибирск | 324090 | 163 | 1988.3 |
Что выведет print(orders.groupby("channel")["revenue"].agg(["sum", "mean"]).shape)?
sum и mean. shape — сначала строки, потом столбцы: (3, 2).Сохраните в channels таблицу по каналам продаж с тремя столбцами: total — выручка, orders — число разных
заказов, buyers — число разных покупателей (customer_id). Добавьте столбец per_buyer — выручка на одного
покупателя, округлённая до рублей.
orders.groupby("channel").agg(total=("revenue", "sum"), orders=("order_id", "nunique"), buyers=(...)).(channels["total"] / channels["buyers"]).round().Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
channels = ...
# добавьте в channels столбец per_buyerУпражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
channels = orders.groupby("channel").agg(
total=("revenue", "sum"),
orders=("order_id", "nunique"),
buyers=("customer_id", "nunique"),
)
channels["per_buyer"] = (channels["total"] / channels["buyers"]).round()Сайт лидирует не только по выручке: каждый его покупатель приносит 7.9 тыс. ₽ против 5.7 тыс. ₽ в приложении и на маркетплейсе.
Группировка по двум столбцам
Если передать в groupby список столбцов, группой станет каждое сочетание их значений:
by_pair = orders.groupby(["city", "channel"])["revenue"].sum()
by_pair.head(7)city channel
Екатеринбург маркетплейс 83080
приложение 150240
сайт 190030
Казань маркетплейс 113670
приложение 106970
сайт 219510
Москва маркетплейс 343390
Name: revenue, dtype: int64Слева теперь два столбца подписей — у индекса два уровня: город и канал. Название города напечатано один
раз на всю свою группу строк. Такой индекс называют MultiIndex.
Значение достают по паре меток в круглых скобках. Если указать только первую метку, вернётся вся её группа:
print(by_pair.loc[("Казань", "сайт")])
print(by_pair.loc["Казань"])219510 channel маркетплейс 113670 приложение 106970 сайт 219510 Name: revenue, dtype: int64
Пять городов и три канала дают 15 групп, а сумма по ним — всё та же выручка года. Названия уровней
хранит атрибут index.names:
print(len(by_pair))
print(by_pair.sum())
print(list(by_pair.index.names))15 3301420 ['city', 'channel']
Порядок столбцов в списке задаёт порядок уровней: ["city", "channel"] — сначала города, внутри них каналы.
Сохраните в city_category выручку по каждой паре «город, категория» (сначала город). В moscow — выручку
по категориям в Москве, в moscow_tea — выручку от чая в Москве одним числом.
orders.groupby(["city", "category"])["revenue"].sum().city_category.loc["Москва"] — все категории города; city_category.loc[("Москва", "Чай")] — одна пара.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
city_category = ...
moscow = ...
moscow_tea = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
city_category = orders.groupby(["city", "category"])["revenue"].sum()
moscow = city_category.loc["Москва"]
moscow_tea = city_category.loc[("Москва", "Чай")]Именованная агрегация работает и с двумя ключами:
orders.groupby(["category", "channel"]).agg(total=("revenue", "sum"), lines=("revenue", "size")).head(6)| total | lines | ||
|---|---|---|---|
| category | channel | ||
| Аксессуары | маркетплейс | 46230 | 24 |
| приложение | 49520 | 32 | |
| сайт | 77750 | 52 | |
| Кофе | маркетплейс | 461000 | 234 |
| приложение | 540520 | 272 | |
| сайт | 912920 | 469 |
С индексом из двух уровней не всегда удобно работать дальше. Как превратить его в обычные столбцы, разберём в следующем уроке.
В прошлом модуле мы сравнивали два города и писали всё дважды. Теперь — все пять одной командой. Сохраните
в climate таблицу по городам: t_mean — средний temp_max, t_std — его стандартное отклонение,
rain — сумма precip_mm. Округлите всю таблицу до одного знака.
weather.groupby("city").agg(t_mean=("temp_max", "mean"), t_std=("temp_max", "std"), rain=("precip_mm", "sum")).round: climate.round(1).Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
weather = pd.read_csv("data/weather.csv")
climate = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
weather = pd.read_csv("data/weather.csv")
climate = weather.groupby("city").agg(
t_mean=("temp_max", "mean"),
t_std=("temp_max", "std"),
rain=("precip_mm", "sum"),
)
climate = climate.round(1)Итоги
.agg(["sum", "mean"])— несколько итогов одного столбца; результат — таблица..agg(название=("столбец", "функция"), ...)— показатели по разным столбцам со своими названиями.groupby(["a", "b"])— группы по сочетаниям; у результата индекс из двух уровней.- Значение по двум уровням —
результат.loc[("метка a", "метка b")].