5. Keener’s Method

Keener’s Method states that the strength \(s\) of a team should

  • consider its interaction with opponents and these opponents’ strengths, as well as,

  • be proportionally constant to its rating \(r\)

    • \(s = \lambda r\).

The strength statistics \(a_{ij}\) created by the i-th team when playing the j-th team can be derived from something meaning such as wins, yards, passes, etc… For example, \(a_{ij}\) can be the number of wins by the i-th team over the j-th team.

\(a_{ij} = w_{ij}\)

Or it can consider ties as well.

\(a_{ij} = w_{ij} - \dfrac{t_{ij}}{2}\)

Whatever strength statistics you use, we can denoted as \(S_{ij}\).

\(a_{ij} = S_{ij}\)

Typically, we want want to normalize \(a_{ij}\) as follows to avoid disproportionate effects on the ratings.

\(a_{ij} = \dfrac{S_{ij}}{S_{ij} + S_{ji}}\)

But, since there are some pairs of teams that might have not played, we can use Laplace’s rule of succession to normalize as follows.

\(a_{ij} = \dfrac{S_{ij} + 1}{S_{ij} + S_{ji} + 2}\)

Keener also suggests that we apply a skewing function to combat skew in the data. Skew could come from teams inflating their strength statistics to increase their ratings and thereby ranking. A particular skew function recommended by Keener is as follows.

\(h(x) = \dfrac{1}{2} + \dfrac{\mathrm{sgn} \{x - \frac{1}{2} \} \sqrt{|2x - 1|}}{2}\)

The strength \(s\) of each team is given as

\(s = Ar\),

where

  • \(A\) is a \(n\) x \(n\) matrix with \(a_{ij}\) element representing the strength statistic of the i-th team when it plays the j-th team, and

  • \(r\) is the rating.

Remember that we stated two things:

  • \(s = \lambda r\), and

  • \(s = Ar\).

Thus,

\(Ar = \lambda r\),

and we can interpret

  • \(\lambda\) as an eigenvalue, and

  • \(r\) as the corresponding eigenvector to the eigenvalue.

Using any numerical method we want, when can compute the eigenvectors and eigenvalues of \(A\). The eigenvector associated with the largest eigenvalue will have its elements correspond to the teams and be the ratings. When the ratings are sorted (the values of the eigenvector), then the ranking is produced.

5.1. NFL 2009

Let’s look at the whole NFL 2009 season. This data has scores, yards and turnovers. We will use scores to create the strength statistics.

[1]:
import pandas as pd

def get_nfl_2009():
    return pd.read_csv('./nfl/2009.csv')

df = get_nfl_2009()
teams = sorted(list(set(df.t1) | set(df.t2)))

df
[1]:
t1 t2 s1 s2 yd1 yd2 to1 to2 home_team week
0 Steelers Titans 13 10 357 320 3 2.0 Steelers 1
1 Broncos Bengals 12 7 302 307 0 2.0 Bengals 1
2 Ravens Chiefs 38 24 501 188 1 0.0 Ravens 1
3 Colts Jaguars 14 12 365 228 2 0.0 Colts 1
4 Cowboys Buccaneers 34 21 462 450 0 0.0 Buccaneers 1
... ... ... ... ... ... ... ... ... ... ...
251 Cowboys Eagles 24 0 474 228 1 1.0 Cowboys 17
252 Chiefs Broncos 44 24 524 512 2 3.0 Broncos 17
253 Ravens Raiders 21 13 330 325 0 2.0 Raiders 17
254 Titans Seahawks 17 13 304 309 2 1.0 Seahawks 17
255 Jets Bengals 37 0 320 72 0 3.0 Jets 17

256 rows × 10 columns

\(a_{ij}\) will actually be the total number of points scored by the i-th team on the j-th team during the whole season. These are the cummulative points.

[2]:
def get_cummulative_score(df, t1, t2, f1='s1', f2='s2'):
    s1 = df[(df.t1 == t1) & (df.t2 == t2)][f1].sum()
    s2 = df[(df.t1 == t2) & (df.t2 == t1)][f2].sum()
    s = s1 + s2
    return s

rscore_df = pd.DataFrame([[get_cummulative_score(df, t1, t2) if t1 != t2 else 0 for t2 in teams]
              for t1 in teams], index=teams, columns=teams)
rscore_df
[2]:
49ers Bears Bengals Bills Broncos Browns Buccaneers Cardinals Chargers Chiefs ... Raiders Rams Ravens Redskins Saints Seahawks Steelers Texans Titans Vikings
49ers 0 10 0 0 0 0 0 44 0 0 ... 0 63 0 0 0 40 0 21 27 24
Bears 6 0 10 0 0 30 0 21 0 0 ... 0 17 7 0 0 25 17 0 0 46
Bengals 0 45 0 0 7 39 0 0 24 17 ... 17 0 34 0 0 0 41 17 0 10
Bills 0 0 0 0 0 3 33 0 0 16 ... 0 0 0 0 7 0 0 10 17 0
Broncos 0 0 12 0 0 27 0 0 37 68 ... 42 0 7 17 0 0 10 0 0 0
Browns 0 6 27 6 6 0 0 0 23 41 ... 23 0 3 0 0 0 27 0 0 20
Buccaneers 0 0 0 20 0 0 0 0 0 0 ... 0 0 0 13 27 24 0 0 0 0
Cardinals 25 41 0 0 0 0 0 0 0 0 ... 0 52 0 0 0 58 0 28 17 30
Chargers 0 0 27 0 55 30 0 0 0 80 ... 48 0 26 23 0 0 28 0 42 0
Chiefs 0 0 10 10 57 34 0 0 21 0 ... 26 0 24 14 0 0 27 0 0 0
Colts 18 0 0 7 28 0 0 31 0 0 ... 0 42 17 0 0 34 0 55 58 0
Cowboys 0 0 0 0 10 0 34 0 17 26 ... 24 0 0 24 24 38 0 0 0 0
Dolphins 0 0 0 52 0 0 25 0 13 0 ... 0 0 0 0 34 0 24 20 24 0
Eagles 27 24 0 0 30 0 33 0 23 34 ... 9 0 0 54 22 0 0 0 0 0
Falcons 45 21 0 31 0 0 40 0 0 0 ... 0 0 0 31 50 0 0 0 0 0
Giants 0 0 0 0 6 0 24 17 20 27 ... 44 0 0 68 27 0 0 0 0 7
Jaguars 3 0 0 18 0 17 0 17 0 24 ... 0 23 0 0 0 0 0 54 50 0
Jets 0 0 37 32 0 0 26 0 0 0 ... 38 0 0 0 10 0 0 24 24 0
Lions 6 47 13 0 0 38 0 24 0 0 ... 0 10 3 19 27 20 20 0 0 23
Packers 30 42 24 0 0 31 28 33 0 0 ... 0 36 27 0 0 48 36 0 0 49
Panthers 0 0 0 9 0 0 44 34 0 0 ... 0 0 0 20 43 0 0 0 0 26
Patriots 0 0 0 42 17 0 35 0 0 0 ... 0 0 27 0 17 0 0 27 59 0
Raiders 0 0 20 0 23 9 0 0 36 23 ... 0 0 13 13 0 0 27 6 0 0
Rams 6 9 0 0 0 0 0 23 0 0 ... 0 0 0 7 23 17 0 13 7 10
Ravens 0 31 21 0 30 50 0 0 31 38 ... 21 0 0 0 0 0 40 0 0 31
Redskins 0 0 0 0 27 0 16 0 20 6 ... 34 9 0 0 30 0 0 0 0 0
Saints 0 0 0 27 0 0 55 0 0 0 ... 0 28 0 33 0 0 0 0 0 0
Seahawks 30 19 0 0 0 0 7 23 0 0 ... 0 55 0 0 0 0 0 7 13 9
Steelers 0 14 32 0 28 33 0 0 38 24 ... 24 0 40 0 0 0 0 0 13 27
Texans 24 0 28 31 0 0 0 21 0 0 ... 29 16 0 0 0 34 0 0 51 0
Titans 34 0 0 41 0 0 0 20 17 0 ... 0 47 0 0 0 17 10 51 0 0
Vikings 27 66 30 0 0 34 0 17 0 0 ... 0 38 33 0 0 35 17 0 0 0

32 rows × 32 columns

We then apply Laplace's rule of succession.

[3]:
def get_strength(df, i, j):
    s_ij = df.iloc[i].iloc[j]
    s_ji = df.iloc[j].iloc[i]
    s = (s_ij + 1) / (s_ij + s_ji + 2)
    return s

sscore_df = pd.DataFrame([[get_strength(rscore_df, i, j) for j in range(len(teams))]
                          for i in range(len(teams))], index=teams, columns=teams)
sscore_df
[3]:
49ers Bears Bengals Bills Broncos Browns Buccaneers Cardinals Chargers Chiefs ... Raiders Rams Ravens Redskins Saints Seahawks Steelers Texans Titans Vikings
49ers 0.500000 0.611111 0.500000 0.500000 0.500000 0.500000 0.500000 0.633803 0.500000 0.500000 ... 0.500000 0.901408 0.500000 0.500000 0.500000 0.569444 0.500000 0.468085 0.444444 0.471698
Bears 0.388889 0.500000 0.192982 0.500000 0.500000 0.815789 0.500000 0.343750 0.500000 0.500000 ... 0.500000 0.642857 0.200000 0.500000 0.500000 0.565217 0.545455 0.500000 0.500000 0.412281
Bengals 0.500000 0.807018 0.500000 0.500000 0.380952 0.588235 0.500000 0.500000 0.471698 0.620690 ... 0.461538 0.500000 0.614035 0.500000 0.500000 0.500000 0.560000 0.382979 0.500000 0.261905
Bills 0.500000 0.500000 0.500000 0.500000 0.500000 0.363636 0.618182 0.500000 0.500000 0.607143 ... 0.500000 0.500000 0.500000 0.500000 0.222222 0.500000 0.500000 0.255814 0.300000 0.500000
Broncos 0.500000 0.500000 0.619048 0.500000 0.500000 0.800000 0.500000 0.500000 0.404255 0.543307 ... 0.641791 0.500000 0.205128 0.391304 0.500000 0.500000 0.275000 0.500000 0.500000 0.500000
Browns 0.500000 0.184211 0.411765 0.636364 0.200000 0.500000 0.500000 0.500000 0.436364 0.545455 ... 0.705882 0.500000 0.072727 0.500000 0.500000 0.500000 0.451613 0.500000 0.500000 0.375000
Buccaneers 0.500000 0.500000 0.500000 0.381818 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 ... 0.500000 0.500000 0.500000 0.451613 0.333333 0.757576 0.500000 0.500000 0.500000 0.500000
Cardinals 0.366197 0.656250 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 ... 0.500000 0.688312 0.500000 0.500000 0.500000 0.710843 0.500000 0.568627 0.461538 0.632653
Chargers 0.500000 0.500000 0.528302 0.500000 0.595745 0.563636 0.500000 0.500000 0.500000 0.786408 ... 0.569767 0.500000 0.457627 0.533333 0.500000 0.500000 0.426471 0.500000 0.704918 0.500000
Chiefs 0.500000 0.500000 0.379310 0.392857 0.456693 0.454545 0.500000 0.500000 0.213592 0.500000 ... 0.529412 0.500000 0.390625 0.681818 0.500000 0.500000 0.528302 0.500000 0.500000 0.500000
Colts 0.558824 0.500000 0.500000 0.205128 0.630435 0.500000 0.500000 0.744186 0.500000 0.500000 ... 0.500000 0.860000 0.529412 0.500000 0.500000 0.660377 0.500000 0.554455 0.686047 0.500000
Cowboys 0.500000 0.500000 0.500000 0.500000 0.379310 0.500000 0.614035 0.500000 0.461538 0.562500 ... 0.757576 0.500000 0.500000 0.781250 0.581395 0.684211 0.500000 0.500000 0.500000 0.500000
Dolphins 0.500000 0.500000 0.500000 0.557895 0.500000 0.500000 0.520000 0.500000 0.368421 0.500000 ... 0.500000 0.500000 0.500000 0.500000 0.426829 0.500000 0.446429 0.428571 0.471698 0.500000
Eagles 0.666667 0.543478 0.500000 0.500000 0.525424 0.500000 0.693878 0.500000 0.428571 0.700000 ... 0.416667 0.500000 0.500000 0.567010 0.319444 0.500000 0.500000 0.500000 0.500000 0.500000
Falcons 0.807018 0.594595 0.500000 0.888889 0.500000 0.500000 0.594203 0.500000 0.500000 0.500000 ... 0.500000 0.500000 0.500000 0.640000 0.451327 0.500000 0.500000 0.500000 0.500000 0.500000
Giants 0.500000 0.500000 0.500000 0.500000 0.205882 0.500000 0.961538 0.418605 0.488372 0.622222 ... 0.849057 0.500000 0.500000 0.696970 0.363636 0.500000 0.500000 0.500000 0.500000 0.150943
Jaguars 0.160000 0.500000 0.500000 0.542857 0.500000 0.428571 0.500000 0.360000 0.500000 0.531915 ... 0.500000 0.533333 0.500000 0.500000 0.500000 0.023256 0.500000 0.561224 0.515152 0.500000
Jets 0.500000 0.500000 0.974359 0.523810 0.500000 0.500000 0.870968 0.500000 0.500000 0.500000 ... 0.975000 0.500000 0.500000 0.500000 0.305556 0.500000 0.500000 0.757576 0.581395 0.500000
Lions 0.250000 0.358209 0.368421 0.500000 0.500000 0.506494 0.500000 0.438596 0.500000 0.500000 ... 0.500000 0.379310 0.075472 0.571429 0.378378 0.388889 0.420000 0.500000 0.500000 0.303797
Packers 0.553571 0.589041 0.438596 0.500000 0.500000 0.888889 0.426471 0.809524 0.500000 0.500000 ... 0.500000 0.672727 0.651163 0.500000 0.500000 0.816667 0.493333 0.500000 0.500000 0.420168
Panthers 0.500000 0.500000 0.500000 0.322581 0.500000 0.500000 0.616438 0.614035 0.500000 0.500000 ... 0.500000 0.500000 0.500000 0.538462 0.517647 0.500000 0.500000 0.500000 0.500000 0.771429
Patriots 0.500000 0.500000 0.500000 0.551282 0.461538 0.500000 0.818182 0.500000 0.500000 0.500000 ... 0.500000 0.500000 0.560000 0.500000 0.315789 0.500000 0.500000 0.444444 0.983607 0.500000
Raiders 0.500000 0.500000 0.538462 0.500000 0.358209 0.294118 0.500000 0.500000 0.430233 0.470588 ... 0.500000 0.500000 0.388889 0.285714 0.500000 0.500000 0.528302 0.189189 0.500000 0.500000
Rams 0.098592 0.357143 0.500000 0.500000 0.500000 0.500000 0.500000 0.311688 0.500000 0.500000 ... 0.500000 0.500000 0.500000 0.444444 0.452830 0.243243 0.500000 0.451613 0.142857 0.220000
Ravens 0.500000 0.800000 0.385965 0.500000 0.794872 0.927273 0.500000 0.500000 0.542373 0.609375 ... 0.611111 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.484848
Redskins 0.500000 0.500000 0.500000 0.500000 0.608696 0.500000 0.548387 0.500000 0.466667 0.318182 ... 0.714286 0.555556 0.500000 0.500000 0.476923 0.500000 0.500000 0.500000 0.500000 0.500000
Saints 0.500000 0.500000 0.500000 0.777778 0.500000 0.500000 0.666667 0.500000 0.500000 0.500000 ... 0.500000 0.547170 0.500000 0.523077 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000
Seahawks 0.430556 0.434783 0.500000 0.500000 0.500000 0.500000 0.242424 0.289157 0.500000 0.500000 ... 0.500000 0.756757 0.500000 0.500000 0.500000 0.500000 0.500000 0.186047 0.437500 0.217391
Steelers 0.500000 0.454545 0.440000 0.500000 0.725000 0.548387 0.500000 0.500000 0.573529 0.471698 ... 0.471698 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.560000 0.608696
Texans 0.531915 0.500000 0.617021 0.744186 0.500000 0.500000 0.500000 0.431373 0.500000 0.500000 ... 0.810811 0.548387 0.500000 0.500000 0.500000 0.813953 0.500000 0.500000 0.500000 0.500000
Titans 0.555556 0.500000 0.500000 0.700000 0.500000 0.500000 0.500000 0.538462 0.295082 0.500000 ... 0.500000 0.857143 0.500000 0.500000 0.500000 0.562500 0.440000 0.500000 0.500000 0.500000
Vikings 0.528302 0.587719 0.738095 0.500000 0.500000 0.625000 0.500000 0.367347 0.500000 0.500000 ... 0.500000 0.780000 0.515152 0.500000 0.500000 0.782609 0.391304 0.500000 0.500000 0.500000

32 rows × 32 columns

Finally, we combat skew in the data.

[4]:
import numpy as np

def h(x):
    h = 0.5 + ((np.sign(x - 0.5) * np.sqrt(np.abs(2 * x - 1))) / 2)
    return h

X = sscore_df.applymap(h)
X
[4]:
49ers Bears Bengals Bills Broncos Browns Buccaneers Cardinals Chargers Chiefs ... Raiders Rams Ravens Redskins Saints Seahawks Steelers Texans Titans Vikings
49ers 0.500000 0.735702 0.500000 0.500000 0.500000 0.500000 0.500000 0.758653 0.500000 0.500000 ... 0.500000 0.948000 0.500000 0.500000 0.500000 0.686339 0.500000 0.373677 0.333333 0.381042
Bears 0.264298 0.500000 0.108198 0.500000 0.500000 0.897360 0.500000 0.220492 0.500000 0.500000 ... 0.500000 0.767261 0.112702 0.500000 0.500000 0.680579 0.650756 0.500000 0.500000 0.290573
Bengals 0.500000 0.891802 0.500000 0.500000 0.256025 0.710042 0.500000 0.500000 0.381042 0.745652 ... 0.361325 0.500000 0.738783 0.500000 0.500000 0.500000 0.673205 0.258110 0.500000 0.154967
Bills 0.500000 0.500000 0.500000 0.500000 0.500000 0.238884 0.743086 0.500000 0.500000 0.731455 ... 0.500000 0.500000 0.500000 0.500000 0.127322 0.500000 0.500000 0.150582 0.183772 0.500000
Broncos 0.500000 0.500000 0.743975 0.500000 0.500000 0.887298 0.500000 0.500000 0.281203 0.647151 ... 0.766262 0.500000 0.116026 0.266874 0.500000 0.500000 0.164590 0.500000 0.500000 0.500000
Browns 0.500000 0.102640 0.289958 0.761116 0.112702 0.500000 0.500000 0.500000 0.321623 0.650756 ... 0.820844 0.500000 0.037792 0.500000 0.500000 0.500000 0.344457 0.500000 0.500000 0.250000
Buccaneers 0.500000 0.500000 0.500000 0.256914 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 ... 0.500000 0.500000 0.500000 0.344457 0.211325 0.858870 0.500000 0.500000 0.500000 0.500000
Cardinals 0.241347 0.779508 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 ... 0.500000 0.806848 0.500000 0.500000 0.500000 0.824687 0.500000 0.685240 0.361325 0.757539
Chargers 0.500000 0.500000 0.618958 0.500000 0.718797 0.678377 0.500000 0.500000 0.500000 0.878423 ... 0.686772 0.500000 0.354444 0.629099 0.500000 0.500000 0.308259 0.500000 0.820092 0.500000
Chiefs 0.500000 0.500000 0.254348 0.268545 0.352849 0.349244 0.500000 0.500000 0.121577 0.500000 ... 0.621268 0.500000 0.266146 0.801511 0.500000 0.500000 0.618958 0.500000 0.500000 0.500000
Colts 0.671499 0.500000 0.500000 0.116026 0.755377 0.500000 0.500000 0.849418 0.500000 0.500000 ... 0.500000 0.924264 0.621268 0.500000 0.500000 0.783176 0.500000 0.665008 0.804997 0.500000
Cowboys 0.500000 0.500000 0.500000 0.500000 0.254348 0.500000 0.738783 0.500000 0.361325 0.676777 ... 0.858870 0.500000 0.500000 0.875000 0.701737 0.803488 0.500000 0.500000 0.500000 0.500000
Dolphins 0.500000 0.500000 0.500000 0.670139 0.500000 0.500000 0.600000 0.500000 0.243505 0.500000 ... 0.500000 0.500000 0.500000 0.500000 0.308727 0.500000 0.336337 0.311018 0.381042 0.500000
Eagles 0.788675 0.647442 0.500000 0.500000 0.612747 0.500000 0.811350 0.500000 0.311018 0.816228 ... 0.295876 0.500000 0.500000 0.683044 0.199537 0.500000 0.500000 0.500000 0.500000 0.500000
Falcons 0.891802 0.717479 0.500000 0.940959 0.500000 0.500000 0.717029 0.500000 0.500000 0.500000 ... 0.500000 0.500000 0.500000 0.764575 0.343999 0.500000 0.500000 0.500000 0.500000 0.500000
Giants 0.500000 0.500000 0.500000 0.500000 0.116518 0.500000 0.980384 0.298263 0.423751 0.747207 ... 0.917766 0.500000 0.500000 0.813823 0.238884 0.500000 0.500000 0.500000 0.500000 0.082234
Jaguars 0.087689 0.500000 0.500000 0.646385 0.500000 0.311018 0.500000 0.235425 0.500000 0.626323 ... 0.500000 0.629099 0.500000 0.500000 0.500000 0.011766 0.500000 0.674964 0.587039 0.500000
Jets 0.500000 0.500000 0.987011 0.609109 0.500000 0.500000 0.930678 0.500000 0.500000 0.500000 ... 0.987340 0.500000 0.500000 0.500000 0.188195 0.500000 0.500000 0.858870 0.701737 0.500000
Lions 0.146447 0.233738 0.243505 0.500000 0.500000 0.556980 0.500000 0.324781 0.500000 0.500000 ... 0.500000 0.254348 0.039279 0.688982 0.253402 0.264298 0.300000 0.500000 0.500000 0.186789
Packers 0.663663 0.710999 0.324781 0.500000 0.500000 0.940959 0.308259 0.893398 0.500000 0.500000 ... 0.500000 0.793877 0.774921 0.500000 0.500000 0.897911 0.442265 0.500000 0.500000 0.300210
Panthers 0.500000 0.500000 0.500000 0.202158 0.500000 0.500000 0.741287 0.738783 0.500000 0.500000 ... 0.500000 0.500000 0.500000 0.638675 0.593934 0.500000 0.500000 0.500000 0.500000 0.868394
Patriots 0.500000 0.500000 0.500000 0.660128 0.361325 0.500000 0.898862 0.500000 0.500000 0.500000 ... 0.500000 0.500000 0.673205 0.500000 0.196512 0.500000 0.500000 0.333333 0.991735 0.500000
Raiders 0.500000 0.500000 0.638675 0.500000 0.233738 0.179156 0.500000 0.500000 0.313228 0.378732 ... 0.500000 0.500000 0.264298 0.172673 0.500000 0.500000 0.618958 0.105785 0.500000 0.500000
Rams 0.052000 0.232739 0.500000 0.500000 0.500000 0.500000 0.500000 0.193152 0.500000 0.500000 ... 0.500000 0.500000 0.500000 0.333333 0.346426 0.141701 0.500000 0.344457 0.077423 0.125834
Ravens 0.500000 0.887298 0.261217 0.500000 0.883974 0.962208 0.500000 0.500000 0.645556 0.733854 ... 0.735702 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.412961
Redskins 0.500000 0.500000 0.500000 0.500000 0.733126 0.500000 0.655543 0.500000 0.370901 0.198489 ... 0.827327 0.666667 0.500000 0.500000 0.392583 0.500000 0.500000 0.500000 0.500000 0.500000
Saints 0.500000 0.500000 0.500000 0.872678 0.500000 0.500000 0.788675 0.500000 0.500000 0.500000 ... 0.500000 0.653574 0.500000 0.607417 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000
Seahawks 0.313661 0.319421 0.500000 0.500000 0.500000 0.500000 0.141130 0.175313 0.500000 0.500000 ... 0.500000 0.858299 0.500000 0.500000 0.500000 0.500000 0.500000 0.103797 0.323223 0.124095
Steelers 0.500000 0.349244 0.326795 0.500000 0.835410 0.655543 0.500000 0.500000 0.691741 0.381042 ... 0.381042 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.673205 0.733126
Texans 0.626323 0.500000 0.741890 0.849418 0.500000 0.500000 0.500000 0.314760 0.500000 0.500000 ... 0.894215 0.655543 0.500000 0.500000 0.500000 0.896203 0.500000 0.500000 0.500000 0.500000
Titans 0.666667 0.500000 0.500000 0.816228 0.500000 0.500000 0.500000 0.638675 0.179908 0.500000 ... 0.500000 0.922577 0.500000 0.500000 0.500000 0.676777 0.326795 0.500000 0.500000 0.500000
Vikings 0.618958 0.709427 0.845033 0.500000 0.500000 0.750000 0.500000 0.242461 0.500000 0.500000 ... 0.500000 0.874166 0.587039 0.500000 0.500000 0.875905 0.266874 0.500000 0.500000 0.500000

32 rows × 32 columns

5.2. Numpy

We will use numpy to find the eigenvalues and eigenvectors.

[5]:
e, E = np.linalg.eig(X)
[6]:
e
[6]:
array([1.58321633e+01+0.j        , 2.22768644e-03+1.85772142j,
       2.22768644e-03-1.85772142j, 3.39712442e-03+1.35674872j,
       3.39712442e-03-1.35674872j, 1.00041396e-03+1.26017375j,
       1.00041396e-03-1.26017375j, 1.04401936e-02+1.20532274j,
       1.04401936e-02-1.20532274j, 4.22054558e-03+1.07142414j,
       4.22054558e-03-1.07142414j, 7.62218544e-04+0.95584753j,
       7.62218544e-04-0.95584753j, 1.50585060e-02+0.7822698j ,
       1.50585060e-02-0.7822698j , 2.70582449e-03+0.72218668j,
       2.70582449e-03-0.72218668j, 2.01788728e-03+0.63803178j,
       2.01788728e-03-0.63803178j, 4.25439229e-03+0.57011899j,
       4.25439229e-03-0.57011899j, 1.11927138e-02+0.4517283j ,
       1.11927138e-02-0.4517283j , 7.65441825e-03+0.35245526j,
       7.65441825e-03-0.35245526j, 1.15967256e-02+0.24209874j,
       1.15967256e-02-0.24209874j, 3.16542655e-03+0.18988739j,
       3.16542655e-03-0.18988739j, 2.97621199e-03+0.07044387j,
       2.97621199e-03-0.07044387j, 2.49615168e-03+0.j        ])

The sorted eigenvector shows that the Saints are at the top with the Rams at the bottom. The Saints won the Super Bowl during the 2009-2010 season.

[7]:
pd.Series(E[:,0], index=teams).sort_values(ascending=True)
[7]:
Saints       -0.203355+0.000000j
Packers      -0.201014+0.000000j
Patriots     -0.197235+0.000000j
Chargers     -0.197097+0.000000j
Colts        -0.195922+0.000000j
Vikings      -0.195730+0.000000j
Cowboys      -0.195317+0.000000j
Jets         -0.195166+0.000000j
Eagles       -0.190664+0.000000j
Ravens       -0.190316+0.000000j
Steelers     -0.188673+0.000000j
Texans       -0.188029+0.000000j
Falcons      -0.183949+0.000000j
Cardinals    -0.182012+0.000000j
49ers        -0.179368+0.000000j
Broncos      -0.178877+0.000000j
Bengals      -0.177157+0.000000j
Panthers     -0.173233+0.000000j
Titans       -0.171839+0.000000j
Giants       -0.171517+0.000000j
Dolphins     -0.167714+0.000000j
Bears        -0.165493+0.000000j
Redskins     -0.163789+0.000000j
Bills        -0.163556+0.000000j
Jaguars      -0.162974+0.000000j
Chiefs       -0.157590+0.000000j
Browns       -0.157127+0.000000j
Seahawks     -0.153406+0.000000j
Raiders      -0.147552+0.000000j
Buccaneers   -0.147396+0.000000j
Lions        -0.144028+0.000000j
Rams         -0.140011+0.000000j
dtype: complex128

5.3. Scikit-Learn

Here, we use Scikit-Learn to retrieve the eigenvalues and eigenvectors.

[8]:
from sklearn.decomposition import TruncatedSVD

svd = TruncatedSVD(n_components=2, n_iter=100, random_state=37)
svd.fit(X)
[8]:
TruncatedSVD(n_iter=100, random_state=37)
[9]:
svd.singular_values_
[9]:
array([16.16520004,  1.85991954])

Again, we see the Saints at the top.

[10]:
pd.Series(svd.components_[0,:], index=teams).sort_values(ascending=True)
[10]:
Saints        0.148945
Packers       0.151001
Patriots      0.154887
Chargers      0.155021
Colts         0.156045
Vikings       0.156115
Cowboys       0.156881
Jets          0.156972
Eagles        0.161209
Ravens        0.161649
Steelers      0.163317
Texans        0.163902
Falcons       0.167846
Cardinals     0.169756
49ers         0.172460
Broncos       0.172764
Bengals       0.174560
Panthers      0.178314
Titans        0.179604
Giants        0.180136
Dolphins      0.183847
Bears         0.186135
Redskins      0.187734
Bills         0.188043
Jaguars       0.188691
Chiefs        0.193845
Browns        0.194392
Seahawks      0.198091
Buccaneers    0.203681
Raiders       0.203764
Lions         0.207208
Rams          0.211215
dtype: float64

Here are the ratings side-by-side.

[11]:
pd.DataFrame({
    'numpy': pd.Series(E[:,0], index=teams),
    'scikit': pd.Series(svd.components_[0,:], index=teams)
})
[11]:
numpy scikit
49ers -0.179368+0.000000j 0.172460
Bears -0.165493+0.000000j 0.186135
Bengals -0.177157+0.000000j 0.174560
Bills -0.163556+0.000000j 0.188043
Broncos -0.178877+0.000000j 0.172764
Browns -0.157127+0.000000j 0.194392
Buccaneers -0.147396+0.000000j 0.203681
Cardinals -0.182012+0.000000j 0.169756
Chargers -0.197097+0.000000j 0.155021
Chiefs -0.157590+0.000000j 0.193845
Colts -0.195922+0.000000j 0.156045
Cowboys -0.195317+0.000000j 0.156881
Dolphins -0.167714+0.000000j 0.183847
Eagles -0.190664+0.000000j 0.161209
Falcons -0.183949+0.000000j 0.167846
Giants -0.171517+0.000000j 0.180136
Jaguars -0.162974+0.000000j 0.188691
Jets -0.195166+0.000000j 0.156972
Lions -0.144028+0.000000j 0.207208
Packers -0.201014+0.000000j 0.151001
Panthers -0.173233+0.000000j 0.178314
Patriots -0.197235+0.000000j 0.154887
Raiders -0.147552+0.000000j 0.203764
Rams -0.140011+0.000000j 0.211215
Ravens -0.190316+0.000000j 0.161649
Redskins -0.163789+0.000000j 0.187734
Saints -0.203355+0.000000j 0.148945
Seahawks -0.153406+0.000000j 0.198091
Steelers -0.188673+0.000000j 0.163317
Texans -0.188029+0.000000j 0.163902
Titans -0.171839+0.000000j 0.179604
Vikings -0.195730+0.000000j 0.156115

Here’s the ranking side-by-side. The only difference is with the Buccaneers and Raiders.

[12]:
ranking_df = pd.DataFrame({
    'numpy': pd.Series(E[:,0], index=teams).sort_values().index,
    'scikit': pd.Series(svd.components_[0,:], index=teams).sort_values().index
})
ranking_df['differs?'] = ranking_df.numpy != ranking_df.scikit
ranking_df
[12]:
numpy scikit differs?
0 Saints Saints False
1 Packers Packers False
2 Patriots Patriots False
3 Chargers Chargers False
4 Colts Colts False
5 Vikings Vikings False
6 Cowboys Cowboys False
7 Jets Jets False
8 Eagles Eagles False
9 Ravens Ravens False
10 Steelers Steelers False
11 Texans Texans False
12 Falcons Falcons False
13 Cardinals Cardinals False
14 49ers 49ers False
15 Broncos Broncos False
16 Bengals Bengals False
17 Panthers Panthers False
18 Titans Titans False
19 Giants Giants False
20 Dolphins Dolphins False
21 Bears Bears False
22 Redskins Redskins False
23 Bills Bills False
24 Jaguars Jaguars False
25 Chiefs Chiefs False
26 Browns Browns False
27 Seahawks Seahawks False
28 Raiders Buccaneers True
29 Buccaneers Raiders True
30 Lions Lions False
31 Rams Rams False

5.4. Ranking by yards

Let’s use yardage gains instead.

[13]:
rscore_df = pd.DataFrame([[get_cummulative_score(df, t1, t2, 'yd1', 'yd2') if t1 != t2 else 0 for t2 in teams]
              for t1 in teams], index=teams, columns=teams)
sscore_df = pd.DataFrame([[get_strength(rscore_df, i, j) for j in range(len(teams))]
                          for i in range(len(teams))], index=teams, columns=teams)
X = sscore_df.applymap(h)

e, E = np.linalg.eig(X)

svd = TruncatedSVD(n_components=2, n_iter=100, random_state=37)
svd.fit(X)

ranking_df = pd.DataFrame({
    'numpy': pd.Series(E[:,0], index=teams).sort_values().index,
    'scikit': pd.Series(svd.components_[0,:], index=teams).sort_values().index
})
ranking_df['differs?'] = ranking_df.numpy != ranking_df.scikit
ranking_df
[13]:
numpy scikit differs?
0 Browns Packers True
1 Buccaneers Cowboys True
2 Raiders Texans True
3 Lions Jets True
4 Rams Steelers True
5 Chiefs Eagles True
6 Bills Patriots True
7 49ers Vikings True
8 Seahawks Saints True
9 Falcons Ravens True
10 Bears Giants True
11 Dolphins Chargers True
12 Jaguars Colts True
13 Cardinals Panthers True
14 Titans Broncos True
15 Redskins Bengals True
16 Bengals Redskins True
17 Broncos Titans True
18 Panthers Cardinals True
19 Colts Jaguars True
20 Chargers Dolphins True
21 Giants Bears True
22 Ravens Falcons True
23 Saints Seahawks True
24 Vikings 49ers True
25 Patriots Bills True
26 Eagles Chiefs True
27 Steelers Rams True
28 Jets Lions True
29 Texans Raiders True
30 Cowboys Buccaneers True
31 Packers Browns True

5.5. Turnovers

Let’s rank the teams by turnovers. It’s interesting that we can also rank teams to see who’s the worst.

[14]:
rscore_df = pd.DataFrame([[get_cummulative_score(df, t1, t2, 'to1', 'to2') if t1 != t2 else 0 for t2 in teams]
              for t1 in teams], index=teams, columns=teams)
sscore_df = pd.DataFrame([[get_strength(rscore_df, i, j) for j in range(len(teams))]
                          for i in range(len(teams))], index=teams, columns=teams)
X = sscore_df.applymap(h)

e, E = np.linalg.eig(X)

svd = TruncatedSVD(n_components=2, n_iter=100, random_state=37)
svd.fit(X)

ranking_df = pd.DataFrame({
    'numpy': pd.Series(E[:,0], index=teams).sort_values().index,
    'scikit': pd.Series(svd.components_[0,:], index=teams).sort_values().index
})
ranking_df['differs?'] = ranking_df.numpy != ranking_df.scikit
ranking_df
[14]:
numpy scikit differs?
0 Lions Lions False
1 Raiders Raiders False
2 Rams Rams False
3 Browns Browns False
4 Giants Giants False
5 Seahawks Seahawks False
6 Dolphins Dolphins False
7 Steelers Steelers False
8 Buccaneers Buccaneers False
9 Titans Titans False
10 Redskins Redskins False
11 Jaguars Jaguars False
12 Bengals Bengals False
13 Bills Bills False
14 Bears Bears False
15 Texans Texans False
16 Chargers Chargers False
17 49ers 49ers False
18 Cardinals Cardinals False
19 Colts Colts False
20 Saints Saints False
21 Cowboys Cowboys False
22 Falcons Falcons False
23 Jets Jets False
24 Broncos Broncos False
25 Panthers Panthers False
26 Vikings Eagles True
27 Eagles Vikings True
28 Ravens Ravens False
29 Chiefs Chiefs False
30 Patriots Patriots False
31 Packers Packers False