-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enhanced docstring readability for improved code documentation.
Update readme. Refactored `load_games` function. Removed the multiplication by 100 of the uncertainty coefficient in the player ratings retrieval process. Replaced the `inspect` method in the `Game` class with the `__str__` method for more intuitive object representation.
- Loading branch information
1 parent
b3d1c87
commit 005e799
Showing
8 changed files
with
628 additions
and
287 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
*.csv | ||
*.pkl | ||
__py* | ||
setup* | ||
build/* | ||
dist/* | ||
whole_history_rating.egg-info/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,80 +1,161 @@ | ||
# whole_history_rating | ||
A python conversion from the ruby implementation of Rémi Coulom's Whole-History Rating (WHR) algorithm. | ||
|
||
The original ruby code can be found [here](https://github.com/goshrine/whole_history_rating) | ||
# Whole History Rating (WHR) Python Implementation | ||
|
||
This Python library is a conversion from the original Ruby implementation of Rémi Coulom's Whole-History Rating (WHR) algorithm, designed to provide a dynamic rating system for games or matches where players' skills are continuously estimated over time. | ||
|
||
Installation | ||
------------ | ||
The original Ruby code is available here at [goshrine](https://github.com/goshrine/whole_history_rating). | ||
|
||
## Installation | ||
|
||
To install the library, use the following command: | ||
|
||
```shell | ||
pip install whole-history-rating | ||
``` | ||
|
||
Usage | ||
----- | ||
## Usage | ||
|
||
### Basic Setup | ||
|
||
Start by importing the library and initializing the base WHR object: | ||
|
||
```py | ||
```python | ||
from whr import whole_history_rating | ||
|
||
whr = whole_history_rating.Base() | ||
``` | ||
|
||
### Creating Games | ||
|
||
Add games to the system using `create_game()` method. It takes the names of the black and white players, the winner ('B' for black, 'W' for white), the day number, and an optional handicap (generally less than 500 elo). | ||
|
||
# Base.create_game() arguments: black player name, white player name, winner, day number, handicap | ||
# Handicap should generally be less than 500 elo | ||
```python | ||
whr.create_game("shusaku", "shusai", "B", 1, 0) | ||
whr.create_game("shusaku", "shusai", "W", 2, 0) | ||
whr.create_game("shusaku", "shusai", "W", 3, 0) | ||
``` | ||
|
||
|
||
### Refining Ratings Towards Stability | ||
|
||
To achieve accurate and stable ratings, the WHR algorithm allows for iterative refinement. This process can be controlled manually or handled automatically to adjust player ratings until they reach a stable state. | ||
|
||
#### Manual Iteration | ||
|
||
For manual control over the iteration process, specify the number of iterations you wish to perform. This approach gives you direct oversight over the refinement steps. | ||
|
||
# Iterate the WHR algorithm towards convergence with more players/games, more iterations are needed. | ||
```python | ||
whr.iterate(50) | ||
``` | ||
|
||
This command will perform 50 iterations, incrementally adjusting player ratings towards stability with each step. | ||
|
||
#### Automatic Iteration | ||
|
||
For a more hands-off approach, the algorithm can automatically iterate until the Elo ratings stabilize within a specified precision. Automatic iteration is particularly useful when dealing with large datasets or when seeking to automate the rating process. | ||
|
||
```python | ||
whr.auto_iterate(time_limit=10, precision=1e-3, batch_size=10) | ||
``` | ||
|
||
- `time_limit` (optional): Sets a maximum duration (in seconds) for the iteration process. If `None` (the default), the algorithm will run indefinitely until the specified precision is achieved. | ||
- `precision` (optional): Defines the desired level of accuracy for the ratings' stability. The default value is `0.001`, indicating that iteration will stop when changes between iterations are less than or equal to this threshold. | ||
- `batch_size` (optional): Determines the number of iterations to perform before checking for convergence and, if a `time_limit` is set, before evaluating whether the time limit has been reached. The default value is `10`, balancing between frequent convergence checks and computational efficiency. | ||
|
||
This automated process allows the algorithm to efficiently converge to stable ratings, adjusting the number of iterations dynamically based on the complexity of the data and the specified precision and time constraints. | ||
|
||
|
||
### Viewing Ratings | ||
|
||
Retrieve and view player ratings, which include the day number, elo rating, and uncertainty: | ||
|
||
```python | ||
# Example output for whr.ratings_for_player("shusaku") | ||
print(whr.ratings_for_player("shusaku")) | ||
# Output: | ||
# [[1, -43, 0.84], | ||
# [2, -45, 0.84], | ||
# [3, -45, 0.84]] | ||
|
||
# Example output for whr.ratings_for_player("shusai") | ||
print(whr.ratings_for_player("shusai")) | ||
# Output: | ||
# [[1, 43, 0.84], | ||
# [2, 45, 0.84], | ||
# [3, 45, 0.84]] | ||
|
||
``` | ||
|
||
You can also view or retrieve all ratings in order: | ||
|
||
```python | ||
whr.print_ordered_ratings(current=False) # Set `current=True` for the latest rankings only. | ||
ratings = whr.get_ordered_ratings(current=False, compact=False) # Set `compact=True` for a condensed list. | ||
``` | ||
|
||
### Predicting Match Outcomes | ||
|
||
Predict the outcome of future matches, including between non-existent players: | ||
|
||
```python | ||
# Example of predicting a future match outcome | ||
probability = whr.probability_future_match("shusaku", "shusai", 0) | ||
print(f"Win probability: shusaku: {probability[0]*100}%; shusai: {probability[1]*100}%") | ||
# Output: | ||
# Win probability: shusaku: 37.24%; shusai: 62.76% <== this is printed | ||
# (0.3724317501643667, 0.6275682498356332) | ||
``` | ||
|
||
|
||
### Enhanced Batch Loading of Games | ||
|
||
This feature facilitates the batch loading of multiple games simultaneously by accepting a list of strings, where each string encapsulates the details of a single game. To accommodate names with both first and last names and ensure flexibility in data formatting, you can specify a custom separator (e.g., a comma) to delineate the game attributes. | ||
|
||
#### Standard Loading | ||
|
||
Without specifying a separator, the default space (' ') is used to split the game details: | ||
|
||
```python | ||
whr.load_games([ | ||
"shusaku shusai B 1 0", # Game 1: Shusaku vs. Shusai, Black wins, Day 1, no handicap. | ||
"shusaku shusai W 2 0", # Game 2: Shusaku vs. Shusai, White wins, Day 2, no handicap. | ||
"shusaku shusai W 3 0" # Game 3: Shusaku vs. Shusai, White wins, Day 3, no handicap. | ||
]) | ||
``` | ||
|
||
#### Custom Separator for Complex Names | ||
|
||
When game details include names with spaces, such as first and last names, utilize the `separator` parameter to define an alternative delimiter, ensuring the integrity of each data point: | ||
|
||
```python | ||
whr.load_games([ | ||
"John Doe, Jane Smith, W, 1, 0", # Game 1: John Doe vs. Jane Smith, White wins, Day 1, no handicap. | ||
"Emily Chen, Liam Brown, B, 2, 0" # Game 2: Emily Chen vs. Liam Brown, Black wins, Day 2, no handicap. | ||
], separator=",") | ||
``` | ||
|
||
This method allows for a clear and error-free way to load game data, especially when player names or game details include spaces, providing a robust solution for managing diverse datasets. | ||
|
||
|
||
### Saving and Loading States | ||
|
||
Save the current state to a file and reload it later to avoid recalculating: | ||
|
||
# Or let the module iterate until the elo is stable (precision by default 10E-3) with a time limit of 10 seconds by default | ||
whr.auto_iterate(time_limit = 10, precision = 10E-3) | ||
|
||
# Results are stored in one triplet for each game: [day_number, elo_rating, uncertainty] | ||
whr.ratings_for_player("shusaku") => | ||
[[1, -43, 84], | ||
[2, -45, 84], | ||
[3, -45, 84]] | ||
whr.ratings_for_player("shusai") => | ||
[[1, 43, 84], | ||
[2, 45, 84], | ||
[3, 45, 84]] | ||
|
||
# You can print or get all ratings ordered | ||
whr.print_ordered_ratings(current=False) # current to True to only get the last rank estimation | ||
whr.get_ordered_ratings(current=False, compact=False) # compact to True to not have the name before each ranks | ||
|
||
# You can get a prediction for a future game between two players (even non existing players) | ||
# Base.probability_future_match() arguments: black player name, white player name, handicap | ||
whr.probability_future_match("shusaku", "shusai",0) => | ||
win probability: shusaku:37.24%; shusai:62.76% | ||
|
||
# You can load several games all together using a file or a list of string representing the game | ||
# all elements in list must be like: "black_name white_name winner time_step handicap extras" | ||
# you can exclude handicap (default=0) and extras (default={}) | ||
whr.load_games(["shusaku shusai B 1 0", "shusaku shusai W 2", "shusaku shusai W 3 0"]) | ||
whr.load_games(["firstname1 name1, firstname2 name2, W, 1"], separator=",") | ||
|
||
# You can save and load a base (you don't have to redo all iterations) | ||
whr.save_base(path) | ||
whr2 = whole_history_rating.Base.load_base(path) | ||
```python | ||
whr.save_base('path_to_save.whr') | ||
whr2 = whole_history_rating.Base.load_base('path_to_save.whr') | ||
``` | ||
|
||
Optional Configuration | ||
---------------------- | ||
## Optional Configuration | ||
|
||
One of the meta parameters to WHR is the variance of rating change over one time step, `w2`, | ||
which determines how much a player's rating is likely change in one day. Higher numbers allow for faster progress. | ||
The default value is 300, which is fairly high. | ||
Rémi Coulom in his paper, used w2=14 to get his [results](https://www.remi-coulom.fr/WHR/WHR.pdf) | ||
Adjust the `w2` parameter, which influences the variance of rating change over time, allowing for faster or slower progression. The default is set to 300, but Rémi Coulom used a value of 14 in his paper to achieve his results. | ||
|
||
```py | ||
whr = whole_history_rating.Base({'w2':14}) | ||
```python | ||
whr = whole_history_rating.Base({'w2': 14}) | ||
``` | ||
|
||
You can also set the base not case sensitive. "shusaku" and "ShUsAkU" will be the same. | ||
Enable case-insensitive player names to treat "shusaku" and "ShUsAkU" as the same entity: | ||
|
||
```py | ||
whr = whole_history_rating.Base({'uncased':True}) | ||
```python | ||
whr = whole_history_rating.Base({'uncased': True}) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.