summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoremkael <emkael@tlen.pl>2014-11-07 16:49:24 +0100
committeremkael <emkael@tlen.pl>2014-11-07 16:49:24 +0100
commitefcf12f3e6bb437f5089215fd81b2c924961e4d2 (patch)
tree16a877357c36b54f3a3f60ad63f880f4459e0167
parent50eda8dcf4c764493efe3cae4cf81df916ea2e7d (diff)
* disparity variation algorithm
-rw-r--r--config/elo.json2
-rw-r--r--doc/ideas.txt1
-rw-r--r--doc/results.txt9
-rw-r--r--f1elo/elo.py32
-rw-r--r--f1elo/interface.py11
5 files changed, 44 insertions, 11 deletions
diff --git a/config/elo.json b/config/elo.json
index 38a7796..6b08ee8 100644
--- a/config/elo.json
+++ b/config/elo.json
@@ -1,6 +1,6 @@
{
"initial_ranking": 2000.0,
- "disparity": 400,
+ "disparity": 600,
"importance": {
"NC_Q": 1.6,
"NC_R": 16,
diff --git a/doc/ideas.txt b/doc/ideas.txt
index ec8f999..3c7f773 100644
--- a/doc/ideas.txt
+++ b/doc/ideas.txt
@@ -7,6 +7,7 @@ Tables:
Charts:
- rolling average and deviation of rankings
- average ranking at the beginning of each championship season
+ - average ranking at the end of each championship season
- peak season ranking (MAX, MIN, AVG, STDDEV GROUP BY YEAR(date))
- top 5 of every season (on average) by decade
diff --git a/doc/results.txt b/doc/results.txt
index c353368..13a56f3 100644
--- a/doc/results.txt
+++ b/doc/results.txt
@@ -21,14 +21,21 @@ Specific rules regarding Elo implementation for this application's purposes:
* no minimum rating limit for driver is enforced
* drivers are rated from their first entry (there's no initial grace period)
* shared drive entries have the effective ranking equal to the average ranking of drivers sharing the drive; pending ranking points from duels of such entries are divided equally between drivers sharing the drive
+ * driver group disparity is varied to accommodate for dynamic shifts of relative performance within the F1 field (caused by technical changes) - see below
Other than that, standard Elo rating conventions apply:
* drivers start with identical initial rating
* duels between high-ranked drivers change their rankings by fewer points than duels between low-ranked drivers
+Field disparity change:
+ * higher disparity leads to more rating inflation - as disparity is a measure of rating difference that yields a certian probability of driver's victory, so higher disparity leads to more attribution for a victory to a driver (and less attribution to the shift of performance, e.g. car performance change)
+ * higher rating deviation in the months prior to a race suggests a shift in relative performance (some drivers gain a lot, some divers lose a lot), so there's a need for damping further changes a bit - by lowering disparity
+ * once the ratings are stabilized (meaning relative performance within the field had settled), disparity can be increased
+ * this helps with the initial "rolling start" phase of the ranking (1946-1949 races)
+
Specific parameters which are configurable:
* initial driver ranking
- * disparity factor (ranking difference which drops the possibility of lower-ranked driver's win by the factor of 10)
+ * initial disparity factor (ranking difference which drops the possibility of lower-ranked driver's win by the factor of 10)
* duel importance (base, i.e. for drivers ranked below importance thresholds) for all race types
* importance thresholds (for 50% of base importance and 75% of base importance)
diff --git a/f1elo/elo.py b/f1elo/elo.py
index 51c9e23..8b3c806 100644
--- a/f1elo/elo.py
+++ b/f1elo/elo.py
@@ -1,4 +1,5 @@
-import json
+import json, dateutil
+from sqlalchemy import func
from itertools import combinations
from os import path
@@ -23,6 +24,27 @@ class Elo:
return sum([self.get_ranking(d, date) for d in entry.drivers]) / len(entry.drivers)
def rank_race(self, race):
+ recent_date = race.date - dateutil.relativedelta.relativedelta(months=9)
+ recent_ratings = self.session.query(
+ func.min(Ranking.ranking).label('min'),
+ func.max(Ranking.ranking).label('max')
+ ).filter(
+ Ranking.rank_date >= recent_date
+ ).group_by(
+ Ranking._driver
+ )
+ changes_query = self.session.query(
+ func.avg(
+ func.abs(
+ recent_ratings.subquery().columns.min - recent_ratings.subquery().columns.max
+ )
+ )
+ )
+ recent_rank_changes = changes_query.scalar()
+ race_disparity = self.config['disparity']
+ if recent_rank_changes:
+ race_disparity -= recent_rank_changes
+ print(race_disparity, str(race.date))
entries = race.entries
entries_to_compare = []
rankings = {}
@@ -38,7 +60,9 @@ class Elo:
self.get_outcome(c),
self.get_importance(race,
[rankings[c[0]],
- rankings[c[1]]]))
+ rankings[c[1]]]),
+ race_disparity
+ )
new_rankings[c[0]] += score
new_rankings[c[1]] -= score
return new_rankings
@@ -59,5 +83,5 @@ class Elo:
return 0
return 0.5
- def get_score(self, difference, outcome, importance):
- return importance * (outcome - 1 / (1 + (10 ** (-difference / self.config['disparity']))))
+ def get_score(self, difference, outcome, importance, disparity):
+ return importance * (outcome - 1 / (1 + (10 ** (-difference / disparity))))
diff --git a/f1elo/interface.py b/f1elo/interface.py
index 173cfa7..3768f8a 100644
--- a/f1elo/interface.py
+++ b/f1elo/interface.py
@@ -111,11 +111,12 @@ class Interface:
one_year = dateutil.relativedelta.relativedelta(years=1)
rankings = self.session.query(
- Ranking).filter(
- Ranking.rank_date > (
- date - one_year)).filter(
- Ranking.rank_date <= date).all(
- )
+ Ranking
+ ).filter(
+ Ranking.rank_date > (date - one_year)
+ ).filter(
+ Ranking.rank_date <= date
+ ).all()
drivers = {}
for ranking in rankings: