Last active
August 29, 2015 13:57
-
-
Save paulgrav/9393738 to your computer and use it in GitHub Desktop.
Rankings
This file contains hidden or 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
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
import operator | |
import pprint | |
matches = [ | |
{ "h": {"name": "PG", "score": 2}, "a": {"name": "MK", "score": 2} }, | |
{ "h": {"name": "PG", "score": 1}, "a": {"name": "MK", "score": 2} }, | |
{ "h": {"name": "MK", "score": 1}, "a": {"name": "PG", "score": 0} }, | |
{ "h": {"name": "GT", "score": 1}, "a": {"name": "MK", "score": 2} }, | |
{ "h": {"name": "NP", "score": 2}, "a": {"name": "MK", "score": 1} }, | |
{ "h": {"name": "PG", "score": 0}, "a": {"name": "MC", "score": 1} }, | |
{ "h": {"name": "PG", "score": 0}, "a": {"name": "JH", "score": 0} }, | |
{ "h": {"name": "PG", "score": 3}, "a": {"name": "CBo", "score": 0} }, | |
{ "h": {"name": "PG", "score": 1}, "a": {"name": "NP", "score": 1} }, | |
{ "h": {"name": "MC", "score": 1}, "a": {"name": "DG", "score": 2} }, | |
{ "h": {"name": "MC", "score": 0}, "a": {"name": "DK", "score": 0} }, | |
{ "h": {"name": "DK", "score": 3}, "a": {"name": "CBo", "score": 0} }, | |
{ "h": {"name": "AB", "score": 0}, "a": {"name": "CBo", "score": 0} }, | |
{ "h": {"name": "CBo", "score": 2}, "a": {"name": "JH", "score": 0} }, | |
{ "h": {"name": "GT", "score": 1}, "a": {"name": "DK", "score": 1} }, | |
{ "h": {"name": "DK", "score": 4}, "a": {"name": "NP", "score": 4} }, | |
{ "h": {"name": "NP", "score": 1}, "a": {"name": "CBe", "score": 1} }, | |
{ "h": {"name": "MK", "score": 1}, "a": {"name": "CBe", "score": 1} }, | |
{ "h": {"name": "AB", "score": 0}, "a": {"name": "PG", "score": 1} }, | |
{ "h": {"name": "JH", "score":1 }, "a": {"name": "CBe", "score": 4} }, | |
{ "h": {"name": "JH", "score":2 }, "a": {"name": "MK", "score": 1} }, | |
{ "h": {"name": "DK", "score":0 }, "a": {"name": "DG", "score": 4} }, | |
{ "h": {"name": "DG", "score":2 }, "a": {"name": "CBo", "score": 0} }, | |
{ "h": {"name": "DK", "score":3 }, "a": {"name": "AB", "score": 0} }, | |
{ "h": {"name": "PG", "score":2 }, "a": {"name": "GT", "score": 0} }, | |
{ "h": {"name": "DG", "score":3 }, "a": {"name": "DK", "score": 1} }, | |
{ "h": {"name": "NP", "score":4 }, "a": {"name": "JH", "score": 0} }, | |
{ "h": {"name": "MC", "score":5 }, "a": {"name": "CBo", "score": 0} }, | |
] | |
current_ratings = {} | |
K = 20 # all matches are friendlies | |
INITIAL_RANKING = 1700 | |
def rating(ro, k, w, we): | |
return ro + k * (w - we) | |
def winexpect(dr): | |
""" | |
winexpectancy should return a float between 0 and 1.0. | |
dr is the rating difference | |
""" | |
return 1 / (10 ** ( -dr / 400) + 1.0) | |
def resultscore(scorea, scoreb): | |
if scorea > scoreb: | |
return 1 | |
if scorea < scoreb: | |
return 0 | |
return 0.5 | |
def kmodifier(gd): | |
#K is then adjusted for the goal difference in the game. It is increased by half if a game is won by two goals, by 3/4 if a game is won by three goals, and by 3/4 + (N-3)/8 if the game is won by four or more goals, where N is the goal difference. | |
if gd >=4: | |
return 1.75 + (gd-3)/8 | |
if gd >=3: | |
return 1.75 | |
if gd >=2: | |
return 1.5 | |
return 1 | |
def ratingsformatch(match): | |
hname = match["h"]["name"] | |
aname = match["a"]["name"] | |
hscore = match["h"]["score"] | |
ascore = match["a"]["score"] | |
chrating = current_ratings.get(hname, INITIAL_RANKING) | |
carating = current_ratings.get(aname, INITIAL_RANKING) | |
hres = resultscore(hscore, ascore) | |
ares = resultscore(ascore, hscore) | |
goaldiff = abs(hscore - ascore) | |
hrating = rating(chrating, K * kmodifier(goaldiff), hres, winexpect(chrating - carating)) | |
arating = rating(carating, K * kmodifier(goaldiff), ares, winexpect(carating - chrating)) | |
return (hrating, arating) | |
for i in matches: | |
(hrating, arating) = ratingsformatch(i) | |
hname = i['h']['name'] | |
aname = i['a']['name'] | |
i["h"]["rating"] = int(hrating) | |
i["a"]["rating"] = int(arating) | |
current_ratings[hname] = hrating | |
current_ratings[aname] = arating | |
sorted_ratings = sorted(current_ratings.iteritems(), key=operator.itemgetter(1)) | |
sorted_ratings.reverse() | |
for (k, v) in sorted_ratings: | |
print "%s\t%s" % (k, int(v)) | |
for i in matches: | |
for (k,v) in i.iteritems(): | |
print "%s\t%s\t%s" % (v['name'],v['score'],v['rating']) | |
# The ratings are based on the following formulas: | |
# | |
# Rn = Ro + K x (W - We) | |
# | |
# Rn is the new rating, Ro is the old (pre-match) rating. | |
# | |
# K is the weight constant for the tournament played: | |
# | |
# 60 for World Cup finals; | |
# 50 for continental championship finals and major intercontinental tournaments; | |
# 40 for World Cup and continental qualifiers and major tournaments; | |
# 30 for all other tournaments; | |
# 20 for friendly matches. | |
# K is then adjusted for the goal difference in the game. It is increased by half if a game is won by two goals, by 3/4 if a game is won by three goals, and by 3/4 + (N-3)/8 if the game is won by four or more goals, where N is the goal difference. | |
# | |
# W is the result of the game (1 for a win, 0.5 for a draw, and 0 for a loss). | |
# | |
# We is the expected result (win expectancy), either from the chart or the following formula: | |
# | |
# We = 1 / (10(-dr/400) + 1) | |
# | |
# dr equals the difference in ratings plus 100 points for a team playing at home. | |
# | |
# Sample Winning Expectancies | |
# Difference | |
# in Ratings Higher | |
# Rated Lower | |
# Rated | |
# 0 0.500 0.500 | |
# 10 0.514 0.486 | |
# 20 0.529 0.471 | |
# 30 0.543 0.457 | |
# 40 0.557 0.443 | |
# 50 0.571 0.429 | |
# 60 0.585 0.415 | |
# 70 0.599 0.401 | |
# 80 0.613 0.387 | |
# 90 0.627 0.373 | |
# 100 0.640 0.360 | |
# 110 0.653 0.347 | |
# 120 0.666 0.334 | |
# 130 0.679 0.321 | |
# 140 0.691 0.309 | |
# 150 0.703 0.297 | |
# 160 0.715 0.285 | |
# 170 0.727 0.273 | |
# 180 0.738 0.262 | |
# 190 0.749 0.251 | |
# 200 0.760 0.240 | |
# 210 0.770 0.230 | |
# 220 0.780 0.220 | |
# 230 0.790 0.210 | |
# 240 0.799 0.201 | |
# 250 0.808 0.192 | |
# 260 0.817 0.183 | |
# 270 0.826 0.174 | |
# 280 0.834 0.166 | |
# 290 0.841 0.159 | |
# 300 0.849 0.151 | |
# 325 0.867 0.133 | |
# 350 0.882 0.118 | |
# 375 0.896 0.104 | |
# 400 0.909 0.091 | |
# 425 0.920 0.080 | |
# 450 0.930 0.070 | |
# 475 0.939 0.061 | |
# 500 0.947 0.053 | |
# 525 0.954 0.046 | |
# 550 0.960 0.040 | |
# 575 0.965 0.035 | |
# 600 0.969 0.031 | |
# 625 0.973 0.027 | |
# 650 0.977 0.023 | |
# 675 0.980 0.020 | |
# 700 0.983 0.017 | |
# 725 0.985 0.015 | |
# 750 0.987 0.013 | |
# 775 0.989 0.011 | |
# 800 0.990 0.010 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment