riix.models.elo_davidson

The Elo-Davidson rating system

  1"""The Elo-Davidson rating system"""
  2import numpy as np
  3import math
  4from riix.core.base import OnlineRatingSystem
  5from riix.utils.math_utils import sigmoid
  6
  7
  8class EloDavidson(OnlineRatingSystem):
  9    """
 10    Implements the Elo-Davidson rating system from https://www.researchgate.net/publication/341384358_Understanding_Draws_in_Elo_Rating_Algorithm
 11    This method applies the method of handling draws proposed by Davidson to the "online" Elo rating system
 12    Davidson's paper: https://www.jstor.org/stable/2283595
 13    """
 14
 15    rating_dim = 1
 16
 17    def __init__(
 18        self,
 19        competitors: list,
 20        initial_rating: float = 0.0,  # this does nothing
 21        k: float = 32.0,
 22        kappa: float = 1.0,  # kappa = 2, sigma = 200 is equivalent to Elo with scale = 400.0
 23        base: float = 10.0,
 24        sigma: float = 200.0,
 25        update_method: str = 'online',
 26        dtype=np.float64,
 27    ):
 28        """
 29        Initializes the Elo rating system with the given parameters.
 30
 31        Parameters:
 32            competitors (list): A list of competitors to be rated within the system.
 33            initial_rating (float, optional): The initial Elo rating for new competitors. Defaults to 1500.0.
 34        """
 35        super().__init__(competitors)
 36        self.k = k
 37        self.kappa = kappa
 38        self.kappa_over_2 = kappa / 2.0
 39        self.alpha = math.log(base) / (2.0 * sigma)
 40        self.ratings = np.zeros(shape=self.num_competitors, dtype=dtype) + initial_rating
 41        if update_method == 'batched':
 42            self.update = self.batched_update
 43        elif update_method == 'online':
 44            self.update = self.online_update
 45        else:
 46            raise ValueError(f'update_method must be one of online or batched, got {update_method}')
 47
 48    def predict(self, matchups: np.ndarray, time_step: int = None, set_cache: bool = False):
 49        """
 50        Generates predictions for a series of matchups between competitors.
 51        Parameters:
 52            matchups (np.ndarray): A NumPy array of matchups, where each row represents a matchup
 53                                    and contains two integers indicating the indices of the competitors
 54                                    in the 'ratings' array.
 55            time_step (int, optional): A time step at which the predictions are made. This parameter
 56                                    is not used in the current implementation but can be utilized
 57                                    for time-dependent predictions. Defaults to None.
 58            set_cache (bool, optional): If True, caches the computed probabilities in the 'cache'
 59                                        attribute under the key 'probs'. Defaults to False.
 60
 61        Returns:
 62            np.ndarray: A NumPy array containing the predicted probabilities for the first competitor
 63                        in each matchup winning against the second.
 64        """
 65        ratings_1 = self.ratings[matchups[:, 0]]
 66        ratings_2 = self.ratings[matchups[:, 1]]
 67        probs = sigmoid(self.alpha * (ratings_1 - ratings_2))
 68        return probs
 69
 70    def get_pre_match_ratings(self, matchups: np.ndarray, **kwargs):
 71        return self.ratings[matchups]
 72
 73    def batched_update(self, matchups, outcomes, use_cache):
 74        """
 75        Apply a single update based on all results of the rating period.
 76
 77        Parameters:
 78            matchups: Matchup information for the rating period.
 79            outcomes: Results of the matchups.
 80            use_cache: Flag to use cached probabilities or calculate anew.
 81        """
 82        raise NotImplementedError
 83
 84    def online_update(self, matchups, outcomes, **kwargs):
 85        """
 86        Treats the matchups in the rating period as sequential events.
 87
 88        Parameters:
 89            matchups: Sequential matchups in the rating period.
 90            outcomes: Results of each matchup.
 91            **kwargs: Additional parameters (not used).
 92        """
 93        for idx in range(matchups.shape[0]):
 94            comp_1, comp_2 = matchups[idx]
 95            r_1 = self.ratings[comp_1]
 96            r_2 = self.ratings[comp_2]
 97
 98            diff = math.exp(self.alpha * (r_1 - r_2))
 99            denom = diff + (1.0 / diff) + self.kappa
100
101            p_1 = (diff + self.kappa_over_2) / denom
102            p_2 = ((1.0 / diff) + self.kappa_over_2) / denom
103
104            update_1 = self.k * (outcomes[idx] - p_1)
105            update_2 = self.k * (1.0 - outcomes[idx] - p_2)
106
107            self.ratings[comp_1] += update_1
108            self.ratings[comp_2] += update_2
109
110    def print_leaderboard(self, num_places):
111        sorted_idxs = np.argsort(-self.ratings)[:num_places]
112        max_len = min(np.max([len(comp) for comp in self.competitors] + [10]), 25)
113        print(f'{"competitor": <{max_len}}\t{"rating"}')
114        for p_idx in range(num_places):
115            comp_idx = sorted_idxs[p_idx]
116            print(f'{self.competitors[comp_idx]: <{max_len}}\t{self.ratings[comp_idx]:.6f}')
class EloDavidson(riix.core.base.OnlineRatingSystem):
  9class EloDavidson(OnlineRatingSystem):
 10    """
 11    Implements the Elo-Davidson rating system from https://www.researchgate.net/publication/341384358_Understanding_Draws_in_Elo_Rating_Algorithm
 12    This method applies the method of handling draws proposed by Davidson to the "online" Elo rating system
 13    Davidson's paper: https://www.jstor.org/stable/2283595
 14    """
 15
 16    rating_dim = 1
 17
 18    def __init__(
 19        self,
 20        competitors: list,
 21        initial_rating: float = 0.0,  # this does nothing
 22        k: float = 32.0,
 23        kappa: float = 1.0,  # kappa = 2, sigma = 200 is equivalent to Elo with scale = 400.0
 24        base: float = 10.0,
 25        sigma: float = 200.0,
 26        update_method: str = 'online',
 27        dtype=np.float64,
 28    ):
 29        """
 30        Initializes the Elo rating system with the given parameters.
 31
 32        Parameters:
 33            competitors (list): A list of competitors to be rated within the system.
 34            initial_rating (float, optional): The initial Elo rating for new competitors. Defaults to 1500.0.
 35        """
 36        super().__init__(competitors)
 37        self.k = k
 38        self.kappa = kappa
 39        self.kappa_over_2 = kappa / 2.0
 40        self.alpha = math.log(base) / (2.0 * sigma)
 41        self.ratings = np.zeros(shape=self.num_competitors, dtype=dtype) + initial_rating
 42        if update_method == 'batched':
 43            self.update = self.batched_update
 44        elif update_method == 'online':
 45            self.update = self.online_update
 46        else:
 47            raise ValueError(f'update_method must be one of online or batched, got {update_method}')
 48
 49    def predict(self, matchups: np.ndarray, time_step: int = None, set_cache: bool = False):
 50        """
 51        Generates predictions for a series of matchups between competitors.
 52        Parameters:
 53            matchups (np.ndarray): A NumPy array of matchups, where each row represents a matchup
 54                                    and contains two integers indicating the indices of the competitors
 55                                    in the 'ratings' array.
 56            time_step (int, optional): A time step at which the predictions are made. This parameter
 57                                    is not used in the current implementation but can be utilized
 58                                    for time-dependent predictions. Defaults to None.
 59            set_cache (bool, optional): If True, caches the computed probabilities in the 'cache'
 60                                        attribute under the key 'probs'. Defaults to False.
 61
 62        Returns:
 63            np.ndarray: A NumPy array containing the predicted probabilities for the first competitor
 64                        in each matchup winning against the second.
 65        """
 66        ratings_1 = self.ratings[matchups[:, 0]]
 67        ratings_2 = self.ratings[matchups[:, 1]]
 68        probs = sigmoid(self.alpha * (ratings_1 - ratings_2))
 69        return probs
 70
 71    def get_pre_match_ratings(self, matchups: np.ndarray, **kwargs):
 72        return self.ratings[matchups]
 73
 74    def batched_update(self, matchups, outcomes, use_cache):
 75        """
 76        Apply a single update based on all results of the rating period.
 77
 78        Parameters:
 79            matchups: Matchup information for the rating period.
 80            outcomes: Results of the matchups.
 81            use_cache: Flag to use cached probabilities or calculate anew.
 82        """
 83        raise NotImplementedError
 84
 85    def online_update(self, matchups, outcomes, **kwargs):
 86        """
 87        Treats the matchups in the rating period as sequential events.
 88
 89        Parameters:
 90            matchups: Sequential matchups in the rating period.
 91            outcomes: Results of each matchup.
 92            **kwargs: Additional parameters (not used).
 93        """
 94        for idx in range(matchups.shape[0]):
 95            comp_1, comp_2 = matchups[idx]
 96            r_1 = self.ratings[comp_1]
 97            r_2 = self.ratings[comp_2]
 98
 99            diff = math.exp(self.alpha * (r_1 - r_2))
100            denom = diff + (1.0 / diff) + self.kappa
101
102            p_1 = (diff + self.kappa_over_2) / denom
103            p_2 = ((1.0 / diff) + self.kappa_over_2) / denom
104
105            update_1 = self.k * (outcomes[idx] - p_1)
106            update_2 = self.k * (1.0 - outcomes[idx] - p_2)
107
108            self.ratings[comp_1] += update_1
109            self.ratings[comp_2] += update_2
110
111    def print_leaderboard(self, num_places):
112        sorted_idxs = np.argsort(-self.ratings)[:num_places]
113        max_len = min(np.max([len(comp) for comp in self.competitors] + [10]), 25)
114        print(f'{"competitor": <{max_len}}\t{"rating"}')
115        for p_idx in range(num_places):
116            comp_idx = sorted_idxs[p_idx]
117            print(f'{self.competitors[comp_idx]: <{max_len}}\t{self.ratings[comp_idx]:.6f}')

Implements the Elo-Davidson rating system from https://www.researchgate.net/publication/341384358_Understanding_Draws_in_Elo_Rating_Algorithm This method applies the method of handling draws proposed by Davidson to the "online" Elo rating system Davidson's paper: https://www.jstor.org/stable/2283595

EloDavidson( competitors: list, initial_rating: float = 0.0, k: float = 32.0, kappa: float = 1.0, base: float = 10.0, sigma: float = 200.0, update_method: str = 'online', dtype=<class 'numpy.float64'>)
18    def __init__(
19        self,
20        competitors: list,
21        initial_rating: float = 0.0,  # this does nothing
22        k: float = 32.0,
23        kappa: float = 1.0,  # kappa = 2, sigma = 200 is equivalent to Elo with scale = 400.0
24        base: float = 10.0,
25        sigma: float = 200.0,
26        update_method: str = 'online',
27        dtype=np.float64,
28    ):
29        """
30        Initializes the Elo rating system with the given parameters.
31
32        Parameters:
33            competitors (list): A list of competitors to be rated within the system.
34            initial_rating (float, optional): The initial Elo rating for new competitors. Defaults to 1500.0.
35        """
36        super().__init__(competitors)
37        self.k = k
38        self.kappa = kappa
39        self.kappa_over_2 = kappa / 2.0
40        self.alpha = math.log(base) / (2.0 * sigma)
41        self.ratings = np.zeros(shape=self.num_competitors, dtype=dtype) + initial_rating
42        if update_method == 'batched':
43            self.update = self.batched_update
44        elif update_method == 'online':
45            self.update = self.online_update
46        else:
47            raise ValueError(f'update_method must be one of online or batched, got {update_method}')

Initializes the Elo 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 Elo rating for new competitors. Defaults to 1500.0.
rating_dim = 1
k
kappa
kappa_over_2
alpha
ratings
def predict( self, matchups: numpy.ndarray, time_step: int = None, set_cache: bool = False):
49    def predict(self, matchups: np.ndarray, time_step: int = None, set_cache: bool = False):
50        """
51        Generates predictions for a series of matchups between competitors.
52        Parameters:
53            matchups (np.ndarray): A NumPy array of matchups, where each row represents a matchup
54                                    and contains two integers indicating the indices of the competitors
55                                    in the 'ratings' array.
56            time_step (int, optional): A time step at which the predictions are made. This parameter
57                                    is not used in the current implementation but can be utilized
58                                    for time-dependent predictions. Defaults to None.
59            set_cache (bool, optional): If True, caches the computed probabilities in the 'cache'
60                                        attribute under the key 'probs'. Defaults to False.
61
62        Returns:
63            np.ndarray: A NumPy array containing the predicted probabilities for the first competitor
64                        in each matchup winning against the second.
65        """
66        ratings_1 = self.ratings[matchups[:, 0]]
67        ratings_2 = self.ratings[matchups[:, 1]]
68        probs = sigmoid(self.alpha * (ratings_1 - ratings_2))
69        return probs

Generates predictions for a series of matchups between competitors.

Arguments:
  • matchups (np.ndarray): A NumPy array of matchups, where each row represents a matchup and contains two integers indicating the indices of the competitors in the 'ratings' array.
  • time_step (int, optional): A time step at which the predictions are made. This parameter is not used in the current implementation but can be utilized for time-dependent predictions. Defaults to None.
  • set_cache (bool, optional): If True, caches the computed probabilities in the 'cache' attribute under the key 'probs'. Defaults to False.
Returns:

np.ndarray: A NumPy array containing the predicted probabilities for the first competitor in each matchup winning against the second.

def get_pre_match_ratings(self, matchups: numpy.ndarray, **kwargs):
71    def get_pre_match_ratings(self, matchups: np.ndarray, **kwargs):
72        return self.ratings[matchups]

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

def batched_update(self, matchups, outcomes, use_cache):
74    def batched_update(self, matchups, outcomes, use_cache):
75        """
76        Apply a single update based on all results of the rating period.
77
78        Parameters:
79            matchups: Matchup information for the rating period.
80            outcomes: Results of the matchups.
81            use_cache: Flag to use cached probabilities or calculate anew.
82        """
83        raise NotImplementedError

Apply a single update based on all results of the rating period.

Arguments:
  • matchups: Matchup information for the rating period.
  • outcomes: Results of the matchups.
  • use_cache: Flag to use cached probabilities or calculate anew.
def online_update(self, matchups, outcomes, **kwargs):
 85    def online_update(self, matchups, outcomes, **kwargs):
 86        """
 87        Treats the matchups in the rating period as sequential events.
 88
 89        Parameters:
 90            matchups: Sequential matchups in the rating period.
 91            outcomes: Results of each matchup.
 92            **kwargs: Additional parameters (not used).
 93        """
 94        for idx in range(matchups.shape[0]):
 95            comp_1, comp_2 = matchups[idx]
 96            r_1 = self.ratings[comp_1]
 97            r_2 = self.ratings[comp_2]
 98
 99            diff = math.exp(self.alpha * (r_1 - r_2))
100            denom = diff + (1.0 / diff) + self.kappa
101
102            p_1 = (diff + self.kappa_over_2) / denom
103            p_2 = ((1.0 / diff) + self.kappa_over_2) / denom
104
105            update_1 = self.k * (outcomes[idx] - p_1)
106            update_2 = self.k * (1.0 - outcomes[idx] - p_2)
107
108            self.ratings[comp_1] += update_1
109            self.ratings[comp_2] += update_2

Treats the matchups in the rating period as sequential events.

Arguments:
  • matchups: Sequential matchups in the rating period.
  • outcomes: Results of each matchup.
  • **kwargs: Additional parameters (not used).
def print_leaderboard(self, num_places):
111    def print_leaderboard(self, num_places):
112        sorted_idxs = np.argsort(-self.ratings)[:num_places]
113        max_len = min(np.max([len(comp) for comp in self.competitors] + [10]), 25)
114        print(f'{"competitor": <{max_len}}\t{"rating"}')
115        for p_idx in range(num_places):
116            comp_idx = sorted_idxs[p_idx]
117            print(f'{self.competitors[comp_idx]: <{max_len}}\t{self.ratings[comp_idx]:.6f}')

Prints the leaderboard of the rating system.

Arguments:
  • num_places int: The number of top places to display on the leaderboard.