Module rating.rating.eloplusplus
Classes
class EloPlusPlus (gamma=0.2, lambda_=0.77, iterations=50, factor=0.602, default_change=np.float64(460.51701859880916), lr_change_coef=0.1, linearized: int = None)
-
EloPlusPlus algorithm based on https://arxiv.org/abs/1012.4571, the winner of the Kaggle rating competition.
Attributes
- gamma (float): The extra value associated to playing home for the EloPlusPlus algorithm.
- lambda_ (float): The regularization parameter for the EloPlusPlus algorithm.
- iterations (int): The number of iterations to run the SGD for.
- factor (float): The factor that is used in updating the learning rate.
- default_change (float): The default standard unit of change in ratings.
- lr_change_coef (float): The learning rate change coefficient used in updated the learning rate.
- linearized (int): The number of periods to linearize the ratings for. If None, the ratings are not linearized.
Args
- gamma (float): The extra value associated to playing home for the EloPlusPlus algorithm. Default is 0.2.
- lambda_ (float): The regularization parameter for the EloPlusPlus algorithm. Default is 0.77.
- iterations (int): The number of iterations to run the SGD for. Default is 50.
- factor (float): The factor that is used in updating the learning rate. Default is 0.602.
- default_change (float): The default standard unit of change in ratings. Default is 200 * np.log(10).
- lr_change_coef (float): The learning rate change coefficient used in updated the learning rate. Default is 0.1.
- linearized (int): The number of periods to linearize the ratings for. If None, the ratings are not linearized. Default is None.
Expand source code
class EloPlusPlus(RatingSystem): def __init__(self, gamma=0.2, lambda_=0.77, iterations=50, factor=0.602, default_change=200 * np.log(10), lr_change_coef=0.1, linearized : int = None): """ EloPlusPlus algorithm based on https://arxiv.org/abs/1012.4571, the winner of the Kaggle rating competition. Attributes: - gamma (float): The extra value associated to playing home for the EloPlusPlus algorithm. - lambda_ (float): The regularization parameter for the EloPlusPlus algorithm. - iterations (int): The number of iterations to run the SGD for. - factor (float): The factor that is used in updating the learning rate. - default_change (float): The default standard unit of change in ratings. - lr_change_coef (float): The learning rate change coefficient used in updated the learning rate. - linearized (int): The number of periods to linearize the ratings for. If None, the ratings are not linearized. Args: - gamma (float): The extra value associated to playing home for the EloPlusPlus algorithm. Default is 0.2. - lambda_ (float): The regularization parameter for the EloPlusPlus algorithm. Default is 0.77. - iterations (int): The number of iterations to run the SGD for. Default is 50. - factor (float): The factor that is used in updating the learning rate. Default is 0.602. - default_change (float): The default standard unit of change in ratings. Default is 200 * np.log(10). - lr_change_coef (float): The learning rate change coefficient used in updated the learning rate. Default is 0.1. - linearized (int): The number of periods to linearize the ratings for. If None, the ratings are not linearized. Default is None. """ super().__init__(gamma=gamma, lambda_=lambda_, iterations=iterations, factor=factor, default_change=default_change, lr_change_coef=lr_change_coef, linearized=linearized) def compute_weights(self, games : List[Game], period_dates : List[datetime]): """ Computes the weights of the games. Args: - games (list): A list of game objects. - period_dates (list): A list of dates for each period. Returns: - list: A list of weights for the games. """ periods = [np.argmax([date >= game.get_date() for date in period_dates]) for game in games] min_period = min(periods) max_period = max(periods) weights = [] for game, period in zip(games, periods): weight = (1 + period - min_period) / (1 + max_period - min_period) weights.append(weight ** 2) return weights def period_update(self, player_database : PlayerDatabase, game_database : GameDatabase, period_dates : List[datetime], **kwargs): """ Updates the ratings of the players for a specific period. Args: - player_database (PlayerDatabase): The player database object. - game_database (GameDatabase): The game database object. - period_dates (list): The list of dates for each period. """ period_dates_last = None if len(period_dates) > 1 and self.linearized is not None: period_dates_last = period_dates[max(0, len(period_dates) - self.linearized - 1)] games = list(game_database.get_games_between_dates(period_dates[-1], period_dates_last)) weights = self.compute_weights(games, period_dates) ratings = [] for id in range(player_database.get_max_id() + 1): player = player_database[id] if player is None: ratings.append(0) else: ratings.append(player.get_rating().rating) for iteration in range(self.iterations): player_averages = [(0, 0, 0) for _ in range(player_database.get_max_id() + 1)] # shuffle games indices = np.random.permutation(len(games)) games = [games[i] for i in indices] weights = [weights[i] for i in indices] for game, weight in zip(games, weights): player_averages[game.home] = (player_averages[game.home][0] + game.weight * weight * ratings[game.out], player_averages[game.home][1] + game.weight * weight, player_averages[game.home][2] + game.weight) player_averages[game.out] = (player_averages[game.out][0] + game.weight * weight * ratings[game.home], player_averages[game.out][1] + game.weight * weight, player_averages[game.out][2] + game.weight) for i in range(len(player_averages)): if player_averages[i][2] > 0: player_averages[i] = (player_averages[i][0] / player_averages[i][1], player_averages[i][2]) else: player_averages[i] = (0, 0) lr = (1 + self.lr_change_coef * self.iterations) / (1 + iteration + self.lr_change_coef * self.iterations) lr = lr ** self.factor for game, weight in zip(games, weights): expected_outcome = self.compute_expected_score_float_rating(ratings[game.home], ratings[game.out]) ratings[game.home] -= lr * weight * (expected_outcome - game.get_result()) * expected_outcome * (1 - expected_outcome) * self.default_change ratings[game.out] += lr * weight * (expected_outcome - game.get_result()) * expected_outcome * (1 - expected_outcome) * self.default_change ratings[game.home] -= lr * self.lambda_ / player_averages[game.home][1] * (ratings[game.home] - player_averages[game.home][0]) ratings[game.out] -= lr * self.lambda_ / player_averages[game.out][1] * (ratings[game.out] - player_averages[game.out][0]) for player in player_database: player.get_rating().update(ratings[player.id]) def compute_expected_score_float_rating(self, rating1 : float, rating2 : float, rating_1_is_home : bool = True) -> float: """ Computes the expected score of a player against an opponent. Args: - rating1 (float): The rating of the first player. - rating2 (float): The rating of the second player. - rating_1_is_home (bool, optional): If True, the first player is home. If False, the second player is home. Defaults to True. Returns: - float: The expected score of the first player against the second player. """ if rating_1_is_home: return 1 / (1 + np.exp((rating2 - rating1) / self.default_change - self.gamma)) else: return 1 / (1 + np.exp((rating2 - rating1) / self.default_change + self.gamma)) def compute_expected_score_rating(self, rating1 : Rating, rating2 : Rating, rating_1_is_home : bool = True) -> float: """ Computes the expected score of a player against an opponent. Args: - rating1 (Rating): The rating of the first player. - rating2 (Rating): The rating of the second player. - rating_1_is_home (bool, optional): If True, the first player is home. If False, the second player is home. Defaults to True. Returns: - float: The expected score of the first player against the second player. """ return self.compute_expected_score_float_rating(rating1.rating, rating2.rating, rating_1_is_home) def compute_expected_score(self, player : Player, games : List[Game], player_database : PlayerDatabase, date : datetime, next : bool = False) -> float: opponents = [game.home if game.home != player.id else game.out for game in games] opponents = [player_database[opponent] for opponent in opponents] is_home = [game.home == player.id for game in games] ratings = [opponent.get_rating_at_date(date, next=next) for opponent in opponents] player_rating = player.get_rating_at_date(date, next=next) expected_scores = [] for i, rating in enumerate(ratings): expected_scores.append(games[i].weight * self.compute_expected_score_rating(player_rating, rating, is_home[i])) return np.sum(expected_scores)
Ancestors
Methods
def compute_expected_score_float_rating(self, rating1: float, rating2: float, rating_1_is_home: bool = True) ‑> float
-
Computes the expected score of a player against an opponent.
Args
- rating1 (float): The rating of the first player.
- rating2 (float): The rating of the second player.
- rating_1_is_home (bool, optional): If True, the first player is home. If False, the second player is home. Defaults to True.
Returns
- float: The expected score of the first player against the second player.
def compute_expected_score_rating(self, rating1: Rating, rating2: Rating, rating_1_is_home: bool = True) ‑> float
-
Computes the expected score of a player against an opponent.
Args
- rating1 (Rating): The rating of the first player.
- rating2 (Rating): The rating of the second player.
- rating_1_is_home (bool, optional): If True, the first player is home. If False, the second player is home. Defaults to True.
Returns
- float: The expected score of the first player against the second player.
def compute_weights(self, games: List[Game], period_dates: List[datetime.datetime])
-
Computes the weights of the games.
Args
- games (list): A list of game objects.
- period_dates (list): A list of dates for each period.
Returns
- list: A list of weights for the games.
Inherited members