Группировка по двум столбцам даёт длинный список: «Екатеринбург, маркетплейс — столько-то; Екатеринбург, приложение — столько-то…»
import pandas as pd
orders = pd.read_csv("data/shop_orders.csv")
orders["revenue"] = orders["price"] * orders["quantity"]
orders.groupby(["city", "channel"])["revenue"].sum().head(6)city channel
Екатеринбург маркетплейс 83080
приложение 150240
сайт 190030
Казань маркетплейс 113670
приложение 106970
сайт 219510
Name: revenue, dtype: int64Сравнивать города между собой по такому списку неудобно. Человеку привычнее таблица, где города — в строках, каналы — в столбцах, а на пересечении — число. Это сводная таблица; если вы работали в Excel, вы её знаете.
pivot_table
table = orders.pivot_table(values="revenue", index="city", columns="channel", aggfunc="sum")
table| channel | маркетплейс | приложение | сайт |
|---|---|---|---|
| city | |||
| Екатеринбург | 83080 | 150240 | 190030 |
| Казань | 113670 | 106970 | 219510 |
| Москва | 343390 | 334240 | 632480 |
| Новосибирск | 85650 | 97200 | 141240 |
| Санкт-Петербург | 250280 | 213780 | 339660 |
Четыре параметра отвечают на четыре вопроса:
values— что считаем: столбец с числами;index— что будет в строках;columns— что будет в столбцах;aggfunc— как сворачиваем:"sum","mean","count","nunique","max"…
По сути это та же группировка по двум ключам — только результат разложен по двум осям. Получилась обычная таблица: города — в индексе, каналы — названия столбцов.
print(table.loc["Казань", "сайт"])
print(table["сайт"].idxmax())
print(table.shape)219510 Москва (5, 3)
Сохраните в by_city сводную таблицу выручки: в строках — категории, в столбцах — города. В spb_tea
сохраните выручку от чая в Санкт-Петербурге.
orders.pivot_table(values="revenue", index="category", columns="city", aggfunc="sum").by_city.loc["Чай", "Санкт-Петербург"]: сначала строка, потом столбец.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
by_city = ...
spb_tea = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
by_city = orders.pivot_table(values="revenue", index="category", columns="city", aggfunc="sum")
spb_tea = by_city.loc["Чай", "Санкт-Петербург"]По умолчанию — среднее
Если aggfunc не указать, pivot_table посчитает среднее:
orders.pivot_table(values="revenue", index="city", columns="channel").round(1)| channel | маркетплейс | приложение | сайт |
|---|---|---|---|
| city | |||
| Екатеринбург | 1298.1 | 1517.6 | 1275.4 |
| Казань | 1306.6 | 1258.5 | 1298.9 |
| Москва | 1436.8 | 1285.5 | 1408.6 |
| Новосибирск | 1259.6 | 1185.4 | 1307.8 |
| Санкт-Петербург | 1480.9 | 1353.0 | 1296.4 |
Числа правдоподобные, ошибки нет — а это не выручка, а средняя выручка строки.
small = pd.DataFrame({"city": ["Омск", "Омск", "Тула"], "cups": [10, 30, 5]})
print(small.pivot_table(values="cups", index="city").loc["Омск", "cups"])Что выведет этот код?
aggfunc не указан — считается среднее: (10 + 30) / 2 = 20.0. Сумму 40 дал бы aggfunc="sum", число строк 2 — aggfunc="count".Итоги по строкам и столбцам
Параметр margins=True добавляет итоговую строку и итоговый столбец; margins_name задаёт их название
(по умолчанию — All):
orders.pivot_table(values="revenue", index="city", columns="channel", aggfunc="sum", margins=True, margins_name="Всего")| channel | маркетплейс | приложение | сайт | Всего |
|---|---|---|---|---|
| city | ||||
| Екатеринбург | 83080 | 150240 | 190030 | 423350 |
| Казань | 113670 | 106970 | 219510 | 440150 |
| Москва | 343390 | 334240 | 632480 | 1310110 |
| Новосибирск | 85650 | 97200 | 141240 | 324090 |
| Санкт-Петербург | 250280 | 213780 | 339660 | 803720 |
| Всего | 876070 | 902430 | 1522920 | 3301420 |
В правом нижнем углу — общий итог: знакомые 3 301 420 ₽.
Сохраните в items сводную таблицу числа проданных штук (quantity): категории в строках, каналы
в столбцах, с итогами под названием «Всего». В site_share сохраните долю сайта в общем числе проданных
штук — посчитайте её по ячейкам items.
margins=True, margins_name="Всего".items.loc["Всего", "сайт"] / items.loc["Всего", "Всего"].Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
items = ...
site_share = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
items = orders.pivot_table(values="quantity", index="category", columns="channel", aggfunc="sum", margins=True, margins_name="Всего")
site_share = items.loc["Всего", "сайт"] / items.loc["Всего", "Всего"]Пустые ячейки
Если какого-то сочетания в данных нет, в ячейке будет пропуск. Посмотрим на аксессуары по месяцам и городам:
orders["month"] = pd.to_datetime(orders["date"]).dt.month
gadgets = orders[orders["category"] == "Аксессуары"]
gadgets.pivot_table(values="revenue", index="month", columns="city", aggfunc="sum").head(4)| city | Екатеринбург | Казань | Москва | Новосибирск | Санкт-Петербург |
|---|---|---|---|---|---|
| month | |||||
| 1 | NaN | 8700.0 | 7360.0 | 2300.0 | 4830.0 |
| 2 | 480.0 | NaN | 9900.0 | NaN | NaN |
| 3 | 1630.0 | 4600.0 | 960.0 | 480.0 | 720.0 |
| 4 | 9420.0 | 9600.0 | 3980.0 | NaN | 6460.0 |
В январе в Екатеринбурге аксессуаров не покупали — и на этом месте NaN. Из-за пропусков вся таблица стала
дробной. Параметр fill_value подставляет значение в пустые ячейки:
gadgets.pivot_table(values="revenue", index="month", columns="city", aggfunc="sum", fill_value=0).head(4)| city | Екатеринбург | Казань | Москва | Новосибирск | Санкт-Петербург |
|---|---|---|---|---|---|
| month | |||||
| 1 | 0 | 8700 | 7360 | 2300 | 4830 |
| 2 | 480 | 0 | 9900 | 0 | 0 |
| 3 | 1630 | 4600 | 960 | 480 | 720 |
| 4 | 9420 | 9600 | 3980 | 0 | 6460 |
Здесь ноль оправдан: «продаж не было» и значит «выручка ноль». Со средними так делать нельзя: если в группе нет ни одной оценки, её средний балл не ноль, а неизвестен.
В таблице orders уже есть столбец month. Сохраните в monthly сводную таблицу выручки: месяцы в строках,
категории в столбцах. В tea_peak сохраните номер месяца с наибольшей выручкой чая,
в december_top — название категории с наибольшей выручкой в декабре.
monthly["Чай"].idxmax().monthly.loc[12].idxmax().Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
monthly = ...
tea_peak = ...
december_top = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
monthly = orders.pivot_table(values="revenue", index="month", columns="category", aggfunc="sum", fill_value=0)
tea_peak = monthly["Чай"].idxmax()
december_top = monthly.loc[12].idxmax()Строка и столбец сводной таблицы — обычные Series, и idxmax работает с обоими: для столбца возвращает
метку строки, для строки — название столбца.
Средние баллы
Сводные таблицы удобны не только для сумм. В файле data/grades.csv — оценки 30 студентов трёх групп
по пяти предметам за два семестра, по строке на каждую оценку:
grades = pd.read_csv("data/grades.csv")
grades.head(4)| student | group | subject | term | score | |
|---|---|---|---|---|---|
| 0 | Анна А. | ИТ-21 | Математика | 1 | 68 |
| 1 | Анна А. | ИТ-21 | Математика | 2 | 71 |
| 2 | Анна А. | ИТ-21 | Физика | 1 | 49 |
| 3 | Анна А. | ИТ-21 | Физика | 2 | 60 |
Сохраните в group_subject сводную таблицу средних баллов (score): группы в строках, предметы в столбцах,
округление до одного знака. В best_subject сохраните предмет, по которому у группы ИТ-22 самый высокий
средний балл.
grades.pivot_table(values="score", index="group", columns="subject", aggfunc="mean").round(1).group_subject.loc["ИТ-22"].idxmax().Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
group_subject = ...
best_subject = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
group_subject = grades.pivot_table(values="score", index="group", columns="subject", aggfunc="mean").round(1)
best_subject = group_subject.loc["ИТ-22"].idxmax()Итоги
таблица.pivot_table(values=..., index=..., columns=..., aggfunc=...)— что считаем, что в строках, что в столбцах и как сворачиваем.- Без
aggfuncсчитается среднее — для сумм пишитеaggfunc="sum". margins=Trueдобавляет итоги,fill_valueзаполняет пустые ячейки.- Результат — обычная таблица:
loc,idxmax, сортировка работают как всегда.