def play_game(self): elo = Elo() # get expected results for all systems wl1 = self.team1.wl wl2 = self.team2.wl wlmx = (wl1 + (1 - wl2)) / 2 elo_diff = self.team1.elo - self.team2.elo dim_diff = self.team1.dim - self.team2.dim mov_diff = self.team1.mov - self.team2.mov dimv_diff = self.team1.dimv - self.team2.dimv # baseline: 1.215, 1.187, 1.189, 1.1646 if self.loc == "H": elo_diff += h_adv dim_diff += h_adv mov_diff += h_adv dimv_diff += h_adv elif self.loc == "A": elo_diff -= h_adv dim_diff -= h_adv mov_diff -= h_adv dimv_diff -= h_adv # basic elo systems elox = elo.get_expected(elo_diff) dimx = elo.get_expected(dim_diff) # mov systems movx = elo.get_expected(mov_diff) dimvx = elo.get_expected(dimv_diff) # glicko # glickox = glicko.get_expected(self.team1.glicko, self.team2.glicko) # step # stephx = steph.get_expected(self.team1.steph, self.team2.steph) self.team1.played_game() self.team2.played_game() self.team1.add_win() self.team2.add_loss() self.team1.calc_win_loss() self.team2.calc_win_loss() p1_x = { "wlm": wlmx, "elo": elox, "dim": dimx, "mov": movx, "dimv": dimvx, # "glicko":glickox, # "steph":stephx } p2_x = { "wlm": 1 - wlmx, "elo": 1 - elox, "dim": 1 - dimx, "mov": 1 - movx, "dimv": 1 - dimvx, # "glicko":glickox, # "steph":stephx } self.team1.add_errors(1, p1_x) self.team2.add_errors(0, p2_x) # update ratings elo_K = elo_set['K'] elo_delta = elo.get_delta(1, elox, elo_K) if self.games_played <= 2: dim_K = 170 elif self.games_played <= 4: dim_K = 97.5 elif self.games_played <= 5: dim_K = 67 elif self.games_played <= 8: dim_K = 67 elif self.games_played <= 10: dim_K = 30 else: dim_K = dim_set['K'] dim_delta = elo.get_delta(1, dimx, dim_K) mov_K = mov_set['K'] mov_delta = elo.get_mov_delta(1, movx, self.margin, (self.team1.mov - self.team2.mov), mov_K) if self.games_played <= 4: dimv_K = 60 # else: # dimv_K = 170.59 * (self.games_played ** -0.673) elif self.games_played <= 8: dimv_K = 45 elif self.games_played <= 12: dimv_K = 31 elif self.games_played <= 16: dimv_K = 28.5 elif self.games_played <= 20: dimv_K = 22 elif self.games_played <= 24: dimv_K = 18 elif self.games_played <= 28: dimv_K = 18 elif self.games_played <= 32: dimv_K = 18 else: dimv_K = dimv_set['K'] dimv_delta = elo.get_mov_delta(1, dimvx, self.margin, (self.team1.dimv - self.team2.dimv), dimv_K) self.team1.elo += elo_delta self.team1.dim += dim_delta self.team1.mov += mov_delta self.team1.dimv += dimv_delta self.team2.elo -= elo_delta self.team2.dim -= dim_delta self.team2.mov -= mov_delta self.team2.dimv -= dimv_delta return self.team1, self.team2
def test_systems(): # load season data sdf = pd.read_csv('./data/SeasonResults.csv') # separate data into individual seasons seasons = list(sdf.Season.unique()) # set how long before glicko updates g_resolve = glicko_set['resolve_time'] # track error per season sea_error = [] wkbywk_err = None first_season = seasons[0] for season in tqdm(seasons): sea_df = sdf.loc[sdf.Season == season] # sort in order sea_df = sea_df.sort_values(by='DayNum') sea_df = sea_df[[ 'Season', 'DayNum', 'WTeam', 'WScore', 'LTeam', 'LScore' ]] # get list of teams in season wteams = list(sea_df.WTeam.unique()) lteams = list(sea_df.LTeam.unique()) teams = list(set((wteams + lteams))) load_preseason = False # use for preseason rankings if season > first_season: load_preseason = True else: prev_team_dir = None # create team directory to track everything team_dir = init_td(teams, load_preseason, prev_team_dir) # init classes elo = Elo() glicko = Glicko() # track error per week week_err = [] wk_l5err = wk_eloerr = wk_ieloerr = wk_gerr = wk_tserr = 0 wk_gp = 0 wk_thres = 7 wk_cnt = 0 # iterate games for index, row in sea_df.iterrows(): t1 = row['WTeam'] t2 = row['LTeam'] team1 = team_dir[t1] team2 = team_dir[t2] # set max number of games for testing # if (team1.gp > 11): # continue # if (team2.gp > 11): # continue # tracking error by week, so check if it's a new week day_num = row['DayNum'] if day_num > wk_thres: # it's a new week # add end date of next week wk_thres += 7 # ignore weeks that don't have games if wk_gp > 0: wk_l5err /= wk_gp wk_eloerr /= wk_gp wk_ieloerr /= wk_gp wk_gerr /= wk_gp wk_tserr /= wk_gp week_err.append([ season, wk_cnt, wk_l5err, wk_eloerr, wk_ieloerr, wk_gerr, wk_tserr ]) wk_cnt += 1 wk_l5err = wk_eloerr = wk_ieloerr = wk_gerr = wk_serr = 0 wk_gp = 0 # track games played this week wk_gp += 1 margin = row['WScore'] - row['LScore'] # get expected outcome for each system log5_expect = l5_x(team1.wl, team2.wl) elo_expect = elo.x(team1.elo, team2.elo) ielo_expect = elo.x(team1.ielo, team2.ielo) ts_expect = ts_win_prob([team1.tskill], [team2.tskill]) # special steps for glicko expectation mu, phi = glicko.scale_down(team1.glicko, team1.g_phi) mu2, phi2 = glicko.scale_down(team2.glicko, team2.g_phi) impact = glicko.reduce_impact(phi2) glicko_expect = glicko.get_expected(mu, mu2, impact) # update error if log5_expect == 0: log5_expect += .001 expects = [ log5_expect, elo_expect, ielo_expect, glicko_expect, ts_expect ] t1_errors = calc_error(expects, 1) t2_errors = t1_errors team1.update_errors(t1_errors) team2.update_errors(t2_errors) # update week error wk_l5err += t1_errors[0] wk_eloerr += t1_errors[1] wk_ieloerr += t1_errors[2] wk_gerr += t1_errors[3] wk_tserr += t1_errors[4] ## update ratings ## # elo elo_delta = elo.get_delta(elo_expect) t1_ielo_delta, t2_ielo_delta = elo.get_ielo_delta( ielo_expect, margin, team1, team2) team1.update_rating("elo", elo_delta) team1.update_rating("ielo", t1_ielo_delta) team2.update_rating("elo", -elo_delta) team2.update_rating("ielo", t2_ielo_delta) team1.update_ts(team2.tskill, "won") team2.update_ts(team1.tskill, "lost") # log5 team1.add_win() team2.add_loss() # glicko (second arg is win or loss) team1.add_glicko_opp(team2, 1) team2.add_glicko_opp(team1, 0) # check if time to resolve if team1.gp % g_resolve == 0: team1 = glicko.update(team1) if team2.gp % g_resolve == 0: team2 = glicko.update(team2) team_dir[t1] = team1 team_dir[t2] = team2 # add week_err df to season trackers week_err = pd.DataFrame( week_err, columns=['Season', 'Week', 'Log5', 'Elo', 'IElo', 'Glicko', 'TS']) if wkbywk_err is None: wkbywk_err = week_err else: wkbywk_err = pd.concat([wkbywk_err, week_err]) # find total error in season sea_gp = 0 sea_l5err = 0 sea_eloerr = 0 sea_ieloerr = 0 sea_gerr = 0 sea_tserr = 0 for team in team_dir.values(): sea_gp += team.gp sea_l5err += team.l5err sea_eloerr += team.eloerr sea_ieloerr += team.ieloerr sea_gerr += team.glickoerr sea_tserr += team.tserr sea_l5err /= sea_gp sea_eloerr /= sea_gp sea_ieloerr /= sea_gp sea_gerr /= sea_gp sea_tserr /= sea_gp sea_error.append( [season, sea_l5err, sea_eloerr, sea_ieloerr, sea_gerr, sea_tserr]) # store rankings for preseason rankings next season prev_team_dir = team_dir final_table = pd.DataFrame( sea_error, columns=['Season', 'Log5', 'Elo', 'IElo', 'Glicko', 'TS']) print(final_table) print(final_table.mean()) wkbywk = pd.DataFrame( wkbywk_err, columns=['Season', 'Week', 'Log5', 'Elo', 'IElo', 'Glicko', 'TS']) wkbywk = wkbywk.drop(columns=['TS']) wk_avg = wkbywk.groupby('Week').mean() def plot_weeks(wk_avg): import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(15, 7)) plt.plot(wk_avg.index.values, wk_avg.Log5, '-k', label='Log5 Baseline') plt.plot(wk_avg.index.values, wk_avg.Elo, '-c', label='Elo') plt.plot(wk_avg.index.values, wk_avg.IElo, '-b', label='Improved Elo') plt.plot(wk_avg.index.values, wk_avg.Glicko, '-r', label='Glicko') plt.xlabel("Week of Season") plt.ylabel("Cross Entropy Error") xint = range(0, math.ceil(17) + 1) plt.xticks(xint) plt.legend(loc='upper left') plt.show() return plot_weeks(wk_avg) return