riix.models.glicko
Glicko
1"""Glicko""" 2import math 3import numpy as np 4from riix.core.base import OnlineRatingSystem 5from riix.utils.math_utils import sigmoid, sigmoid_scalar 6from riix.utils.constants import PI2, Q, Q2, Q2_3 7 8 9class Glicko(OnlineRatingSystem): 10 """ 11 Implements the original Glicko rating system, designed by Mark Glickman. 12 13 This rating system is an improvement over the Elo rating system, introducing the concept of rating 14 deviation and volatility to better account for the uncertainty in a player's true strength. 15 """ 16 17 rating_dim = 2 18 19 def __init__( 20 self, 21 competitors: list, 22 initial_rating: float = 1500.0, 23 initial_rating_dev: float = 350.0, 24 c: float = 63.2, 25 dtype=np.float64, 26 update_method='online', 27 # update_method='batched', 28 do_weird_prob=False, 29 ): 30 """ 31 Initializes the Glicko rating system with the given parameters. 32 33 Parameters: 34 competitors (list): A list of competitors to be rated within the system. 35 initial_rating (float, optional): The initial Glicko rating for new competitors. Defaults to 1500.0. 36 initial_rating_dev (float, optional): The initial rating deviation for new competitors. Defaults to 350.0. 37 c (float, optional): Constant used to adjust the rate of change of the rating deviation. Defaults to 63.2. 38 dtype (data-type, optional): The desired data-type for the ratings and deviations arrays. Defaults to np.float64. 39 update_method (str, optional): Method used for updating ratings ('online' or another specified method). Defaults to 'online'. 40 do_weird_prob (bool, optional): If set to True, applies an alternative probability calculation. Defaults to False. 41 """ 42 super().__init__(competitors) 43 self.initial_rating_dev = initial_rating_dev 44 self.c2 = c**2.0 45 self.ratings = np.zeros(shape=self.num_competitors, dtype=dtype) + initial_rating 46 self.rating_devs = np.zeros(shape=self.num_competitors, dtype=dtype) + initial_rating_dev 47 self.has_played = np.zeros(shape=self.num_competitors, dtype=np.bool_) 48 self.prev_time_step = -1 49 self.do_weird_prob = do_weird_prob 50 51 if update_method == 'batched': 52 self.update = self.batched_update 53 elif update_method == 'online': 54 self.update = self.online_update 55 else: 56 raise ValueError(f'update_method must be one of online or batched, got {update_method}') 57 58 @staticmethod 59 def g_vector(rating_dev): 60 """ 61 Calculates the g function as part of the Glicko rating system. 62 63 This function is used to scale the rating deviation, affecting the impact of a game outcome 64 as a function of the opponent's rating volatility. 65 66 Parameters: 67 rating_dev (float): The rating deviation of an opponent. 68 69 Returns: 70 float: The calculated g function result, used to scale the expected score between players. 71 """ 72 return 1.0 / np.sqrt(1.0 + (Q2_3 * np.square(rating_dev)) / PI2) 73 74 @staticmethod 75 def g_scalar(rating_dev): 76 return 1.0 / math.sqrt(1.0 + (Q2_3 * (rating_dev) ** 2.0) / PI2) 77 78 # TODO should Glicko probs incorporate the dev increase? 79 def predict(self, matchups: np.ndarray, time_step: int = None, set_cache: bool = False): 80 """generate predictions""" 81 ratings_1 = self.ratings[matchups[:, 0]] 82 ratings_2 = self.ratings[matchups[:, 1]] 83 rating_diffs = ratings_1 - ratings_2 84 if self.do_weird_prob: 85 # not sure why they do it this way but it seems to work better than the "real" way 86 # https://github.com/McLeopold/PythonSkills/blob/95559262fbeaabc39cc5d698b93a6e43dc9b5e64/skills/glicko.py#L181 87 probs = 1.0 / (1.0 + np.power(10, -rating_diffs / (2.0 * self.initial_rating_dev))) 88 else: 89 rating_devs_1 = self.rating_devs[matchups[:, 0]] 90 rating_devs_2 = self.rating_devs[matchups[:, 1]] 91 combined_dev = self.g_vector(np.sqrt(np.square(rating_devs_1) + np.square(rating_devs_2))) 92 probs = sigmoid(Q * combined_dev * rating_diffs) 93 return probs 94 95 def get_pre_match_ratings(self, matchups: np.ndarray, **kwargs): 96 means = self.ratings[matchups] 97 devs = self.rating_devs[matchups] 98 ratings = np.concatenate((means[..., None], devs[..., None]), axis=2).reshape(means.shape[0], -1) 99 return ratings 100 101 def increase_rating_dev(self, time_step, matchups): 102 """called once per period to model the increase in variance over time""" 103 active_in_period = np.unique(matchups) 104 self.has_played[active_in_period] = True 105 time_delta = time_step - self.prev_time_step 106 self.rating_devs[self.has_played] = np.minimum( 107 np.sqrt(np.square(self.rating_devs[self.has_played]) + (time_delta * self.c2)), self.initial_rating_dev 108 ) 109 self.prev_time_step = time_step 110 return active_in_period 111 112 def batched_update(self, matchups, outcomes, time_step, use_cache=False, **kwargs): 113 """apply one update based on all of the results of the rating period""" 114 active_in_period = self.increase_rating_dev(time_step, matchups) 115 masks = np.equal(matchups[:, :, None], active_in_period[None, :]) # N x 2 x active 116 117 ratings = self.ratings[matchups] 118 rating_diffs = ratings[:, 0] - ratings[:, 1] 119 g_rating_devs = self.g_vector(self.rating_devs[matchups]) 120 probs_1 = sigmoid(Q * g_rating_devs[:, 1] * rating_diffs) 121 probs_2 = sigmoid(-1.0 * (Q * g_rating_devs[:, 0] * rating_diffs)) 122 123 tmp = np.stack([probs_1 * (1.0 - probs_1), probs_2 * (1.0 - probs_2)]).T * np.square(g_rating_devs)[:, [1, 0]] 124 d2 = 1.0 / ((tmp[:, :, None] * masks).sum(axis=(0, 1)) * Q2) 125 126 outcomes = np.hstack([outcomes[:, None], 1.0 - outcomes[:, None]]) 127 probs = np.hstack([probs_1[:, None], probs_2[:, None]]) 128 129 r_num = Q * ((g_rating_devs[:, [1, 0]] * (outcomes - probs))[:, :, None] * masks).sum(axis=(0, 1)) 130 r_denom = (1.0 / np.square(self.rating_devs[active_in_period])) + (1.0 / d2) 131 132 self.ratings[active_in_period] += r_num / r_denom 133 self.rating_devs[active_in_period] = np.sqrt(1.0 / r_denom) 134 135 def online_update(self, matchups, outcomes, time_step, **kwargs): 136 """treat the matchups in the rating period as if they were sequential""" 137 self.increase_rating_dev(time_step, matchups) 138 for idx in range(matchups.shape[0]): 139 comp_1, comp_2 = matchups[idx] 140 rating_diff = self.ratings[comp_1] - self.ratings[comp_2] 141 g_rating_devs = self.g_vector(self.rating_devs[matchups[idx]]) 142 g_rating_devs_2 = np.square(g_rating_devs) 143 prob_1 = sigmoid_scalar(Q * g_rating_devs[1] * rating_diff) 144 prob_2 = sigmoid_scalar(-Q * g_rating_devs[0] * rating_diff) 145 d2_1 = 1.0 / (Q2 * prob_1 * (1.0 - prob_1) * g_rating_devs_2[1]) 146 d2_2 = 1.0 / (Q2 * prob_2 * (1.0 - prob_2) * g_rating_devs_2[0]) 147 r1_num = Q * g_rating_devs[1] * (outcomes[idx] - prob_1) 148 r2_num = Q * g_rating_devs[0] * (1.0 - outcomes[idx] - prob_2) 149 r1_denom = (1.0 / (self.rating_devs[comp_1] ** 2.0)) + (1.0 / d2_1) 150 r2_denom = (1.0 / (self.rating_devs[comp_2] ** 2.0)) + (1.0 / d2_2) 151 152 self.ratings[comp_1] += r1_num / r1_denom 153 self.ratings[comp_2] += r2_num / r2_denom 154 self.rating_devs[comp_1] = 1.0 / math.sqrt(r1_denom) 155 self.rating_devs[comp_2] = 1.0 / math.sqrt(r2_denom) 156 157 def print_leaderboard(self, num_places): 158 sort_array = self.ratings - (3.0 * self.rating_devs) 159 sorted_idxs = np.argsort(-sort_array)[:num_places] 160 max_len = min(np.max([len(comp) for comp in self.competitors] + [10]), 25) 161 print(f'{"competitor": <{max_len}}\t{"rating - (3*dev)"}\t') 162 for p_idx in range(num_places): 163 comp_idx = sorted_idxs[p_idx] 164 print(f'{self.competitors[comp_idx]: <{max_len}}\t{sort_array[comp_idx]:.6f}')
10class Glicko(OnlineRatingSystem): 11 """ 12 Implements the original Glicko rating system, designed by Mark Glickman. 13 14 This rating system is an improvement over the Elo rating system, introducing the concept of rating 15 deviation and volatility to better account for the uncertainty in a player's true strength. 16 """ 17 18 rating_dim = 2 19 20 def __init__( 21 self, 22 competitors: list, 23 initial_rating: float = 1500.0, 24 initial_rating_dev: float = 350.0, 25 c: float = 63.2, 26 dtype=np.float64, 27 update_method='online', 28 # update_method='batched', 29 do_weird_prob=False, 30 ): 31 """ 32 Initializes the Glicko rating system with the given parameters. 33 34 Parameters: 35 competitors (list): A list of competitors to be rated within the system. 36 initial_rating (float, optional): The initial Glicko rating for new competitors. Defaults to 1500.0. 37 initial_rating_dev (float, optional): The initial rating deviation for new competitors. Defaults to 350.0. 38 c (float, optional): Constant used to adjust the rate of change of the rating deviation. Defaults to 63.2. 39 dtype (data-type, optional): The desired data-type for the ratings and deviations arrays. Defaults to np.float64. 40 update_method (str, optional): Method used for updating ratings ('online' or another specified method). Defaults to 'online'. 41 do_weird_prob (bool, optional): If set to True, applies an alternative probability calculation. Defaults to False. 42 """ 43 super().__init__(competitors) 44 self.initial_rating_dev = initial_rating_dev 45 self.c2 = c**2.0 46 self.ratings = np.zeros(shape=self.num_competitors, dtype=dtype) + initial_rating 47 self.rating_devs = np.zeros(shape=self.num_competitors, dtype=dtype) + initial_rating_dev 48 self.has_played = np.zeros(shape=self.num_competitors, dtype=np.bool_) 49 self.prev_time_step = -1 50 self.do_weird_prob = do_weird_prob 51 52 if update_method == 'batched': 53 self.update = self.batched_update 54 elif update_method == 'online': 55 self.update = self.online_update 56 else: 57 raise ValueError(f'update_method must be one of online or batched, got {update_method}') 58 59 @staticmethod 60 def g_vector(rating_dev): 61 """ 62 Calculates the g function as part of the Glicko rating system. 63 64 This function is used to scale the rating deviation, affecting the impact of a game outcome 65 as a function of the opponent's rating volatility. 66 67 Parameters: 68 rating_dev (float): The rating deviation of an opponent. 69 70 Returns: 71 float: The calculated g function result, used to scale the expected score between players. 72 """ 73 return 1.0 / np.sqrt(1.0 + (Q2_3 * np.square(rating_dev)) / PI2) 74 75 @staticmethod 76 def g_scalar(rating_dev): 77 return 1.0 / math.sqrt(1.0 + (Q2_3 * (rating_dev) ** 2.0) / PI2) 78 79 # TODO should Glicko probs incorporate the dev increase? 80 def predict(self, matchups: np.ndarray, time_step: int = None, set_cache: bool = False): 81 """generate predictions""" 82 ratings_1 = self.ratings[matchups[:, 0]] 83 ratings_2 = self.ratings[matchups[:, 1]] 84 rating_diffs = ratings_1 - ratings_2 85 if self.do_weird_prob: 86 # not sure why they do it this way but it seems to work better than the "real" way 87 # https://github.com/McLeopold/PythonSkills/blob/95559262fbeaabc39cc5d698b93a6e43dc9b5e64/skills/glicko.py#L181 88 probs = 1.0 / (1.0 + np.power(10, -rating_diffs / (2.0 * self.initial_rating_dev))) 89 else: 90 rating_devs_1 = self.rating_devs[matchups[:, 0]] 91 rating_devs_2 = self.rating_devs[matchups[:, 1]] 92 combined_dev = self.g_vector(np.sqrt(np.square(rating_devs_1) + np.square(rating_devs_2))) 93 probs = sigmoid(Q * combined_dev * rating_diffs) 94 return probs 95 96 def get_pre_match_ratings(self, matchups: np.ndarray, **kwargs): 97 means = self.ratings[matchups] 98 devs = self.rating_devs[matchups] 99 ratings = np.concatenate((means[..., None], devs[..., None]), axis=2).reshape(means.shape[0], -1) 100 return ratings 101 102 def increase_rating_dev(self, time_step, matchups): 103 """called once per period to model the increase in variance over time""" 104 active_in_period = np.unique(matchups) 105 self.has_played[active_in_period] = True 106 time_delta = time_step - self.prev_time_step 107 self.rating_devs[self.has_played] = np.minimum( 108 np.sqrt(np.square(self.rating_devs[self.has_played]) + (time_delta * self.c2)), self.initial_rating_dev 109 ) 110 self.prev_time_step = time_step 111 return active_in_period 112 113 def batched_update(self, matchups, outcomes, time_step, use_cache=False, **kwargs): 114 """apply one update based on all of the results of the rating period""" 115 active_in_period = self.increase_rating_dev(time_step, matchups) 116 masks = np.equal(matchups[:, :, None], active_in_period[None, :]) # N x 2 x active 117 118 ratings = self.ratings[matchups] 119 rating_diffs = ratings[:, 0] - ratings[:, 1] 120 g_rating_devs = self.g_vector(self.rating_devs[matchups]) 121 probs_1 = sigmoid(Q * g_rating_devs[:, 1] * rating_diffs) 122 probs_2 = sigmoid(-1.0 * (Q * g_rating_devs[:, 0] * rating_diffs)) 123 124 tmp = np.stack([probs_1 * (1.0 - probs_1), probs_2 * (1.0 - probs_2)]).T * np.square(g_rating_devs)[:, [1, 0]] 125 d2 = 1.0 / ((tmp[:, :, None] * masks).sum(axis=(0, 1)) * Q2) 126 127 outcomes = np.hstack([outcomes[:, None], 1.0 - outcomes[:, None]]) 128 probs = np.hstack([probs_1[:, None], probs_2[:, None]]) 129 130 r_num = Q * ((g_rating_devs[:, [1, 0]] * (outcomes - probs))[:, :, None] * masks).sum(axis=(0, 1)) 131 r_denom = (1.0 / np.square(self.rating_devs[active_in_period])) + (1.0 / d2) 132 133 self.ratings[active_in_period] += r_num / r_denom 134 self.rating_devs[active_in_period] = np.sqrt(1.0 / r_denom) 135 136 def online_update(self, matchups, outcomes, time_step, **kwargs): 137 """treat the matchups in the rating period as if they were sequential""" 138 self.increase_rating_dev(time_step, matchups) 139 for idx in range(matchups.shape[0]): 140 comp_1, comp_2 = matchups[idx] 141 rating_diff = self.ratings[comp_1] - self.ratings[comp_2] 142 g_rating_devs = self.g_vector(self.rating_devs[matchups[idx]]) 143 g_rating_devs_2 = np.square(g_rating_devs) 144 prob_1 = sigmoid_scalar(Q * g_rating_devs[1] * rating_diff) 145 prob_2 = sigmoid_scalar(-Q * g_rating_devs[0] * rating_diff) 146 d2_1 = 1.0 / (Q2 * prob_1 * (1.0 - prob_1) * g_rating_devs_2[1]) 147 d2_2 = 1.0 / (Q2 * prob_2 * (1.0 - prob_2) * g_rating_devs_2[0]) 148 r1_num = Q * g_rating_devs[1] * (outcomes[idx] - prob_1) 149 r2_num = Q * g_rating_devs[0] * (1.0 - outcomes[idx] - prob_2) 150 r1_denom = (1.0 / (self.rating_devs[comp_1] ** 2.0)) + (1.0 / d2_1) 151 r2_denom = (1.0 / (self.rating_devs[comp_2] ** 2.0)) + (1.0 / d2_2) 152 153 self.ratings[comp_1] += r1_num / r1_denom 154 self.ratings[comp_2] += r2_num / r2_denom 155 self.rating_devs[comp_1] = 1.0 / math.sqrt(r1_denom) 156 self.rating_devs[comp_2] = 1.0 / math.sqrt(r2_denom) 157 158 def print_leaderboard(self, num_places): 159 sort_array = self.ratings - (3.0 * self.rating_devs) 160 sorted_idxs = np.argsort(-sort_array)[:num_places] 161 max_len = min(np.max([len(comp) for comp in self.competitors] + [10]), 25) 162 print(f'{"competitor": <{max_len}}\t{"rating - (3*dev)"}\t') 163 for p_idx in range(num_places): 164 comp_idx = sorted_idxs[p_idx] 165 print(f'{self.competitors[comp_idx]: <{max_len}}\t{sort_array[comp_idx]:.6f}')
Implements the original Glicko rating system, designed by Mark Glickman.
This rating system is an improvement over the Elo rating system, introducing the concept of rating deviation and volatility to better account for the uncertainty in a player's true strength.
20 def __init__( 21 self, 22 competitors: list, 23 initial_rating: float = 1500.0, 24 initial_rating_dev: float = 350.0, 25 c: float = 63.2, 26 dtype=np.float64, 27 update_method='online', 28 # update_method='batched', 29 do_weird_prob=False, 30 ): 31 """ 32 Initializes the Glicko rating system with the given parameters. 33 34 Parameters: 35 competitors (list): A list of competitors to be rated within the system. 36 initial_rating (float, optional): The initial Glicko rating for new competitors. Defaults to 1500.0. 37 initial_rating_dev (float, optional): The initial rating deviation for new competitors. Defaults to 350.0. 38 c (float, optional): Constant used to adjust the rate of change of the rating deviation. Defaults to 63.2. 39 dtype (data-type, optional): The desired data-type for the ratings and deviations arrays. Defaults to np.float64. 40 update_method (str, optional): Method used for updating ratings ('online' or another specified method). Defaults to 'online'. 41 do_weird_prob (bool, optional): If set to True, applies an alternative probability calculation. Defaults to False. 42 """ 43 super().__init__(competitors) 44 self.initial_rating_dev = initial_rating_dev 45 self.c2 = c**2.0 46 self.ratings = np.zeros(shape=self.num_competitors, dtype=dtype) + initial_rating 47 self.rating_devs = np.zeros(shape=self.num_competitors, dtype=dtype) + initial_rating_dev 48 self.has_played = np.zeros(shape=self.num_competitors, dtype=np.bool_) 49 self.prev_time_step = -1 50 self.do_weird_prob = do_weird_prob 51 52 if update_method == 'batched': 53 self.update = self.batched_update 54 elif update_method == 'online': 55 self.update = self.online_update 56 else: 57 raise ValueError(f'update_method must be one of online or batched, got {update_method}')
Initializes the Glicko rating system with the given parameters.
Arguments:
- competitors (list): A list of competitors to be rated within the system.
- initial_rating (float, optional): The initial Glicko rating for new competitors. Defaults to 1500.0.
- initial_rating_dev (float, optional): The initial rating deviation for new competitors. Defaults to 350.0.
- c (float, optional): Constant used to adjust the rate of change of the rating deviation. Defaults to 63.2.
- dtype (data-type, optional): The desired data-type for the ratings and deviations arrays. Defaults to np.float64.
- update_method (str, optional): Method used for updating ratings ('online' or another specified method). Defaults to 'online'.
- do_weird_prob (bool, optional): If set to True, applies an alternative probability calculation. Defaults to False.
59 @staticmethod 60 def g_vector(rating_dev): 61 """ 62 Calculates the g function as part of the Glicko rating system. 63 64 This function is used to scale the rating deviation, affecting the impact of a game outcome 65 as a function of the opponent's rating volatility. 66 67 Parameters: 68 rating_dev (float): The rating deviation of an opponent. 69 70 Returns: 71 float: The calculated g function result, used to scale the expected score between players. 72 """ 73 return 1.0 / np.sqrt(1.0 + (Q2_3 * np.square(rating_dev)) / PI2)
Calculates the g function as part of the Glicko rating system.
This function is used to scale the rating deviation, affecting the impact of a game outcome as a function of the opponent's rating volatility.
Arguments:
- rating_dev (float): The rating deviation of an opponent.
Returns:
float: The calculated g function result, used to scale the expected score between players.
80 def predict(self, matchups: np.ndarray, time_step: int = None, set_cache: bool = False): 81 """generate predictions""" 82 ratings_1 = self.ratings[matchups[:, 0]] 83 ratings_2 = self.ratings[matchups[:, 1]] 84 rating_diffs = ratings_1 - ratings_2 85 if self.do_weird_prob: 86 # not sure why they do it this way but it seems to work better than the "real" way 87 # https://github.com/McLeopold/PythonSkills/blob/95559262fbeaabc39cc5d698b93a6e43dc9b5e64/skills/glicko.py#L181 88 probs = 1.0 / (1.0 + np.power(10, -rating_diffs / (2.0 * self.initial_rating_dev))) 89 else: 90 rating_devs_1 = self.rating_devs[matchups[:, 0]] 91 rating_devs_2 = self.rating_devs[matchups[:, 1]] 92 combined_dev = self.g_vector(np.sqrt(np.square(rating_devs_1) + np.square(rating_devs_2))) 93 probs = sigmoid(Q * combined_dev * rating_diffs) 94 return probs
generate predictions
96 def get_pre_match_ratings(self, matchups: np.ndarray, **kwargs): 97 means = self.ratings[matchups] 98 devs = self.rating_devs[matchups] 99 ratings = np.concatenate((means[..., None], devs[..., None]), axis=2).reshape(means.shape[0], -1) 100 return ratings
Returns the ratings for competitors at the timestep of the matchups Useful when using pre-match ratings as features in downstream ML pipelines
Arguments:
- matchups (np.ndarray of shape (n,2)): competitor indices
- time_step (optional int)
Returns:
np.ndarray of shape (n,2): ratings for specified competitors
102 def increase_rating_dev(self, time_step, matchups): 103 """called once per period to model the increase in variance over time""" 104 active_in_period = np.unique(matchups) 105 self.has_played[active_in_period] = True 106 time_delta = time_step - self.prev_time_step 107 self.rating_devs[self.has_played] = np.minimum( 108 np.sqrt(np.square(self.rating_devs[self.has_played]) + (time_delta * self.c2)), self.initial_rating_dev 109 ) 110 self.prev_time_step = time_step 111 return active_in_period
called once per period to model the increase in variance over time
113 def batched_update(self, matchups, outcomes, time_step, use_cache=False, **kwargs): 114 """apply one update based on all of the results of the rating period""" 115 active_in_period = self.increase_rating_dev(time_step, matchups) 116 masks = np.equal(matchups[:, :, None], active_in_period[None, :]) # N x 2 x active 117 118 ratings = self.ratings[matchups] 119 rating_diffs = ratings[:, 0] - ratings[:, 1] 120 g_rating_devs = self.g_vector(self.rating_devs[matchups]) 121 probs_1 = sigmoid(Q * g_rating_devs[:, 1] * rating_diffs) 122 probs_2 = sigmoid(-1.0 * (Q * g_rating_devs[:, 0] * rating_diffs)) 123 124 tmp = np.stack([probs_1 * (1.0 - probs_1), probs_2 * (1.0 - probs_2)]).T * np.square(g_rating_devs)[:, [1, 0]] 125 d2 = 1.0 / ((tmp[:, :, None] * masks).sum(axis=(0, 1)) * Q2) 126 127 outcomes = np.hstack([outcomes[:, None], 1.0 - outcomes[:, None]]) 128 probs = np.hstack([probs_1[:, None], probs_2[:, None]]) 129 130 r_num = Q * ((g_rating_devs[:, [1, 0]] * (outcomes - probs))[:, :, None] * masks).sum(axis=(0, 1)) 131 r_denom = (1.0 / np.square(self.rating_devs[active_in_period])) + (1.0 / d2) 132 133 self.ratings[active_in_period] += r_num / r_denom 134 self.rating_devs[active_in_period] = np.sqrt(1.0 / r_denom)
apply one update based on all of the results of the rating period
136 def online_update(self, matchups, outcomes, time_step, **kwargs): 137 """treat the matchups in the rating period as if they were sequential""" 138 self.increase_rating_dev(time_step, matchups) 139 for idx in range(matchups.shape[0]): 140 comp_1, comp_2 = matchups[idx] 141 rating_diff = self.ratings[comp_1] - self.ratings[comp_2] 142 g_rating_devs = self.g_vector(self.rating_devs[matchups[idx]]) 143 g_rating_devs_2 = np.square(g_rating_devs) 144 prob_1 = sigmoid_scalar(Q * g_rating_devs[1] * rating_diff) 145 prob_2 = sigmoid_scalar(-Q * g_rating_devs[0] * rating_diff) 146 d2_1 = 1.0 / (Q2 * prob_1 * (1.0 - prob_1) * g_rating_devs_2[1]) 147 d2_2 = 1.0 / (Q2 * prob_2 * (1.0 - prob_2) * g_rating_devs_2[0]) 148 r1_num = Q * g_rating_devs[1] * (outcomes[idx] - prob_1) 149 r2_num = Q * g_rating_devs[0] * (1.0 - outcomes[idx] - prob_2) 150 r1_denom = (1.0 / (self.rating_devs[comp_1] ** 2.0)) + (1.0 / d2_1) 151 r2_denom = (1.0 / (self.rating_devs[comp_2] ** 2.0)) + (1.0 / d2_2) 152 153 self.ratings[comp_1] += r1_num / r1_denom 154 self.ratings[comp_2] += r2_num / r2_denom 155 self.rating_devs[comp_1] = 1.0 / math.sqrt(r1_denom) 156 self.rating_devs[comp_2] = 1.0 / math.sqrt(r2_denom)
treat the matchups in the rating period as if they were sequential
158 def print_leaderboard(self, num_places): 159 sort_array = self.ratings - (3.0 * self.rating_devs) 160 sorted_idxs = np.argsort(-sort_array)[:num_places] 161 max_len = min(np.max([len(comp) for comp in self.competitors] + [10]), 25) 162 print(f'{"competitor": <{max_len}}\t{"rating - (3*dev)"}\t') 163 for p_idx in range(num_places): 164 comp_idx = sorted_idxs[p_idx] 165 print(f'{self.competitors[comp_idx]: <{max_len}}\t{sort_array[comp_idx]:.6f}')
Prints the leaderboard of the rating system.
Arguments:
- num_places int: The number of top places to display on the leaderboard.