fix main.py and add divisions.py

This commit is contained in:
q 2026-06-30 09:23:39 +03:00
parent d2cf5cd57c
commit bab805c8d6
3 changed files with 434 additions and 21 deletions

152
3.py Normal file
View file

@ -0,0 +1,152 @@
'''
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()

180
divisions.py Normal file
View file

@ -0,0 +1,180 @@
import sys # аргументы командной строки
import os # путь к main.py и имя csv-файла
import pandas as pd
import numpy as np
'''
Отдельный модуль: ДИВИЗИОНЫ и ПРАВИЛО ПОСЕВА победителей дивизионов.
Зачем он нужен.
Базовый main.py сортирует команды в конференции строго по проценту очков (%О)
и тай-брейкам. Но в части сезонов КХЛ в таблице конференции действовало правило:
ПОБЕДИТЕЛИ ДВУХ ДИВИЗИОНОВ получают места 1 и 2 в конференции, даже если по очкам
они не входят в топ-2. Из-за этого, например, в сезоне 2020/21 в Западной
конференции СКА (82 очка) стоит на сайте выше Динамо М (84) и Локомотива (83):
СКА выиграл дивизион Боброва, а те двое - из дивизиона Тарасова, который выиграл ЦСКА.
main.py этот посев не делает (в нём вообще нет деления на дивизионы, только
Запад/Восток), поэтому правило вынесено сюда, в отдельный файл, и main.py не трогается.
'''
'''
Расчёт таблицы берём из main.py, чтобы не дублировать логику (единственный источник
правды - там). main.py в конце сам вызывает main(), поэтому импортировать его напрямую
нельзя - он бы тут же попытался прочитать аргументы и упал. Поэтому читаем его исходник
и выполняем без последней строки `main()`: так получаем готовые функции build_standings
и словарь CONFERENCES, ничего при этом не запуская и не печатая.
'''
_main_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'main.py')
_src = open(_main_path, encoding='utf-8').read()
_src = _src.replace('\nmain()\n', '\n') # убираем автозапуск
_ns = {}
exec(compile(_src, 'main.py', 'exec'), _ns)
build_standings = _ns['build_standings']
CONFERENCES = _ns['CONFERENCES']
def make_div(bobrov, tarasov, kharlamov, chernyshev):
'''
Собрать словарь {команда -> дивизион} из четырёх списков команд.
Принимает: четыре списка - составы дивизионов Боброва и Тарасова (Запад),
Харламова и Чернышёва (Восток).
Возвращает: dict, где каждой команде сопоставлено название её дивизиона.
Аналог make_conf из main.py, только делений не два (Запад/Восток), а четыре.
'''
d = {}
d.update({t: 'Боброва' for t in bobrov}) # Запад
d.update({t: 'Тарасова' for t in tarasov}) # Запад
d.update({t: 'Харламова' for t in kharlamov}) # Восток
d.update({t: 'Чернышёва' for t in chernyshev}) # Восток
return d
'''
Состав дивизионов по сезонам (имена команд - ровно как в соответствующем csv).
Источники:
2020/21 - официальное распределение КХЛ;
2021/22 - Википедия "КХЛ в сезоне 2021/2022";
2018/19, 2019/20 - стандартная сетка дивизионов тех сезонов.
Для посева важен только победитель каждого дивизиона; выходные таблицы ниже
сверены с официальным сайтом КХЛ по всем четырём сезонам и совпадают.
'''
DIVISIONS = {
'2018_19': make_div(
bobrov=['СКА', 'Йокерит', 'Динамо М', 'Динамо Мн', 'Динамо Р', 'Слован'],
tarasov=['ЦСКА', 'Локомотив', 'ХК Сочи', 'Спартак', 'Витязь', 'Северсталь'],
kharlamov=['Автомобилист', 'Металлург Мг', 'Ак Барс', 'Трактор',
'Нефтехимик', 'Куньлунь РС', 'Адмирал'],
chernyshev=['Барыс', 'Авангард', 'Салават Юлаев', 'Сибирь',
'Торпедо НН', 'Амур'],
),
'2019_20': make_div(
bobrov=['СКА', 'Йокерит', 'Спартак', 'Динамо М', 'Динамо Мн', 'Динамо Р'],
tarasov=['ЦСКА', 'Локомотив', 'Витязь', 'Торпедо НН', 'ХК Сочи', 'Северсталь'],
kharlamov=['Ак Барс', 'Металлург Мг', 'Автомобилист', 'Нефтехимик',
'Трактор', 'Адмирал'],
chernyshev=['Авангард', 'Барыс', 'Салават Юлаев', 'Сибирь',
'Амур', 'Куньлунь РС'],
),
'2020_21': make_div(
bobrov=['СКА', 'Йокерит', 'Северсталь', 'Спартак', 'Витязь', 'ХК Сочи'],
tarasov=['ЦСКА', 'Динамо М', 'Локомотив', 'Динамо Мн', 'Динамо Р'],
kharlamov=['Автомобилист', 'Ак Барс', 'Металлург Мг', 'Нефтехимик',
'Трактор', 'Торпедо НН'],
chernyshev=['Авангард', 'Амур', 'Барыс', 'Куньлунь РС',
'Салават Юлаев', 'Сибирь'],
),
'2021_22': make_div(
bobrov=['Витязь', 'Йокерит', 'СКА', 'ХК Сочи', 'Торпедо', 'Спартак'],
tarasov=['Динамо М', 'Динамо Мн', 'Динамо Р', 'Локомотив', 'Северсталь', 'ЦСКА'],
kharlamov=['Автомобилист', 'Ак Барс', 'Куньлунь РС', 'Металлург Мг',
'Нефтехимик', 'Трактор'],
chernyshev=['Авангард', 'Амур', 'Барыс', 'Сибирь', 'Салават Юлаев', 'Адмирал'],
),
}
'''
В каких сезонах правило посева победителей дивизионов отражалось в таблице конференции.
2018/19, 2019/20, 2020/21 - да; в 2021/22 КХЛ перешла к чистому порядку по %О
(на сайте за 2021/22 победители дивизионов наверх НЕ поднимаются).
В сезонах 2018/19 и 2019/20 победители дивизионов и так были в топ-2 по очкам,
поэтому правило там виден результат не меняет - но оставляем его включённым,
чтобы логика была единой.
'''
SEED_BY_DIVISION = {
'2018_19': True,
'2019_20': True,
'2020_21': True,
'2021_22': False,
}
def seed_conference(conf_table):
'''
conf_table - часть итоговой таблицы по одной конференции, УЖЕ отсортированная
по критериям КХЛ (%О, затем В, ВО, ВБ, Р, ГЗ) - так её отдаёт build_standings.
Берём лучшего из каждого дивизиона (это и есть победитель дивизиона, т.к. таблица
отсортирована) и ставим этих двоих на места 1-2; между собой они идут в том же
порядке сортировки. Остальные команды - следом, тоже в порядке сортировки.
'''
# head(1) внутри каждого дивизиона = первая (а значит, лучшая) команда дивизиона.
winners = conf_table.groupby('Див', sort=False).head(1)
rest = conf_table.drop(winners.index)
return pd.concat([winners, rest])
def show_table(table, title):
'''
Напечатать таблицу конференции в консоль.
То же, что show_table в main.py, но дополнительно выводит столбец 'Див'
(дивизион) - чтобы было видно, почему победители дивизионов оказались наверху.
Принимает: table - готовая (уже посеянная) таблица; title - заголовок.
Ничего не возвращает - только печатает.
'''
# Тот же набор колонок, что на сайте КХЛ, плюс столбец дивизиона.
cols = ['Див', 'И', 'В', 'ВО', 'ВБ', 'ПБ', 'ПО', 'П', 'ГЗ', 'ГП', 'Р', 'О', '%О']
view = table[cols].copy()
view.insert(0, 'Место', np.arange(1, len(view) + 1))
view.insert(1, 'Команда', view.index)
print('\n' + '=' * 86)
print(title)
print('=' * 86)
print(view.to_string(index=False))
def main():
'''
Точка входа. Шаги:
1) определить сезон по имени CSV-файла;
2) построить таблицу команд через build_standings (логика из main.py);
3) приписать каждой команде дивизион (столбец 'Див');
4) для каждой конференции при необходимости применить посев победителей
дивизионов (seed_conference) и напечатать результат.
Пример: python3 divisions.py khl_3/khl_2020_21.csv
'''
path = sys.argv[1]
name = os.path.basename(path)
season = name.replace('khl_', '').replace('.csv', '')
df = pd.read_csv(path)
table = build_standings(df, CONFERENCES[season])
# Подписываем каждой команде её дивизион.
table['Див'] = pd.Series(table.index, index=table.index).map(DIVISIONS[season])
use_seeding = SEED_BY_DIVISION[season]
for conf in ['Запад', 'Восток']:
part = table[table['Конф'] == conf]
if use_seeding:
part = seed_conference(part)
suffix = ' (посев по дивизионам)' if use_seeding else ' (по очкам, без посева)'
show_table(part, 'Конференция ' + conf + ' ' + season + suffix)
main()

123
main.py
View file

@ -1,3 +1,25 @@
'''
main.py - построение турнирной таблицы КХЛ из файла с результатами матчей.
ОБЩАЯ ЛОГИКА (что происходит, сверху вниз):
1. Запуск: python3 main.py khl_3/khl_2018_19.csv
2. По имени файла определяется код сезона (например, "2018_19") и берётся
словарь "команда -> конференция" из CONFERENCES.
3. CSV читается в таблицу матчей df (одна строка = один матч, счёт по периодам).
4. build_standings(df, conf_map) превращает матчи в турнирную таблицу команд:
по каждому матчу определяется исход (победа/поражение и каким образом),
результаты разносятся на обе команды, суммируются и сортируются по правилам КХЛ.
5. show_table(...) печатает таблицу: общую по чемпионату и отдельно по двум
конференциям (Запад / Восток).
ВХОДНЫЕ ДАННЫЕ (столбцы CSV):
Команда_1 - хозяева, Команда_2 - гости;
Период_1/2/3 - счёт по периодам в формате "3:1" (хозяева:гости);
Овертайм, Буллиты - счёт за овертайм и серию буллитов (или ":" если их не было).
Зависимости: pandas (таблицы), numpy (нумерация мест).
'''
import sys # для аргументов командной строки import sys # для аргументов командной строки
import os # чтобы достать имя файла из пути import os # чтобы достать имя файла из пути
import pandas as pd import pandas as pd
@ -5,6 +27,14 @@ import numpy as np
def make_conf(west, east): def make_conf(west, east):
'''
Собрать словарь {название команды -> конференция} из двух списков команд.
Принимает: west, east - списки названий команд Западной и Восточной конференций.
Возвращает: dict, где каждой команде сопоставлена строка 'Запад' или 'Восток'.
Нужен, чтобы позже одной операцией .map() приписать каждой команде её конференцию.
'''
d = {} d = {}
d.update({team: 'Запад' for team in west}) d.update({team: 'Запад' for team in west})
d.update({team: 'Восток' for team in east}) d.update({team: 'Восток' for team in east})
@ -43,32 +73,46 @@ CONFERENCES = {
), ),
} }
'''
Шаг 1.
Формат счёта: 3:1 (хозяева забили 3, гости 1).
Если период не игрался (нет овертайма или буллитов), в файле стоит ":" - пустые числа.
df[col].str.split(':', expand=True) применяет строковую операцию split(':')
сразу ко всем строкам столбца и раскладывает результат в два столбца (expand=True).
pd.to_numeric превращает текст "3" в число 3. Параметр errors='coerce' означает:
если это не число (например, пустая строка), сделай NaN, а fillna(0) заменяет такие
пустые места на 0. В итоге не сыгранный период считается как "0:0".
'''
def parse_score(df, col): def parse_score(df, col):
'''
Шаг 1. Разобрать столбец со счётом вида "3:1" на два числовых столбца.
Принимает: df - таблица матчей; col - имя столбца ('Период_1', 'Овертайм' и т.п.).
Возвращает: (home, away) - два числовых столбца (Series): голы хозяев и голы гостей.
Как работает:
- str.split(':', expand=True) разбивает "3:1" на части "3" и "1" сразу по
всему столбцу и раскладывает их в два столбца (expand=True);
- pd.to_numeric(..., errors='coerce') превращает текст в число, а всё, что
числом не является (пустая строка при ":" или знаки "+ — -"), делает NaN;
- fillna(0) заменяет NaN на 0. Итог: не сыгранный период читается как "0:0".
'''
parts = df[col].str.split(':', expand=True) parts = df[col].str.split(':', expand=True)
home = pd.to_numeric(parts[0], errors='coerce').fillna(0) home = pd.to_numeric(parts[0], errors='coerce').fillna(0)
away = pd.to_numeric(parts[1], errors='coerce').fillna(0) away = pd.to_numeric(parts[1], errors='coerce').fillna(0)
return home, away return home, away
# Шаг 2. Функция, которая из таблицы матчей строит таблицу команд.
def build_standings(df, conf_map): def build_standings(df, conf_map):
'''
Шаг 2. Превратить таблицу матчей в турнирную таблицу команд.
Принимает: df - матчи (одна строка = один матч); conf_map - словарь
{команда -> конференция} из make_conf.
Возвращает: таблицу (DataFrame), где индекс - название команды, а столбцы -
И, В, ВО, ВБ, ПБ, ПО, П, ГЗ, ГП, О, Р, %О, Конф; строки уже
отсортированы по правилам КХЛ (лучшая команда сверху).
Идея целиком:
1) разбираем счёт по периодам/овертайму/буллитам;
2) по каждому матчу определяем ОДИН из шести исходов для хозяев и для гостей
(победа в основное/овертайме/буллитах и зеркальные поражения);
3) отдельно учитываем технические результаты ("+ — -" / "- — +");
4) считаем заброшенные/пропущенные шайбы;
5) разносим каждый матч на две строки (взгляд хозяев и взгляд гостей),
склеиваем и группируем по команде - получаем суммы по сезону;
6) считаем производные показатели (И, О, Р, %О) и сортируем.
'''
# Достаём счёт по периодам и т.д. # Достаём счёт по периодам и т.д.
h1, a1 = parse_score(df, 'Период_1') # голы хозяев/гостей в 1 периоде h1, a1 = parse_score(df, 'Период_1') # голы хозяев/гостей в 1 периоде
h2, a2 = parse_score(df, 'Период_2') # второй h2, a2 = parse_score(df, 'Период_2') # второй
@ -94,6 +138,25 @@ def build_standings(df, conf_map):
win_home_so = tie & (oth == ota) & (soh > soa) # ОТ ничейный -> буллиты выиграли хозяева win_home_so = tie & (oth == ota) & (soh > soa) # ОТ ничейный -> буллиты выиграли хозяева
win_away_so = tie & (oth == ota) & (soa > soh) # буллиты выиграли гости win_away_so = tie & (oth == ota) & (soa > soh) # буллиты выиграли гости
'''
Технические результаты (неявка/отмена матча). В файле счёт записан не цифрами,
а знаками: "+ — -" - техническая победа хозяев, "- — +" - техническая победа гостей.
КХЛ засчитывает их как победу/поражение в основное время (2 и 0 очков), но шайбы
при этом НЕ начисляются (в графу Ш такой матч идёт как 0:0).
'''
p1 = df['Период_1'].astype(str).str.strip()
tech_home = p1.str.startswith('+') # техническая победа хозяев
tech_away = p1.str.endswith('+') # техническая победа гостей
tech = tech_home | tech_away
# Для технических матчей перекрываем обычную классификацию (там сейчас "ничья 0:0").
win_home_reg = (win_home_reg & ~tech) | tech_home
win_away_reg = (win_away_reg & ~tech) | tech_away
win_home_ot = win_home_ot & ~tech
win_away_ot = win_away_ot & ~tech
win_home_so = win_home_so & ~tech
win_away_so = win_away_so & ~tech
''' '''
Считаем заброшенные шайбы для итоговой таблицы. Считаем заброшенные шайбы для итоговой таблицы.
К голам в основное время прибавляем голы овертайма, а победителю серии буллитов К голам в основное время прибавляем голы овертайма, а победителю серии буллитов
@ -160,9 +223,11 @@ def build_standings(df, conf_map):
%О - процент набранных очков = очки / максимально возможные (2 за каждую игру). %О - процент набранных очков = очки / максимально возможные (2 за каждую игру).
По этому проценту КХЛ расставляет места, когда команды сыграли разное число игр. По этому проценту КХЛ расставляет места, когда команды сыграли разное число игр.
Сайт КХЛ НЕ округляет, а отбрасывает знаки после второго (72.448 -> 72.44), Сайт КХЛ НЕ округляет, а отбрасывает знаки после второго (72.448 -> 72.44),
поэтому np.floor (округление вниз): умножаем на 10000, отбрасываем дробь, делим на 100. поэтому считаем в целых числах: умножаем на 10000 и делим нацело (//) на 2*И -
остаток (дробная часть) отбрасывается, затем делим на 100. Целочисленная
арифметика без float, поэтому ровные значения (57.00) не съезжают в 56.99.
''' '''
table['%О'] = np.floor(table['О'] / (2 * table['И']) * 10000) / 100 table['%О'] = (table['О'] * 10000 // (2 * table['И'])) / 100
# Подписываем каждой команде её конференцию. .map(словарь) проходит по индексу # Подписываем каждой команде её конференцию. .map(словарь) проходит по индексу
# (названиям команд) и подставляет значение из словаря. # (названиям команд) и подставляет значение из словаря.
table['Конф'] = pd.Series(table.index, index=table.index).map(conf_map) table['Конф'] = pd.Series(table.index, index=table.index).map(conf_map)
@ -178,8 +243,18 @@ def build_standings(df, conf_map):
return table return table
# Вывод таблицы текстом
def show_table(table, title): def show_table(table, title):
'''
Напечатать турнирную таблицу в консоль в виде текста.
Принимает: table - готовая (уже отсортированная) таблица команд;
title - заголовок над таблицей.
Ничего не возвращает - только печатает.
Что делает: оставляет нужные столбцы в порядке, как на сайте КХЛ, добавляет
слева колонку "Место" (1..N по порядку строк) и колонку "Команда"
(название берётся из индекса таблицы), затем выводит всё через to_string.
'''
# Колонки в том же порядке и с теми же подписями, что на сайте КХЛ. # Колонки в том же порядке и с теми же подписями, что на сайте КХЛ.
cols = ['И', 'В', 'ВО', 'ВБ', 'ПБ', 'ПО', 'П', 'ГЗ', 'ГП', 'Р', 'О', '%О'] cols = ['И', 'В', 'ВО', 'ВБ', 'ПБ', 'ПО', 'П', 'ГЗ', 'ГП', 'Р', 'О', '%О']
view = table[cols].copy() # берём только нужные столбцы (копия, чтобы не трогать оригинал) view = table[cols].copy() # берём только нужные столбцы (копия, чтобы не трогать оригинал)
@ -195,6 +270,12 @@ def show_table(table, title):
def main(): def main():
'''
Точка входа. Связывает все шаги вместе:
читает путь к CSV из аргумента командной строки -> по имени файла определяет
сезон -> строит таблицу через build_standings -> печатает общую таблицу и две
таблицы по конференциям (Запад/Восток), отфильтрованные по столбцу 'Конф'.
'''
path = sys.argv[1] # получаем путь до csv path = sys.argv[1] # получаем путь до csv
name = os.path.basename(path) # из khl_3/khl_*.csv берём только khl_*.csv name = os.path.basename(path) # из khl_3/khl_*.csv берём только khl_*.csv
season = name.replace('khl_', '').replace('.csv', '') season = name.replace('khl_', '').replace('.csv', '')