summer_practice/README.md
2026-06-29 11:51:37 +03:00

134 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Летняя практика — Турнирные таблицы КХЛ
Проект анализирует результаты матчей регулярного чемпионата КХЛ за сезоны
**2018/19 2021/22** и строит по ним турнирные таблицы и графики.
Основной инструмент — библиотека **pandas** (работа с таблицами) и
**matplotlib** (графики).
---
## 1. Структура проекта
| Файл / папка | Назначение |
|---|---|
| `main.py` | Основная программа: строит полную турнирную таблицу чемпионата и таблицы по конференциям (Запад / Восток). |
| `individual_1.py` | Индивидуальное задание: гистограмма набранных очков по командам. |
| `individual_4.py` | Индивидуальное задание: гистограмма всего заброшенных шайб за сезон. |
| `individual_5.py` | Индивидуальное задание: гистограмма разницы шайб конкретной команды в заданном периоде по датам. |
| `khl_3/` | Исходные данные — CSV-файлы матчей (кодировка UTF-8). |
| `khl_3_win/` | Те же данные в кодировке UTF-8 **с BOM** — открываются в Excel без «кракозябр». |
### Формат исходных данных (CSV)
```
,Номер,Дата,Команда_1,Команда_2,Период_1,Период_2,Период_3,Овертайм,Буллиты
0,1,2018-09-01,Ак Барс,СКА,0:1,1:2,0:3,:,:
```
* `Команда_1` — хозяева, `Команда_2` — гости.
* ериод_1..3`, `Овертайм`, `Буллиты` — счёт в формате `хозяева:гости`.
* Пустой `:` означает, что период не игрался (нет овертайма/буллитов).
---
## 2. Установка и запуск
```bash
pip install pandas numpy matplotlib
# Полная турнирная таблица + таблицы по конференциям
python3 main.py khl_3/khl_2018_19.csv
# Гистограмма очков (по желанию — сохранить в файл)
python3 individual_1.py khl_3/khl_2018_19.csv [output.png]
# Гистограмма заброшенных шайб
python3 individual_4.py khl_3/khl_2018_19.csv
# Разница шайб команды в N-м периоде по датам
python3 individual_5.py khl_3/khl_2018_19.csv 2 "Ак Барс" [output.png]
```
> Имя файла важно: `main.py` достаёт код сезона (`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:0`.
**Шаг 2. `build_standings(df, conf_map)`** — превращает список матчей в таблицу команд:
1. Считает голы в основное время (сумма трёх периодов).
2. Определяет **тип исхода** каждого матча сравнением столбцов:
победа в основное время / в овертайме / по буллитам — и аналогично поражения.
Победителю по буллитам добавляется ровно 1 решающая шайба.
3. **«Разворачивает» матч в две строки** — взгляд хозяев и взгляд гостей —
и склеивает их (`pd.concat`). Теперь у каждой команды по строке на матч.
4. `groupby('Команда').sum()` суммирует все показатели по командам.
5. Считает производные показатели:
* **И** — игры, **О** — очки (победа = 2, поражение в ОТ/буллитах = 1),
* **Р** — разница шайб, **%О** — процент набранных очков.
* `%О` считается с **отбрасыванием** знаков (`np.floor`), как на сайте КХЛ, а не округлением.
6. Сортирует по регламенту КХЛ: `%ОВВО → ВБ → Р → ГЗ`.
**Шаг 3. `show_table` и `main`** — печатают полную таблицу, затем отдельно
конференции Запад и Восток (фильтр по столбцу `Конф`).
### Обозначения столбцов
`И` — игры · `В` — победы в основное время · `ВО` — победы в овертайме ·
`ВБ` — победы по буллитам · `ПБ` — поражения по буллитам · `ПО` — поражения в овертайме ·
`П` — поражения в основное время · `ГЗ` — голы забитые · `ГП` — голы пропущенные ·
`Р` — разница · `О` — очки · `%О` — процент очков.
---
## 4. Индивидуальные задания
* **`individual_1.py`** — переиспользует `parse_score` из `main.py`, считает очки
каждой команды и строит **столбчатую диаграмму очков**. Поддерживает сохранение в PNG.
* **`individual_4.py`** — суммирует все заброшенные шайбы каждой команды за сезон
и строит гистограмму с подписями значений над столбцами.
* **`individual_5.py`** — фильтрует матчи одной команды, считает **разницу шайб
в выбранном периоде** по датам. Столбцы раскрашены: зелёный (плюс), красный (минус),
серый (ноль); добавлена линия нуля и сетка.
---
## 5. Материал к защите
**О чём проект.** По «сырым» результатам матчей КХЛ автоматически строится
официальная турнирная таблица и набор графиков. Цель — освоить обработку
табличных данных в pandas и визуализацию в matplotlib.
**Ключевые идеи, которые стоит назвать на защите:**
1. **Векторизация вместо циклов.** Все расчёты — операции над столбцами целиком
(сложение, сравнение, маски `True/False`). Это и быстрее, и короче, чем `for` по строкам.
2. **Приём «одна строка матча → две строки команд».** Чтобы посчитать статистику
команды, каждый матч дублируется: одна запись со стороны хозяев, другая — гостей.
После этого `groupby` сводит всё в таблицу команд.
3. **Булевы маски.** Тип исхода (победа/поражение и как именно) — это столбцы `True/False`,
которые переводятся в `1/0` через `.astype(int)` и суммируются.
4. **Точное соответствие регламенту КХЛ.** Система очков (2/1/0), добавление шайбы
за буллиты, отбрасывание (а не округление) процента очков, порядок критериев сортировки.
5. **Переиспользование кода.** `individual_1.py` импортирует функции из `main.py`,
чтобы не дублировать логику разбора счёта.
**Возможные вопросы и ответы:**
* *Почему пустой период = 0:0?* — Овертайм/буллиты есть не во всех матчах; `fillna(0)`
делает расчёт единообразным.
* *Почему `np.floor`, а не `round`?* — Сайт КХЛ отбрасывает знаки после второго
(72.448 → 72.44), а не округляет.
* *Зачем папка `khl_3_win`?* — то же содержимое, но с BOM, чтобы Excel правильно
показывал кириллицу.
* *Как добавить новый сезон?* — добавить CSV в `khl_3/` и описать конференции
в словаре `CONFERENCES` внутри `main.py`.