summer_practice/3.py
2026-06-30 09:23:39 +03:00

152 lines
8.7 KiB
Python
Raw Permalink 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.

'''
3.py - задание №3: график разницы заброшенных и пропущенных шайб одной команды
по ходу сезона (по датам матчей).
ОБЩАЯ ЛОГИКА:
1. Запуск: python3 3.py khl_3/khl_2018_19.csv "ЦСКА"
2. Читаем CSV с матчами.
3. match_goals(df) считает итоговые голы хозяев и гостей в каждом матче.
4. plot_team_diff(team, file) оставляет только матчи нужной команды, считает в
каждом разницу (забито - пропущено) и рисует её по оси дат; картинка
сохраняется в файл diff_<команда>.png.
Это отдельный сценарий от main.py: тут не строится таблица, а рисуется график
для одной команды. Часть кода (parse_score, подсчёт голов) намеренно похожа на
main.py, потому что правило подсчёта шайб одно и то же.
Зависимости: pandas (таблицы), matplotlib (график). numpy не используется напрямую.
'''
import sys # для аргументов командной строки
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt # для построения графика
def parse_score(df, col):
'''
Шаг 1. Разобрать столбец со счётом вида "3:1" на два числовых столбца.
Принимает: df - таблица матчей; col - имя столбца со счётом.
Возвращает: (home, away) - голы хозяев и голы гостей (числовые Series).
split(':') разбивает "3:1" на "3" и "1"; to_numeric(errors='coerce') делает
из текста числа, а нечисловое (пустое ":" или знаки) превращает в NaN;
fillna(0) ставит 0. Итог: не сыгранный период читается как "0:0".
'''
parts = df[col].str.split(':', expand=True)
home = pd.to_numeric(parts[0], errors='coerce').fillna(0)
away = pd.to_numeric(parts[1], errors='coerce').fillna(0)
return home, away
def match_goals(df):
'''
Шаг 2. Посчитать итоговые голы хозяев и гостей в КАЖДОМ матче.
Принимает: df - таблица матчей.
Возвращает: (goals_home, goals_away) - два столбца с числом шайб в каждом матче.
Правило подсчёта то же, что в основной таблице: голы трёх периодов + голы
овертайма + 1 решающая шайба победителю серии буллитов. Отдельно зануляются
технические результаты (см. блок ниже).
'''
h1, a1 = parse_score(df, 'Период_1')
h2, a2 = parse_score(df, 'Период_2')
h3, a3 = parse_score(df, 'Период_3')
oth, ota = parse_score(df, 'Овертайм') # овертайм (0/0, если его не было)
soh, soa = parse_score(df, 'Буллиты') # буллиты (0/0, если их не было)
reg_h = h1 + h2 + h3 # голы хозяев в основное время
reg_a = a1 + a2 + a3 # голы гостей в основное время
tie = (reg_h == reg_a) # ничья после трёх периодов
# Победа по буллитам = была ничья в основное И в овертайме, и кто-то выиграл буллиты.
win_home_so = tie & (oth == ota) & (soh > soa)
win_away_so = tie & (oth == ota) & (soa > soh)
# astype(int) переводит True/False в 1/0 - так добавляем ровно 1 решающую шайбу.
goals_home = reg_h + oth + win_home_so.astype(int)
goals_away = reg_a + ota + win_away_so.astype(int)
'''
Технические результаты (неявка/отмена): в файле счёт записан знаками
"+ — -" (техпобеда хозяев) или "- — +" (техпобеда гостей), а не цифрами.
КХЛ шайбы за такие матчи НЕ начисляет, поэтому оба значения принудительно 0:0
(так разница в этом матче будет 0, как на сайте КХЛ).
'''
p1 = df['Период_1'].astype(str).str.strip()
tech = p1.str.startswith('+') | p1.str.endswith('+')
goals_home = goals_home.where(~tech, 0)
goals_away = goals_away.where(~tech, 0)
return goals_home, goals_away
def plot_team_diff(team, filename):
'''
Шаг 3. Построить и сохранить график разницы шайб одной команды по датам.
Принимает: team - название команды (как в CSV); filename - путь к CSV.
Ничего не возвращает - сохраняет картинку diff_<team>.png и показывает окно.
Как работает:
1) считаем голы в каждом матче через match_goals;
2) разносим каждый матч на две строки (взгляд хозяев и взгляд гостей), чтобы
"забито/пропущено" были с точки зрения конкретной команды;
3) оставляем только строки нужной команды, сортируем по дате;
4) для каждого матча считаем "Разница = ГЗ - ГП" и рисуем линию по датам.
'''
df = pd.read_csv(filename) # читаем файл с матчами
goals_home, goals_away = match_goals(df) # голы хозяев/гостей в каждом матче
# Делаем "по две строки на матч": взгляд хозяев и взгляд гостей.
# ГЗ - забито нашей командой, ГП - пропущено (= забито соперником).
home = pd.DataFrame({
'Дата': df['Дата'],
'Команда': df['Команда_1'],
'ГЗ': goals_home,
'ГП': goals_away,
})
away = pd.DataFrame({
'Дата': df['Дата'],
'Команда': df['Команда_2'],
'ГЗ': goals_away,
'ГП': goals_home,
})
both = pd.concat([home, away], ignore_index=True)
both[['ГЗ', 'ГП']] = both[['ГЗ', 'ГП']].astype(int) # из дробных делаем целые
# Оставляем только матчи нужной команды (булева маска == team).
rows = both[both['Команда'] == team].copy()
# Дату-текст превращаем в настоящую дату, чтобы ось X шла по времени.
rows['Дата'] = pd.to_datetime(rows['Дата'])
rows = rows.sort_values('Дата') # по возрастанию даты
rows['Разница'] = rows['ГЗ'] - rows['ГП'] # забито минус пропущено в матче
# Рисуем график.
fig, ax = plt.subplots(figsize=(12, 5))
ax.plot(rows['Дата'], rows['Разница'], marker='o') # линия с точками-матчами
ax.axhline(0, color='gray', linewidth=1) # горизонтальная линия "ноль" для наглядности
ax.set_title('Разница забитых и пропущенных шайб: ' + team)
ax.set_xlabel('Дата матча')
ax.set_ylabel('Забито - пропущено')
ax.grid(True, alpha=0.3) # лёгкая сетка
fig.autofmt_xdate() # автоматически поворачивает подписи дат, чтобы не слипались
fig.savefig('diff_' + team + '.png', dpi=150, bbox_inches='tight') # сохраняем картинку
print('График сохранён: diff_' + team + '.png')
plt.show() # показываем окно с графиком
def main():
'''
Точка входа. Берёт из командной строки два аргумента - путь к CSV и название
команды - и вызывает построение графика.
Пример: python3 3.py khl_3/khl_2018_19.csv "ЦСКА"
'''
filename = sys.argv[1] # первый аргумент - имя файла
team = sys.argv[2] # второй аргумент - имя команды
plot_team_diff(team, filename)
main()