forked from zovos/summer_practice
157 lines
12 KiB
Markdown
157 lines
12 KiB
Markdown
# Летняя практика — Турнирные таблицы КХЛ
|
||
|
||
Проект анализирует результаты матчей регулярного чемпионата КХЛ за сезоны
|
||
**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`).
|