''' 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_.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()