groupby · agg · pivot_table · merge/concat · cut/qcut · EDA
2 ч теории · ~90 минут активной работы: разогрев, 5 блоков мини-лекции, live-coding, разбор ошибки many-to-many, обобщение.
df.dropna() и удивлён —
пропуски остались. Почему? Что он забыл? (Сегодня то же правило встретится в groupby.)df.groupby('col') и применять .mean()/.sum()/.size()/.count();agg({'col': 'fun'}) и по нескольким колонкам;pivot_table и crosstab;merge/join/concat и проверять .shape до/после;cut/qcut;groupby = SQL GROUP BY; pivot_table = формат отчётовgroupby — тот же SQL GROUP BY: встретится на курсе «Базы данных» и в Django ORM (модуль 4). Навык переносится один в один.pivot_table — стандартный формат отчёта в компании: «выручка по месяцам × категориям».df.groupby('category')['revenue'].sum() заменяет 10 строк цикла — вы это «почувствуете» через минуту.Работает, но: (1) ключи идут в порядке встречи в данных, а не по возрастанию; (2) на 50 классов цикл раздувается; (3) код описывает «как», а не «что».
Те же приёмы недель 4–5: загрузка, типы, арифметика колонок
(price*qty — векторизация, а не apply). Данные чистые — можно задавать вопросы.
Одна команда: сгруппировать по category, взять колонку
revenue, просуммировать. Ответ руководства: Электроника — 3 146 000.
| 1 · Split — разбить | 2 · Apply — применить | 3 · Combine — склеить |
|---|---|---|
| Таблица режется на группы по значениям колонки: Книги / Одежда / Электроника |
К каждой группе — функция:.sum(), .mean(), … |
Результаты склеиваются обратно в таблицу |
«df.groupby('Pclass')['Age'].mean() — одна команда, которая делает все три шага».
Цикл со слайда 4 делал split + apply вручную — Pandas делает это за вас.
groupby возвращает GroupBy-объект: «ленив», нужна агрегация«Ленив»: запоминает разбиение, но считает только при агрегации
(.sum(), .mean(), .size()). Без агрегации и без присваивания —
та же потеря результата, что в неделе 5.
len(sales.groupby('category'))?.size() vs .count(): число строк vs число непустых значенийРазница: .size() — число строк в группе;
.count() — число непустых значений по колонке. При пропусках они
расходятся: у 'a' строк две, а count — 1 (NaN не считается).
.size().)Показаны первые месяцы; остальные пропущены (…).
Составной ключ: сначала по месяцу, внутри — по категории; результат — иерархический индекс. Это ответ на вопрос «выручка по категориям и месяцам одним выражением».
agg с dict: каждой колонке — своя функция агрегацииКаждой колонке — свою функцию: средняя цена, суммарное количество, число
разных товаров (nunique = «число уникальных», отсюда «4 разных товара»). Словарь — это «заказ».
.mean()?Тот же agg, но колонкам результата сразу даются осмысленные
имена (avg_price, total_qty) — удобно для отчётов. Синтаксис — обзорно, детали на семинаре.
pivot_table: «месяц × категория → выручка» — двумерная сводкаПоказаны первые 4 из 12 месяцев.
Вторая ось: columns='category' раскладывает категории по столбцам
(у groupby ключ — только по строкам). NaN — «в этом месяце не было продаж категории», а не ошибка.
Внимание: aggfunc по умолчанию mean — для выручки нужен 'sum'.
pivot_table отличается от groupby(['month','category'])?margins=True: итоговые строка и столбец — формат отчётаПоказаны месяц 4 и итоговая строка «Всего».
Готовый формат отчёта: итоговая строка — сумма по категориям, итоговый
столбец — сумма по месяцам. Ячейка («Всего», «Всего») — вся выручка: 3 575 700.
Это ровно команда revenue_report(df) из семинара 6.5.
crosstab: частоты и доли по двум категориальным колонкамЧастоты — «группировка без колонки значений»: сколько раз встретилась пара.
normalize='index' — доли по строкам (каждая строка в сумме 1): доля «Книг» выше всего в СПб (0.58).
crosstab без normalize: абсолютные числа или доли?merge по ключу: обогащение продаж округами (SQL JOIN)Аналог SQL LEFT JOIN: how='left' сохраняет все 60 строк
(плюс колонка district) — .shape 60 → 60. Теперь можно группировать по округам.
.shape и isna().sum() по новой колонке. Зачем?how='inner/left/right/outer': какие строки остаютсяhow | Что остаётся |
|---|---|
inner | только строки с ключом в обеих таблицах |
left | все строки A + данные B, где есть ключ |
right | все строки B + данные A, где есть ключ |
outer | все строки обеих таблиц; недостающее — NaN |
«Чьи строки я не имею права потерять»: для обогащения продаж справочником —
left (все продажи). inner — только пересечение; outer — всё с NaN там, где нет пары.
how, если нужно оставить ВСЕ продажи, даже без региона в справочнике?a — 3 строки, в b — 2. Сколько строк вернёт merge?Размножение: ключ «Москва» есть в обеих таблицах по 2 раза → каждая пара склеивается: 2×2=4 строки вместо 3. Это самый коварный баг соединений — он «не падает», а просто искажает данные.
.shape до/после и дубликаты ключей ДО mergeregions['region'].duplicated().any() → False..shape с ожидаемым (60 → 60) и isna().sum() по новой колонке.Мантра: «не уверен, что в таблице — выведи .shape и .head()».
regions нет дубликатов, до merge?concat: склейка по осям; февраль + март = 9 строкСкладывает друг под другом (axis=0) или рядом (axis=1) — без сопоставления
ключей. ignore_index=True перенумеровывает индекс, иначе индексы частей «наслаиваются».
merge соединяет по ключу, concat — по позициям. Что вернёт concat таблиц с разными колонками?| Инструмент | Что делает | Ключ сопоставления | Когда |
|---|---|---|---|
merge | добавляет колонки из другой таблицы | по колонке/колонкам (on) | SQL JOIN, обогащение |
join | то же, но по индексу | index | merge по индексу |
concat | складывает таблицы друг под другом / рядом | без ключа (по осям) | склейка выгрузок |
Критерий выбора: есть общий ключ → merge/join;
нет ключа, просто две выгрузки → concat. Путаница merge vs concat — ошибка №4 недели 6.
cut: равные интервалы — частоты крайне неравны4 интервала РАВНОЙ ШИРИНЫ. Из-за скошенного распределения (много мелких
чеков, мало крупных) в первый интервал попало 51 покупка, в последний — 2. Группы «неуравновешены»,
но это честно отражает реальность. Интервалы (a, b]: левая граница открыта, правая включена.
qcut: равные частоты — в каждой группе ~одинаково наблюденийДелит на КВАнтили: в каждом сегменте примерно равное число наблюдений
(16/15/15/14). Границы — по данным, а не по ширине. Это «равные группы»; cut — «равные интервалы».
Небольшие отклонения — норма из-за совпадений значений.
revenue, те же 4 группы — почему частоты почти равные, в отличие от cut?cut vs qcut: границы заданы бизнесом vs равные группы| Вопрос | cut (равные интервалы) | qcut (равные частоты) |
|---|---|---|
| Границы | заданы заранее / равной ширины | по квантилям (по данным) |
| Частоты групп | могут сильно различаться (51 vs 2) | примерно равные (16/15/15/14) |
| Когда | «до 1000», «до 5000» — границы продиктованы бизнесом | «разбить на 4 равные группы» для сравнения |
Спросите себя: «что для меня важнее — ширина интервала или число наблюдений?» В проекте оба варианта допустимы, но выбор надо обосновать.
cut?value_counts(normalize=) — категориальные доли в EDAДоли категорий: Электроника — 38% всех покупок. Вместе с выручкой по категориям (слайд 6) — полный портрет: электроника дорогая, и её много.
nlargest«Векторное мышление»: то, что на 1–2 курсе делалось циклом по строкам, здесь —
одна цепочка методов. nlargest не сортирует всё, а берёт топ сразу. Это готовая ячейка проекта модуля.
nlargest(5) идиоматичнее, чем sort_values().head(5)?Показаны первые 4 из 12 месяцев.
Двухключевой groupby + .unstack() — внутренний ключ
превращается в колонки: та же сводка, что у pivot_table. Недели 4–6 сошлись в один инструмент.
groupby с двумя ключами, .unstack(), pivot_table.df.groupby(...) без агрегации/присваивания — группа не «применилась» (правило недели 5).df.groupby(...).agg(...) в цикле копит предупреждения — группировка пишется векторизованно..shape и дубликаты.agg с dict, когда колонки агрегируются по-разному.df.groupby('category').agg({'price':'mean'}) колонка qty пропала. Что объяснить?clean.csv), неделя 6 (groupby + pivot_table + выводы).groupby и pivot_table обязательны; каждый шаг — код + текстовый вывод; без iterrows, где есть векторизация.module2-eda (ноутбук eda_full.ipynb, clean.csv), code review по чек-листу.groupby + pivot_table, выводы подкреплены числами («средняя цена билета у выживших выше: 48 vs 22»).merge отличается от concat — и когда что использовать?agg({'price': 'mean', 'qty': 'sum'})?cut отличается от qcut?Ответьте письменно за 2 минуты — это мини-самооценка перед семинаром.
groupby/agg (6.1–6.2), pivot_table и crosstab (6.3), merge/concat с проверкой .shape (6.4), challenge — cut/qcut и revenue_report (6.5).groupby + pivot_table → 5–7 выводов с числами. Сдача — на этой неделе.groupby(...).mean()/.sum()/.size()/.count().agg({'col': 'fun'}) — разные функции разным колонкам.pivot_table (с margins) и crosstab (с normalize).merge/join/concat и защищаться от many-to-many проверкой .shape и дубликатов ключей.cut/qcut и объяснять разницу.One-minute paper: главное, что вы поняли, и один оставшийся вопрос.