<jupyter_start><jupyter_text>Introduction[Glicko2](https://en.wikipedia.org/wiki/Glicko_rating_system) is a rating system for assessing the relative strength of players in games of skill. The inventor, <NAME>, produced an example document covering the basic implementation of the Glicko2 system that can be read [here](http://www.glicko.net/glicko/glicko2.pdf).This notebook contains a Python implementation of the code in Glickman's example document. This notebook alone likely doesn't make a great deal of sense, it should be read alongside Glickman's example.  ConstantsHere $\tau$ is defined as at the end of pg. 4.<jupyter_code># Tau constrains the change in volatility over time
TAU = 0.5 
#For numerical convergence
EPSILON = 0.0000001<jupyter_output><empty_output><jupyter_text>FunctionsI've pulled these out to the start of the notebook either because they're used in several places or they're understood better in isolation.<jupyter_code>def convert_to_g2(r, RD):
    """
    This method is Step 2 of the example
    """
    mu = (r-1500) / 173.7178
    phi = RD / 173.7178
    return mu, phi

def convert_from_g2(mu, phi):
    """
    This method is Step 8 of the example
    """
    r = 173.7178*mu + 1500
    RD = 173.7178*phi
    return r, RD

def g_fun(phi):
    """
    This is the function g defined in Step 3
    """
    den = np.sqrt(1 + (3*phi**2)/(np.pi**2))
    return 1./den

def E_fun(mu, mu_j, phi_j):
    """
    This is the function E defined in step 3
    """
    den = 1 + np.exp(-g_fun(phi_j)*(mu - mu_j))
    return 1./den<jupyter_output><empty_output><jupyter_text>The example dataThis is the data defined in the **Example calculation** outlined at the bottom of page 4. Putting together this example is Step 1 in the procedure.<jupyter_code># Player in given example
r, RD, sigma = 1500, 200, 0.06

# Opponents from example (r, RD, s)
opps = [
    (1400, 30, 1),
    (1550, 100, 0),
    (1700, 300, 0)
]<jupyter_output><empty_output><jupyter_text>The Glicko 2 algorithmThis is the code for Steps 2-8.<jupyter_code># Step 2 convert player score
mu, phi = convert_to_g2(r, RD)
mu, phi
# Step 2/3
# This builds the example table at the top of page 5.
data = []
for j in [0,1,2]:
    rj, RDj, sj = opps[j]   
    muj, phij = convert_to_g2(rj, RDj)
    
    gj = g_fun(phij)
    Ej = E_fun(mu, muj, phij)
    
    data.append([muj, phij, gj, Ej, sj])
   
pd.DataFrame(data, columns = ['mu_j', 'phi_j', 'g_j', 'E_j', 's_j'])
# Step 3 - compute v
# Note: this value differs from the example on pg. 5 because
# that calculation rounds the values in the table first
v = 1./np.sum([r[2]**2 * r[3] * (1-r[3]) for r in data])
v 
# Step 4 - compute Delta
delta = v*np.sum([r[2]*(r[4] - r[3]) for r in data])
delta
# Step 5 - Numerical iteration
def f(x):
    lnum = np.exp(x)*(delta**2 - phi**2 - v - np.exp(x))
    lden = 2*(phi**2 + v + np.exp(x))**2
    
    rnum = (x-a)
    rden = TAU**2
    
    return lnum/lden - rnum/rden

#Init params
a = np.log(sigma**2)
A = np.log(sigma**2)
print('Init A:', A) 

#Get the initial B
if delta**2 > phi**2 + v:
    B = np.log(delta**2 - phi**2 - v)
else:
    k = 1
    while f(a - k*TAU) < 0:
        k+=1
    B = a-k*TAU
    
print('Init B:', B)

# Numerical procedure 
# this records convergence history in c_rows
c_rows, itr = [], 0
fA, fB = f(A), f(B)
while np.abs(B-A) > EPSILON:
    C = A + (A-B)*fA / (fB - fA)
    fC = f(C)
    c_rows.append([itr, A, B, fA, fB])

    if fC*fB < 0:
        A = B
        fA = fB
    else:
        fA = fA/2.
        
    B = C
    fB = fC
    itr+=1
    
sigma_dash = np.exp(A/2)
print("sigma_dash: ", sigma_dash)
pd.DataFrame(c_rows, columns = ['Iteration', 'A', 'B','fA', 'fB'])
#Step 6
phi_star = np.sqrt(phi**2 + sigma_dash**2)
phi_star
#Step 7 
phi_dash = 1./np.sqrt(1./phi_star**2 + 1./v)
mu_dash = mu + (phi_dash**2)*np.sum([r[2]*(r[4] - r[3]) for r in data])

phi_dash, mu_dash
#Step8 - convert back to r/RD form
r_dash, RD_dash = convert_from_g2(mu_dash, phi_dash)

# The fianl updated rating
r_dash, RD_dash, sigma_dash<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14029042579374845, 'fraction_numerical': 0.038641397981786854, 'mean_word_length': 3.146938775510204, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 20, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '187886', 'license': 'MIT', 'n_tokens_mistral': 1704, 'n_tokens_neox': 1576, 'n_words': 499, 'path': 'notebooks/glicko2.ipynb', 'repo_name': 'ProformAnalytics/tutorial_nbs', 'size': '4068'}