152 lines
8.7 KiB
Python
152 lines
8.7 KiB
Python
'''
|
||
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()
|