Семестр 3 · Модуль 2 · Неделя 6
Группировка и агрегация
groupby · agg · pivot_table · crosstab · merge/join/concat · cut/qcut
2 ч теории. Презентация — 30 мин мини-лекция, далее live-coding
и задачи семинара.
Прежде чем начать: вы умеете чистить данные. Сегодня научитесь
задавать им вопросы: «какая категория приносит больше всего», «как выручка меняется
по месяцам» — и собирать ответ в таблицы-отчёты.
Зачем вам группировка на 2–3 курсе
groupby — это тот же SQL GROUP BY, который будет на
курсе «Базы данных» и в Django ORM (модуль 4).
pivot_table — стандартный формат любого отчёта в компании:
«выручка по месяцам × категориям».
- Одна строка
df.groupby('category')['revenue'].sum() заменяет
10 строк цикла — вы это уже почувствовали на неделе 4.
- К концу недели вы соберёте из этого законченный EDA-ноутбук — проект модуля.
Какой вопрос о ваших данных вы зададите первым делом, если у вас
есть таблица продаж за год?
groupby: паттерн split-apply-combine
Любая группировка — это три шага (одна команда делает все три):
| Шаг | Что происходит |
| Split | таблица разрезается на группы по значениям колонки |
| Apply | к каждой группе применяется функция (mean/sum/…) |
| Combine | результаты склеиваются обратно в таблицу |
sales.groupby('category')['revenue'].sum()
category
Книги 90200
Одежда 339500
Электроника 3146000
Name: revenue, dtype: int64
Что вернёт `df.groupby('col')` сам по себе — таблицу или специальный объект?
groupby возвращает GroupBy-объект
Забывают присвоить результат — главная ошибка недели:
sales.groupby("category") # GroupBy-объект, НЕ таблица
sales.groupby("category").mean() # <- вот теперь результат
sales.groupby('category')['revenue'].mean()
category
Книги 5638.0
Одежда 16167.0
Электроника 136783.0
Name: revenue, dtype: float64
`.size()` — число строк в группе, `.count()` — непустых значений
по колонке; они различаются при пропусках.
Почему `sales.groupby('category').mean()` печатает таблицу, хотя сам по себе `groupby` — объект?
agg: разные колонки — разные функции
sales.groupby('category').agg({
'price': 'mean',
'qty': 'sum',
'product': 'nunique',
})
price qty product
category
Книги 1781.25 51 4
Одежда 5642.86 62 4
Электроника 45217.39 68 4
Когда нужны разные метрики по разным колонкам — agg({'col': 'fun'}).
Что делает `agg({'price': 'mean', 'qty': 'sum'})`? Зачем словарь, а не просто `.mean()`?
pivot_table — двумерная сводка
«Месяц × категория → выручка»: у `groupby` один ключ на ось, у `pivot_table`
— строки (index) И столбцы (columns):
pd.pivot_table(sales, index='month',
columns='category',
values='revenue', aggfunc='sum')
category Книги Одежда Электроника
month
4 5400.0 42000.0 1050000.0
7 12900.0 115500.0 116000.0
NaN — в месяце не было продаж этой категории. `margins=True` добавит итоги.
Чем `pivot_table` отличается от `groupby`? Когда нужна именно сводная таблица?
crosstab — таблицы частот
Частоты/доли по двум категориальным колонкам (группировка без колонки
значений):
pd.crosstab(sales['region'], sales['category'])
pd.crosstab(sales['region'], sales['category'],
normalize='index')
category Книги Одежда Электроника
region
Москва 0.19 0.33 0.48
normalize='index' — доли по строкам (в пределах региона).
Что покажет `crosstab` с `normalize='index'`: абсолютные числа или доли по регионам?
Соединения: merge, join, concat
# merge — по ключу (как SQL JOIN)
pd.merge(sales, regions, on='region', how='left')
# concat — склейка по осям
pd.concat([feb, mar], ignore_index=True)
merge — соединяет по общему ключу (left/right/inner/outer);
concat — просто склеивает по строкам/колонкам, без ключа.
join — merge по индексу.
Что сделает `merge` с дубликатами ключей? А `concat` двух таблиц с разными колонками?
Ошибка недели: merge размножает строки
Если ключ встречается в обеих таблицах несколько раз — many-to-many merge
перемножает строки:
pd.merge(a, b, on="region") # ключ с дубликатами -> строк стало больше!
print('до:', len(sales)) # 60
merged = pd.merge(sales, regions, on='region')
print('после:', len(merged)) # 60, если regions без дубликатов
Правило: проверяй `.shape` до и после merge и уникальность
ключей в обеих таблицах.
Почему число строк после merge может вырасти? Как это проверить?
Бинирование: cut и qcut
Превратить непрерывный признак в категории (сегменты чеков, возрастные группы):
# cut: равные интервалы
pd.cut(sales['revenue'], 4)
# qcut: равные частоты
pd.qcut(sales['revenue'], 4)
cut — диапазоны равной ширины; qcut —
в каждой группе примерно равное число наблюдений (квантили).
Для чека от 100 до 1000 руб.: в каком случае в сегменте «дорогие» может оказаться 1 покупка — cut или qcut?
Собираем EDA: одна строка заменяет цикл
sales['revenue'] = sales['price'] * sales['qty']
sales.groupby('product')['revenue'].sum().nlargest(5)
product
Ноутбук 1980000
Смартфон 750000
Монитор 280000
Куртка 153000
Наушники 136000
Name: revenue, dtype: int64
Это и есть «векторное мышление»: то, что на 1–2 курсе делалось
циклом по строкам, здесь — одна команда.
Как посчитать выручку по категориям и месяцам одним выражением (подсказка: `groupby` с двумя ключами)?
Типичные ошибки недели 6
- Забывают присвоить:
df.groupby(...) без
присваивания — группа не «применилась».
- Мутация в цикле:
df.groupby(...).agg(...)
в цикле копит предупреждения.
- Merge с дубликатами ключей — внезапно разросшийся DataFrame.
- Путаница merge vs concat (по ключу vs по позициям).
- Непонимание, зачем `agg` с dict, когда все колонки агрегируются по-разному.
Студент сделал `df.groupby('category').agg({'price': 'mean'})` и не понимает, почему колонка `qty` пропала. Что объяснить?
Вопросы для проверки понимания
- Чем `merge` отличается от `concat` и когда что использовать?
- Как посчитать выручку по категориям и месяцам одним выражением?
- Что делает `agg({'price': 'mean', 'qty': 'sum'})`?
- Чем `cut` отличается от `qcut`?
Ответьте себе письменно за 2 минуты — это и есть мини-самооценка перед семинаром.
Переход: на семинаре вы проанализируете продажи и соберёте
отчёт «месяц × категория» — это финальный навык модуля.
Что дальше
- Семинар недели 6: продажи, groupby/agg, pivot_table,
cut/qcut, merge — три уровня задач.
- Проект модуля (ДЗ недели 6): полный EDA реального датасета —
загрузка → очистка → группировка → выводы в Jupyter Notebook.
- Неделя 7: визуализация (Matplotlib/Seaborn) — ваши выводы
станут графиками, а дашборд — итогом кросс-проекта.
Готовы? Откройте семинар-6.md и sales.csv.