def rank(player1, player2): """ This function will calculate the TrueSkill ranking of each player. It accepts two players arguments. The first is the winning player, the second is the losing player. Both should be Profile objects. This will not save the players. Args: player1: The winning user profile player2: The losing user profile Returns: A tuple of (player1, player2) """ DRAW_PROBABILITY = 0 # It's impossible to draw 1 on 1 approx = lambda f: round(f, 6) # Round all floats to 6 decimals ts = TrueSkill(draw_probability=DRAW_PROBABILITY) t1 = (ts.Rating(mu=player1.mu, sigma=player1.sigma), ) t2 = (ts.Rating(mu=player2.mu, sigma=player2.sigma), ) r1, r2 = tuple(x[0] for x in ts.transform_ratings(rating_groups=(t1, t2))) player1.mu = approx(r1.mu) player1.sigma = approx(r1.sigma) player1.exposure = approx(r1.exposure) player2.mu = approx(r2.mu) player2.sigma = approx(r2.sigma) player2.exposure = approx(r2.exposure) player1.save() player2.save() return player1, player2
def createTrueskillRating(): ts = TrueSkill(draw_probability=0.01) # 0.01 is arbitary small number beta = 25 / 6 # default value def win_probability(p1, p2): delta_mu = p1.mu - p2.mu sum_sigma = p1.sigma * p1.sigma + p2.sigma * p2.sigma denom = np.sqrt(2 * (beta * beta) + sum_sigma) return ts.cdf(delta_mu / denom) submit = sample_sub_pd submit[['Season', 'Team1', 'Team2']] = submit.apply(lambda r:pd.Series([int(t) for t in r.ID.split('_')]), axis=1) df_tour = reg_season_compact_pd teamIds = np.unique(np.concatenate([df_tour.WTeamID.values, df_tour.LTeamID.values])) ratings = { tid:ts.Rating() for tid in teamIds } def feed_season_results(season): print("season = {}".format(season)) df1 = df_tour[df_tour.Season == season] for r in df1.itertuples(): ratings[r.WTeamID], ratings[r.LTeamID] = rate_1vs1(ratings[r.WTeamID], ratings[r.LTeamID]) def update_pred(season): beta = np.std([r.mu for r in ratings.values()]) print("beta = {}".format(beta)) submit.loc[submit.Season==season, 'Pred'] = submit[submit.Season==season].apply(lambda r:win_probability(ratings[r.Team1], ratings[r.Team2]), axis=1) for season in sorted(df_tour.Season.unique()[:-1]): # exclude last 4 years [:-4]/ last 1 year [:-1] feed_season_results(season) # update_pred(2014) # feed_season_results(2014) # update_pred(2015) # feed_season_results(2015) # update_pred(2016) # feed_season_results(2016) # update_pred(2017) feed_season_results(2017) update_pred(2018) submit.drop(['Season', 'Team1', 'Team2'], axis=1, inplace=True) submit.to_csv('Data/Predictions/trueskill_results2018.csv', index=None)
class FrcTrueSkill: # Constants for sending requests to TBA. TBA_API_BASE = 'https://www.thebluealliance.com/api/v2' HEADERS = {"X-TBA-App-Id": "frc-4774:TrueSkill:1.0"} # Ranks for TrueSkill.rate. Lower is better. WON = 0 LOST = 1 TIE = (WON, WON) RED_WIN = (WON, LOST) BLUE_WIN = (LOST, WON) def __init__(self): self.env = TrueSkill(draw_probability=0.02) self.trueskills = {} self.events = {} self.nicknames = {} self.processed_matches = set() self.session = requests.Session() self.session.headers.update(self.HEADERS) self.get_previous_matches() def init_teams(self, red_alliance, blue_alliance): for team in red_alliance + blue_alliance: if team not in self.trueskills: self.trueskills[team] = self.env.Rating() def update(self, match_data): if match_data['key'] in self.processed_matches: return None alliances = match_data['alliances'] red_teams = [int(x[3:]) for x in alliances['red']['teams']] blue_teams = [int(x[3:]) for x in alliances['blue']['teams']] self.init_teams(red_teams, blue_teams) # Update ratings based on result corrected_scores = self.correct_scores(match_data) if corrected_scores.red == corrected_scores.blue: # Tied if corrected_scores.red == -1: return None # No result yet ranks = self.TIE elif corrected_scores.red > corrected_scores.blue: # Red beat blue ranks = self.RED_WIN else: ranks = self.BLUE_WIN new_red, new_blue = self.env.rate([ [self.trueskills[t] for t in red_teams], [self.trueskills[t] for t in blue_teams]], ranks) # Store the new values for team, rating in zip(red_teams + blue_teams, new_red + new_blue): self.trueskills[team] = rating self.processed_matches.add(match_data['key']) return ranks def predict(self, red_alliance, blue_alliance): self.init_teams(red_alliance, blue_alliance) a = [self.trueskills[t] for t in red_alliance] b = [self.trueskills[t] for t in blue_alliance] delta_mu = sum([x.mu for x in a]) - sum([x.mu for x in b]) sum_sigma = sum([x.sigma ** 2 for x in a + b]) player_count = len(a) + len(b) denominator = (player_count * (self.env.beta**2) + sum_sigma) ** 0.5 return backends.cdf(delta_mu / denominator) def skill(self, team): if team not in self.trueskills: self.trueskills[team] = self.env.Rating() return self.env.expose(self.trueskills[team]) def get_teams_at_event(self, event): if event not in self.events: # We haven't got this one yet teams = self.session.get("%s/event/%s/teams" % (self.TBA_API_BASE, event)) teams = teams.json() self.events[event] = [team["team_number"] for team in teams] for team in teams: self.nicknames[team["team_number"]] = team["nickname"] return self.events[event] def get_previous_matches(self): all_matches = [] events = self.session.get(self.TBA_API_BASE + "/events/2017") events = events.json() for event in events: if event['event_type'] > 5: continue if event['start_date'] <= str(datetime.date(datetime.today()+timedelta(days=1))): matches = self.session.get("%s/event/%s/matches" % (self.TBA_API_BASE, event['key'])) matches = matches.json() all_matches += matches all_matches.sort(key=lambda m: m['time']) for match in all_matches: self.update(match) def correct_scores(self, match): alliances = match['alliances'] red = alliances['red'] blue = alliances['blue'] score = match['score_breakdown'] red_score = red['score'] blue_score = blue['score'] if score is None: return Scores(red_score, blue_score) red_stats = score['red'] blue_stats = score['blue'] if red_stats["rotorRankingPointAchieved"]: red_score += 100 if red_stats["kPaRankingPointAchieved"]: red_score += 20 if blue_stats["rotorRankingPointAchieved"]: blue_score += 100 if blue_stats["kPaRankingPointAchieved"]: blue_score += 20 return Scores(red_score, blue_score)
def win_probability(p1, p2): delta_mu = p1.mu - p2.mu sum_sigma = p1.sigma * p1.sigma + p2.sigma * p2.sigma denom = np.sqrt(2 * (beta * beta) + sum_sigma) return ts.cdf(delta_mu / denom) submit = pd.read_csv('../input/SampleSubmissionStage1.csv') submit[['Season', 'Team1', 'Team2']] = submit.apply( lambda r: pd.Series([int(t) for t in r.ID.split('_')]), axis=1) df_tour = pd.read_csv('../input/RegularSeasonCompactResults.csv') teamIds = np.unique( np.concatenate([df_tour.WTeamID.values, df_tour.LTeamID.values])) ratings = {tid: ts.Rating() for tid in teamIds} def feed_season_results(season): print("season = {}".format(season)) df1 = df_tour[df_tour.Season == season] for r in df1.itertuples(): ratings[r.WTeamID], ratings[r.LTeamID] = rate_1vs1( ratings[r.WTeamID], ratings[r.LTeamID]) def update_pred(season): beta = np.std([r.mu for r in ratings.values()]) print("beta = {}".format(beta)) submit.loc[submit.Season == season, 'Pred'] = submit[ submit.Season == season].apply(
class FrcTrueSkill: def __init__(self): self.env = TrueSkill(draw_probability=0.02) self.trueskills = {} self.events = {} self.get_previous_matches() #for team in self.trueskills.keys(): # print team, self.skill(team) def update(self, red_alliance, red_score, blue_alliance, blue_score): # Calculate teams per alliance for alliance in [red_alliance, blue_alliance]: for team in alliance: if not team in self.trueskills: self.trueskills[team] = self.env.Rating() # Update ratings based on result if red_score == blue_score: # Tied if red_score == -1: return # No result yet ranks = [0, 0] elif red_score > blue_score: # Red beat blue ranks = [0, 1] # Lower is better else: ranks = [1, 0] new_red, new_blue = self.env.rate([[self.trueskills[number] for number in red_alliance], [self.trueskills[number] for number in blue_alliance]], ranks) # Store the new values new_ratings = new_red + new_blue for rating, team_number in zip(new_ratings, red_alliance + blue_alliance): self.trueskills[team_number] = rating def predict(self, red_alliance, blue_alliance): proba = self.env.quality([[self.trueskills[team] for team in red_alliance], [self.trueskills[team] for team in blue_alliance]]) return round(proba*100) def skill(self, team): return self.env.expose(self.trueskills[team]) def get_previous_matches(self): started_events = [] all_matches = [] events = requests.get("https://www.thebluealliance.com/api/v2/events/2017") events = events.json() for event in events: if event['event_type'] > 5: continue if event['start_date'] < str(datetime.date(datetime.today())): started_events.append(event["key"]) teams = requests.get("https://www.thebluealliance.com/api/v2/event/"+event['key']+"/teams", headers={"X-TBA-App-Id":"frc-4774:TrueSkill:1.0"}) teams = teams.json() self.events[event['key']] = teams for event in started_events: matches = requests.get("https://www.thebluealliance.com/api/v2/event/"+event+"/matches", headers={"X-TBA-App-Id":"frc-4774:TrueSkill:1.0"}) matches = matches.json() all_matches += matches all_matches.sort(key=lambda m: m['time']) for match in all_matches: score = match['score_breakdown'] if score is None: continue red_stats = score['red'] blue_stats = score['blue'] alliances = match['alliances'] red = alliances['red'] blue = alliances['blue'] if red_stats["rotor3Engaged"]: red['score'] += 100 if red_stats["kPaRankingPointAchieved"]: red['score'] += 20 if blue_stats["rotor3Engaged"]: blue['score'] += 100 if blue_stats["kPaRankingPointAchieved"]: blue['score'] += 20 self.update(red['teams'], red['score'], blue['teams'], blue['score'])
class LeaderboardTrueskill(Leaderboard): def __init__(self, leaderboard_type, file_path): super().__init__(leaderboard_type, file_path) self._trueskill = TrueSkill(mu=TRUESKILL_START_MU, sigma=TRUESKILL_START_SIGMA) self._ratings = {'current': {}, 'history': []} def update_from_placings(self, placings): placings_ids = [agent_id for agent_id, _ in placings] current_ratings = [ self._trueskill.Rating(*self._ratings['current'].get(agent, ())) for agent in placings_ids ] new_rating_groups = self._trueskill.rate([(cr, ) for cr in current_ratings]) hist_append = {} for agent, new_rating_group in zip(placings_ids, new_rating_groups): new_rating = (new_rating_group[0].mu, new_rating_group[0].sigma) self._ratings['current'][agent] = new_rating hist_append[agent] = new_rating self._ratings['history'].append(hist_append) self.changed = True def get_all_rankings(self): sorted_ratings = sorted(self._ratings['current'].items(), key=lambda x: x[1][0], reverse=True) return sorted_ratings def get_rating_of_agent(self, agent_id): # TODO ranking != mu, but keep atm return self._ratings['current'].get( agent_id, (TRUESKILL_START_MU, TRUESKILL_START_SIGMA)) def reset_rankings(self): self._ratings = {'current': {}, 'history': []} self.changed = True def plot_leaderboard(self, agent_manager): plt.figure(0) plt.subplot() mu = np.ndarray((len(self._ratings['history']), )) sigma_2 = np.ndarray((len(self._ratings['history']), )) x = np.arange(len(self._ratings['history'])) agent_types = { agent_type: idx for idx, agent_type in enumerate(AGENT_TYPES.keys()) } #palette = sns.color_palette("husl", n_colors=len(agent_types)) palette = [(60, 180, 75), (255, 225, 25), (0, 130, 200), (245, 130, 48), (220, 190, 255), (128, 0, 0), (0, 0, 128), (128, 128, 128), (0, 0, 0), (64, 64, 64)] palette = [(r / 255, g / 255, b / 255) for r, g, b in palette] for agent_id in self._ratings['current'].keys(): agent_info = agent_manager.get_info(agent_id) color = palette[agent_types[agent_info.AGENT_TYPE]] for idx, game in enumerate(self._ratings['history']): if agent_id in game: mu[idx] = game[agent_id][0] sigma_2[idx] = game[agent_id][1] * 2 else: mu[idx] = np.NaN sigma_2[idx] = np.NaN mask = np.isfinite(mu) sns.lineplot(x=x[mask], y=mu[mask], style=agent_info.TRAINABLE, dashes={ False: [], True: [2, 2] }, color=color) # plt.errorbar(x=x[mask], # y=mu[mask], # yerr=sigma_2[mask], # linestyle='-') # plt.ylim(87, 125) fake_lines = [plt.Line2D([0], [0], color=color) for color in palette] #plt.title('[{}]: {}'.format(self.LEADERBOARD_TYPE, self.FILE_PATH)) plt.xlabel('rounds played') plt.ylabel('trueskill rating') plt.legend(fake_lines, agent_types) plt.show() def print_leaderboard(self, agent_manager): ratings = [(v[0], v[1], k, agent_manager.get_info(k)) for k, v in self._ratings['current'].items()] output = "\n".join( '{:>4}: {} {}.{} {:<20} {:>7.2f} +/- {:>5.2f}'.format( idx + 1, agent_info.AGENT_TYPE, agent_info.ORIGIN_DIVI, agent_id, agent_info.AGENT_NAME, mu, 2 * sigma) for idx, (mu, sigma, agent_id, agent_info) in enumerate(sorted(ratings, reverse=True))) print(output)