# Летняя практика — Турнирные таблицы КХЛ Проект анализирует результаты матчей регулярного чемпионата КХЛ за сезоны **2018/19 – 2021/22** и строит по ним турнирные таблицы и график. Основные инструменты — библиотека **pandas** (работа с таблицами) и **matplotlib** (график). --- ## 1. Структура проекта | Файл / папка | Назначение | |---|---| | `main.py` | Основная программа: строит полную турнирную таблицу чемпионата и таблицы по конференциям (Запад / Восток). | | `divisions.py` | Отдельный модуль: добавляет деление на **дивизионы** и **правило посева** победителей дивизионов. Логику расчёта берёт из `main.py`, сам `main.py` не меняет. | | `3.py` | Задание №3: **график** разницы заброшенных и пропущенных шайб одной команды по ходу сезона. | | `khl_3/` | Исходные данные — CSV-файлы матчей (4 сезона, кодировка UTF-8). | ### Формат исходных данных (CSV) ``` ,Номер,Дата,Команда_1,Команда_2,Период_1,Период_2,Период_3,Овертайм,Буллиты 0,1,2018-09-01,Ак Барс,СКА,0:1,1:2,0:3,:,: ``` * `Команда_1` — хозяева, `Команда_2` — гости. * `Период_1..3`, `Овертайм`, `Буллиты` — счёт в формате `хозяева:гости`. * Пустой `:` означает, что период не игрался (нет овертайма/буллитов). * **Технические результаты** записаны знаками: `+ — -` (техпобеда хозяев) и `- — +` (техпобеда гостей). Встречаются в сезоне 2020/21. --- ## 2. Установка и запуск ```bash pip install pandas numpy matplotlib # Полная турнирная таблица + таблицы по конференциям python3 main.py khl_3/khl_2018_19.csv # Таблицы по конференциям с дивизионами и посевом победителей дивизионов python3 divisions.py khl_3/khl_2020_21.csv # График разницы шайб одной команды по датам (сохраняется в diff_<команда>.png) python3 3.py khl_3/khl_2018_19.csv "ЦСКА" ``` > Имя файла важно: программы достают код сезона (`2018_19`) из имени > `khl_2018_19.csv`, чтобы понять, какие команды в какой конференции (и дивизионе) > играли. --- ## 3. Как работает `main.py` (ядро проекта) Логика построена на **векторных операциях pandas**: вычисления делаются сразу над целым столбцом, без циклов по строкам. **Шаг 1. `parse_score(df, col)`** — разбирает строку счёта `"3:1"` на два числа. `str.split(':', expand=True)` раскладывает столбец в два, `to_numeric(..., errors='coerce')` превращает текст в числа, а пустой период (`:`) или знаки техрезультата через `fillna(0)` считаются как `0`. **Шаг 2. `build_standings(df, conf_map)`** — превращает список матчей в таблицу команд: 1. Считает голы в основное время (сумма трёх периодов). 2. Определяет **тип исхода** каждого матча сравнением столбцов: победа в основное время / в овертайме / по буллитам — и аналогично поражения. Победителю по буллитам добавляется ровно 1 решающая шайба. 3. Отдельно обрабатывает **технические результаты** (`+ — -` / `- — +`): засчитывает их как победу/поражение в основное время, но шайбы не начисляет (0:0). 4. **«Разворачивает» матч в две строки** — взгляд хозяев и взгляд гостей — и склеивает их (`pd.concat`). Теперь у каждой команды по строке на матч. 5. `groupby('Команда').sum()` суммирует все показатели по командам. 6. Считает производные показатели: * **И** — игры, **О** — очки (победа = 2, поражение в ОТ/буллитах = 1), * **Р** — разница шайб, **%О** — процент набранных очков. * `%О` считается **в целых числах** (`* 10000 // (2*И) / 100`) с отбрасыванием знаков, как на сайте КХЛ, — без округления и без ошибок float (57.00 не съезжает в 56.99). 7. Сортирует по регламенту КХЛ: `%О → В → ВО → ВБ → Р → ГЗ`. **Шаг 3. `show_table` и `main`** — печатают полную таблицу, затем отдельно конференции Запад и Восток (фильтр по столбцу `Конф`). ### Обозначения столбцов `И` — игры · `В` — победы в основное время · `ВО` — победы в овертайме · `ВБ` — победы по буллитам · `ПБ` — поражения по буллитам · `ПО` — поражения в овертайме · `П` — поражения в основное время · `ГЗ` — голы забитые · `ГП` — голы пропущенные · `Р` — разница · `О` — очки · `%О` — процент очков. --- ## 4. Дополнительные файлы ### `divisions.py` — дивизионы и посев В части сезонов КХЛ (2018/19–2020/21) действовало правило: **победители двух дивизионов получают места 1 и 2 в конференции**, даже если по очкам они не в топ-2. Например, в 2020/21 на Западе СКА (82 очка) стоит выше Динамо М (84) и Локомотива (83) — СКА выиграл дивизион Боброва. В сезоне 2021/22 это правило отменили (чистый порядок по %О). `divisions.py`: * переиспользует `build_standings` и `CONFERENCES` из `main.py` (не дублируя логику и **не изменяя** `main.py`); * словарь `DIVISIONS` задаёт состав четырёх дивизионов (Боброва, Тарасова — Запад; Харламова, Чернышёва — Восток) по каждому сезону; * `SEED_BY_DIVISION` включает посев только там, где он применялся; * `seed_conference` поднимает победителей дивизионов на места 1–2; * выводит таблицы конференций с дополнительным столбцом `Див`. ### `3.py` — график разницы шайб Задание №3: строит **график** разницы заброшенных и пропущенных шайб одной команды по датам матчей. Считает голы в каждом матче (`match_goals`, та же логика подсчёта, что в `main.py`, включая зануление технических результатов), разносит матч на взгляд хозяев и гостей, оставляет нужную команду, рисует линию «забито − пропущено» по дате и сохраняет картинку `diff_<команда>.png`. --- ## 5. Материал к защите **О чём проект.** По «сырым» результатам матчей КХЛ автоматически строится официальная турнирная таблица, таблицы с дивизионами и график. Цель — освоить обработку табличных данных в pandas и визуализацию в matplotlib. **Ключевые идеи, которые стоит назвать на защите:** 1. **Векторизация вместо циклов.** Все расчёты — операции над столбцами целиком (сложение, сравнение, маски `True/False`). Это и быстрее, и короче, чем `for` по строкам. 2. **Приём «одна строка матча → две строки команд».** Чтобы посчитать статистику команды, каждый матч дублируется: одна запись со стороны хозяев, другая — гостей. После этого `groupby` сводит всё в таблицу команд. 3. **Булевы маски.** Тип исхода (победа/поражение и как именно) — это столбцы `True/False`, которые переводятся в `1/0` через `.astype(int)` и суммируются. 4. **Точное соответствие регламенту КХЛ.** Система очков (2/1/0), добавление шайбы за буллиты, технические результаты, целочисленный (без округления) процент очков, порядок критериев сортировки, посев победителей дивизионов. 5. **Переиспользование кода.** `divisions.py` и `3.py` опираются на логику из `main.py`, чтобы не дублировать разбор счёта и расчёт таблицы. **Возможные вопросы и ответы:** * *Почему пустой период = 0:0?* — Овертайм/буллиты есть не во всех матчах; `fillna(0)` делает расчёт единообразным. * *Почему %О считается в целых числах, а не через `round`?* — Сайт КХЛ отбрасывает знаки после второго (а не округляет), и целочисленная арифметика не даёт ошибок float (57.00 вместо 56.99...). * *Как обрабатываются технические результаты?* — `+ — -` / `- — +` засчитываются как победа/поражение в основное время, но без шайб (0:0), как в КХЛ. * *Почему дивизионы в отдельном файле?* — чтобы не усложнять `main.py`; `divisions.py` переиспользует его функции, ничего в нём не меняя. * *Как добавить новый сезон?* — добавить CSV в `khl_3/`, описать конференции в `CONFERENCES` (в `main.py`) и при необходимости дивизионы в `DIVISIONS` (в `divisions.py`).