Spaces:
Running on Zero
Running on Zero
File size: 8,580 Bytes
1f62eb2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | # Process to evaluate team season succcess,
# over all available play-level data (2011-current)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, FixedFormatter, FixedLocator
plt.rcParams.update({'font.size': 16})
# Directories - locate your data location from the scraping exercise
# https://github.com/justinjoliver/NHL-Analytics/tree/main/src/data
str_dirc = '__DATA DIR__';
# import relevant functions and import process data
str_dir_sourceCode = "__SOURE DIR__"
exec(open(str_dir_sourceCode + "/measurement_teamSuccess.py").read())
# Last year season progress
idx_yr_last = 2022
list_years = np.arange(2011, idx_yr_last)
for iter_yr in list_years:
# Pull the latest data for process
df_box = pd.read_csv(str_dirc + f"/cleaned/{iter_yr}_02_box.csv",
parse_dates = ['gameDate'],
index_col = 'gameIdx')
# Unpack data
df_box_exnt = df_unpack(df_box)
#df_box_exnt.columns
# Calculate metrics
df_box_exnt = df_metrics(df_box_exnt)
# Aggregate up for team-level statistics
dfteams = df_box_exnt.groupby(['team_tri_for'])\
.agg(
{
"fenwick_lvl":"sum",
"corsi_lvl":"sum",
"rpe":"last",
"rwin":"last",
"rgame":"last"
}
)
dfteams.sort_values(by = ["rwin","fenwick_lvl", "corsi_lvl"], inplace = True)
# Join with season-pythagorean expectation
#dfteams = dfteams.join(df_pe_season)
# Normalize KPIs for measurement comparison
dfteams['wp'] = dfteams.rwin / dfteams.rgame
dfteams['kpi_corsi'] = kpinorm(dfteams.corsi_lvl)
dfteams['kpi_fenwick'] = kpinorm(dfteams.fenwick_lvl)
dfteams['kpi_pe'] = kpinorm(dfteams.rpe)
# Coefficient estimates from 2010-2020, calculate fitted win percentage
beta_pyth_old = 0.00809623 * 100
alpah_pyth_old = 0.042901
x_starting = 0.3
x_end = x_starting + 0.45
y_starting = (x_starting) * beta_pyth_old + alpah_pyth_old
y_end = (x_end) * beta_pyth_old + alpah_pyth_old
dfteams['rwin_fittedPE'] = (dfteams['rpe'] * beta_pyth_old + alpah_pyth_old) * 82
# drop the first game,
df_box_exnt.loc[df_box_exnt.gameDate_del_against.notnull(), :].head(3)
time_games_all = np.sort(df_box_exnt.gameDate_for.unique()); # by date existing
# Capture the last date.
iter_time = time_games_all[-1];
# NOTE: while it may be exciting to compute the measure in real time, it takes
# some time to compute it all. I focus on looking at upto the latest available
# data
# Head to head statistics
dff_h2h_com = df_gen_h2h_common(df_box_exnt, iter_time)
# Game pairwise - RPI statistics
dfteams = dfteams.join(pairwise_h2h(df_box_exnt, dff_h2h_com))
# More head-to-head statistics
dff_h2h_com_win = dff_h2h_com\
.groupby(['team_tri_for_ow', 'team_tri_for_oow']) \
.agg(
{
'game_won_h2h_ow':'sum',
'game_played_h2h_ow':'sum',
'game_won_h2h_oow':'sum',
'game_played_h2h_oow':'sum'
}
)
# for each opponent, compare who has winning record as common opponent
# I use win ratio
dff_h2h_com_win['h2h_win'] = (\
(dff_h2h_com_win.game_won_h2h_ow / dff_h2h_com_win.game_played_h2h_ow) > \
(dff_h2h_com_win.game_won_h2h_oow / dff_h2h_com_win.game_played_h2h_oow)
) * 1
# For each team, count how many opponents the team has winning record against
rank_pairwise_win = dff_h2h_com_win.groupby(['team_tri_for_ow']) \
.agg({'h2h_win':'sum'}) \
.sort_values(by = 'h2h_win', ascending = False)
rank_pairwise_win.columns = ['pairwise_win']
# Here the rank can even be more continuous (how much they win instead of count in games)
# Join with the existing team stats
dfteams = dfteams.join(rank_pairwise_win)
# Normalize
dfteams['kpi_pairwise'] = kpinorm(dfteams.pairwise_win)
dfteams['kpi_rpi'] = kpinorm(dfteams.rpi)
dfteams['kpi_wp'] = kpinorm(dfteams.wp)
dfteams['Final'] = dfteams\
.sort_values(by = 'kpi_wp', ascending = False) \
.reset_index().index + 1
dfteams['Final'] = kpinorm(dfteams.Final)
# Data append
# Add season statistics for and against
df_box_exnt = df_box_exnt.merge(dfteams.reset_index(),
on = ['team_tri_for'], suffixes=('', '_for')) \
.merge(dfteams.reset_index(),
left_on = ['team_tri_against'],
right_on = ['team_tri_for'],
suffixes=('', '_against'))
# Check wins against better opponent
# Is the team-for facing eventual better opponent?
# Using pairwise here - alternatively can use RPI
# ...........................................................................
# Observing win streaks
# Is opponent better than average?
df_box_exnt['idx_OppoGood'] = df_box_exnt.rpi_against > \
np.quantile(df_box_exnt.rpi_against, 0.75)
# Playing against worse team, thus leading to expected outcome?
df_box_exnt['idx_OppoBetter'] = df_box_exnt.pairwise_win_against < \
df_box_exnt.pairwise_win
df_box_exnt['idx_OppoBetter'] = df_box_exnt.rpi_against < \
df_box_exnt.rpi
df_box_exnt['win_for_better'] = df_box_exnt.win_for * df_box_exnt.idx_OppoGood
df_box_exnt['win_for_expected'] = df_box_exnt.win_for * df_box_exnt.idx_OppoBetter
pivot_teamcumwin = df_box_exnt \
.groupby(['team_tri_for', 'rgame']) \
.agg(
win = ("win_for", "sum"),
win_better = ("win_for_better", "sum"),
win_expected = ("win_for_expected", "sum")
) \
.reset_index()
for iter_lag in np.arange(1,3):
pivot_teamcumwin[f'win_lag{iter_lag}'] = pivot_teamcumwin \
.groupby(['team_tri_for']).win.shift(iter_lag)
pivot_teamcumwin[f'win_better_lag{iter_lag}'] = pivot_teamcumwin \
.groupby(['team_tri_for']).win_better.shift(iter_lag)
pivot_teamcumwin[f'win_expected_lag{iter_lag}'] = pivot_teamcumwin \
.groupby(['team_tri_for']).win_expected.shift(iter_lag)
# Identify all win streak
pivot_teamcumwin['idx_WinStreak'] = 0
pivot_teamcumwin.loc[(np.sum(pivot_teamcumwin[['win','win_lag1', 'win_lag2']], axis = 1) == 3), 'idx_WinStreak'] = 1
# Identify win streak with games all expected to win
pivot_teamcumwin['idx_WinStreak_expected'] = 0
pivot_teamcumwin.loc[\
(np.sum(pivot_teamcumwin[['win_expected','win_expected_lag1', 'win_expected_lag2']], axis = 1) >= 2) \
& pivot_teamcumwin['idx_WinStreak'] == 1 \
, 'idx_WinStreak_expected'] = 1
# Identify win streak against good team: 75%tile and above RPI
pivot_teamcumwin['idx_HotStreak'] = 0
pivot_teamcumwin.loc[(pivot_teamcumwin.idx_WinStreak == 1) & \
(np.sum(pivot_teamcumwin[['win_better', 'win_better_lag1', 'win_better_lag2']], axis = 1) >= 1), \
'idx_HotStreak'] = 1
df_streak = pivot_teamcumwin[['team_tri_for',
'idx_WinStreak',
'idx_WinStreak_expected',
'idx_HotStreak']] \
.groupby('team_tri_for').sum()
df_streak['ratio'] = df_streak.idx_HotStreak / df_streak.idx_WinStreak
df_streak['ratio_viz'] = np.int0(df_streak['ratio']*100)
df_streak['ratio_viz'] = [str(iter) + '%' for iter in df_streak['ratio_viz']]
# Sort value for future ease of use
# Finally join with data
dfteams = dfteams.join(df_streak)
# ===========================================================================
# Some plotting
exec(open(str_dir_sourceCode + "/evaluation_winStreak_plot.py").read())
# ...........................................................................
# output data
# (a) game-level stats
df_box_exnt['yr_season'] = iter_yr
df_box_exnt.to_csv(str_dirc + f"/processed/{iter_yr}_02_box.csv")
# (b) team-level stats for each season
dfteams['yr_season'] = iter_yr
dfteams.to_csv(str_dirc + f"/processed/{iter_yr}_02_team.csv")
# (c) team-level head-to-head statistics
dff_h2h_com_win['yr_season'] = iter_yr |