A More Objective College Football Rankings System
College football playoff season is upon us and since I am a USC fan I was curious how our résumé actually stacks up against those SEC/Big Ten schools statistically.
Back in college I was pretty obsessed with this problem and was really inspired by this paper which describes a ranking system that is both simple and powerful. To me, it is the most attractive of the many ways to rank teams when all the teams have played different schedules and have limited sample sizes.
This method takes into account your body of work plus your opponent's body of work plus your oponnent's opponent's ad infinitum. It discounts these indirect result by a tunable parameter. The author's also do the math so that the solution is solved using algebra. The authors use only wins/losses in their rankings but here I have extended it so that margin of victory also matters. Essentially when team a plays team b, team a accumulates points in its favor against team b and team b does the same each according to the outcome of the game. You end up with a network of results (represented as as matrix) and then do some math to figure out how many total points each team ends up with when factoring in all the other teams results.
The nice thing about this problem is there are tunable parameters. Here I have parameterized the ratings using the following parameters:
- alpha - this is the original parameter from the paper linked above. It represents how much emphasis to place on strength of schedule vs direct results
- beta - this is gets multiplied by the adjusted margin of victory, determining relative importance of margin of victory
- zeta - this the amount of points a team gets for actually winning the game and the amount a team loses when they lose the game
- hfa - this is the amount the adjusted margin of victory get adjusted to account for home field advantage
If team i plays team j and wins, the paper says that $A_{ji}$ = 1 asumming they've just played once. The paper also says that $A_{ij} = 0$ meaning that team j gets no points for this matchup no matter how close the game actually was. In my formulation, if team i plays team j and wins by a score of $p_{i}$ to $p_{j}$, then $A_{ij} = 1 + \zeta + \beta\cdot adjmargin$ and $A_{ji} = 1 - \zeta - \beta\cdot adjmargin$. I reversed the indexes since it makes more sense to me. To compute adjusted margin I take $p_{i}$ subtracted $p_{j}$ subtracted the $hfa$ parameter times an indicator if either team a or team b played at home. Let's call that $ham$ for home field adjust margin. To compute the adjusted margin in the end I do this $adjmargin = \log(|ham| + 1)\cdot sign(ham)$. The reason for the log is intuitive - the impressiveness of a win is no proportional to the marign of victory but closer to the log of it.
The nice this about this rating/ranking system is that it has parameters that you can fit to data. In my mind ratings are as good as they can predict the future. So here I download a bunch of game reuslt data from the massey rating website and implement the rating system and then use them to predict a future week of games using the ratings for each team from the games up to that week (using logistic regression). I use logloss of the predictions to measure the success of the ratings - the better the ratings the lower the logloss of the regression is. I kept the seeds the same between runs so that each parameter combination had the same games to predict. Before setting up the final parameterized verison I implemented the original formulation from the paper and predicting the same games it had a logloss of . I iterated a bit on the ratings to come up with this formulation and below is the final tuning process where I run a grid search for the best combination of parameters and output the final ratings.
Ultimately what it shows that even though the humans favor USC over Alabama, Tennessee and Ohio State, the cold hard numbers say that those teams' body of work is more impressive. Not saying that this is necessarily the way the CFB should be determined, but sometimes it's useful and neat to check our human biases with objective numbers.
import pandas as pd
pd.options.display.max_rows = 999
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import log_loss
import requests
from bs4 import BeautifulSoup
import datetime
import json
import itertools
downloading the data and parsing it to a dataframe:
games = []
url = 'https://masseyratings.com/scores.php?s=cf%s&sub=ncaa-d1&all=1&sch=on'
id_mappings = {}
for year in range(1995, 2023):
r = requests.get(url % (year,))
soup = BeautifulSoup(r.text)
lines = soup.find('pre').text.splitlines()
sep1 = datetime.date(year, 9, 1).weekday()
week1_cutoff = datetime.date(year, 9, 4 + -1 * ((sep1 - 5) % -7))
team_to_id = {}
for line in lines:
game = [year]
pieces = line.split()
if len(pieces) < 5:
continue
date = datetime.date(*map(int, pieces.pop(0).split('-')))
if date >= datetime.date.today():
continue
week = max(1, ((date - week1_cutoff).days - 1) // 7 + 2)
game.extend([week, date])
for _ in range(2):
team, is_home = '', False
while True:
new_piece = pieces.pop(0)
try:
score = int(new_piece)
break
except ValueError:
team = team + (' ' if team else '') + new_piece
if team.startswith('@'):
is_home = True
team = team[1:]
if team not in team_to_id:
team_to_id[team] = len(team_to_id)
game.extend([team_to_id[team], is_home, score])
games.append(game)
id_mappings[year] = {val: key for key, val in team_to_id.items()}
columns = [
'year', 'week', 'date',
'team_a', 'team_a_home', 'team_a_score',
'team_b', 'team_b_home', 'team_b_score'
]
df = pd.DataFrame(games, columns=columns).sort_values('date', ascending=True)
df.to_csv('df.csv', index=False)
with open('id_mappings.json', 'w') as f:
json.dump(id_mappings, f, indent=4)
df = pd.read_csv('df.csv')
with open('id_mappings.json', 'r') as f:
id_mappings = {int(year): {
int(id_): name for id_, name in map_.items()
} for year, map_ in json.load(f).items()} #cause keys cant be int in json
school_lookup = {year: {
name: id_ for id_, name in map_.items()
} for year, map_ in id_mappings.items()}
the parsed games:
df.tail(15)
year | week | date | team_a | team_a_home | team_a_score | team_b | team_b_home | team_b_score | |
---|---|---|---|---|---|---|---|---|---|
40876 | 2022 | 13 | 2022-11-26 | 14 | True | 21 | 205 | False | 17 |
40877 | 2022 | 13 | 2022-11-26 | 125 | True | 26 | 145 | False | 13 |
40878 | 2022 | 13 | 2022-11-26 | 127 | True | 28 | 141 | False | 23 |
40879 | 2022 | 13 | 2022-11-26 | 129 | True | 37 | 110 | False | 0 |
40880 | 2022 | 13 | 2022-11-26 | 113 | False | 44 | 182 | True | 7 |
40881 | 2022 | 13 | 2022-11-26 | 32 | False | 42 | 131 | True | 16 |
40882 | 2022 | 13 | 2022-11-26 | 133 | False | 45 | 138 | True | 23 |
40883 | 2022 | 13 | 2022-11-26 | 34 | True | 35 | 97 | False | 16 |
40884 | 2022 | 13 | 2022-11-26 | 36 | False | 46 | 143 | True | 39 |
40885 | 2022 | 13 | 2022-11-26 | 116 | False | 49 | 93 | True | 46 |
40886 | 2022 | 13 | 2022-11-26 | 148 | True | 49 | 153 | False | 27 |
40887 | 2022 | 13 | 2022-11-26 | 170 | False | 48 | 151 | True | 19 |
40888 | 2022 | 13 | 2022-11-26 | 192 | False | 37 | 183 | True | 30 |
40889 | 2022 | 13 | 2022-11-26 | 160 | True | 47 | 103 | False | 27 |
40890 | 2022 | 13 | 2022-11-26 | 13 | False | 49 | 173 | True | 14 |
functions for computing the ratings and scoring the parameter combinations:
def get_ratings(year, week, alpha, beta, zeta, hfa):
sub = df.loc[(df.year == year) & (df.week < week)]
hfa_adjustment = np.zeros((sub.shape[0],))
hfa_adjustment[np.where(sub.team_a_home)] = hfa
hfa_adjustment[np.where(sub.team_b_home)] = -hfa
teams = pd.concat([sub.team_a, sub.team_b]).unique()
n_teams = teams.shape[0]
assert teams.min() == 0 and teams.max() == n_teams - 1
assert (sub.team_a_score >= sub.team_b_score).mean() == 1
A = np.zeros((n_teams, n_teams))
team_a_won_mask = sub.team_a_score > sub.team_b_score
team_a_won = sub[team_a_won_mask]
margin = team_a_won.team_a_score - team_a_won.team_b_score - hfa_adjustment[team_a_won_mask]
adj_margin = np.log(np.abs(margin) + 1) * np.sign(margin)
np.add.at(A, (team_a_won.team_a, team_a_won.team_b), 1 + zeta + beta * adj_margin)
np.add.at(A, (team_a_won.team_b, team_a_won.team_a), 1 - zeta - beta * adj_margin)
tie_mask = sub.team_a_score == sub.team_b_score
ties = sub[tie_mask]
margin = ties.team_a_score - ties.team_b_score - hfa_adjustment[tie_mask]
adj_margin = np.log(np.abs(margin) + 1) * np.sign(margin)
np.add.at(A, (ties.team_a, ties.team_b), .5 + beta * adj_margin)
np.add.at(A, (ties.team_b, ties.team_a), .5 - beta * adj_margin)
game_counts = pd.concat([sub.team_a, sub.team_b]).value_counts().sort_index()
A = A/game_counts.values[:, None] #normalize by num. of games
max_alpha = 1 / np.linalg.eigvals(A).max().real
alpha_ = alpha * max_alpha
ratings_w = np.linalg.inv(np.eye(n_teams) - alpha_ * A) @ A.sum(1)
ratings_l = np.linalg.inv(np.eye(n_teams) - alpha_ * A.T) @ A.sum(0)
return ratings_w - ratings_l
def get_score(alpha, beta, zeta, hfa, folds=50):
all_years = list(range(1995, 2023))
scores = []
for fold in range(folds):
np.random.seed(fold)
train_years = set(np.random.choice(all_years, size=int(len(all_years) * .8), replace=False))
test_years = set(year for year in all_years if year not in train_years)
X_trains, y_trains = [], []
X_tests, y_tests = [], []
for year in all_years:
np.random.seed(year * (fold + 1))
week = np.random.randint(8, df.loc[df.year == year, 'week'].max() + 1) #week to predict
ratings = get_ratings(year, week, alpha, beta, zeta, hfa)
n_teams = ratings.shape[0]
games = df.loc[(
(df.year == year) & (df.week == week) &
(df.team_a < n_teams) & (df.team_b < n_teams) &
(df.team_a_score > df.team_b_score)
)]
ratings_diff = ratings[games.team_a] - ratings[games.team_b]
home_indicator = games.team_a_home.astype(float) - games.team_b_home.astype(float)
outcomes = np.random.choice([0., 1.], size=len(games))
ratings_diff[outcomes == 0] = -1 * ratings_diff[outcomes == 0]
home_indicator[outcomes == 0] = -1 * home_indicator[outcomes == 0]
if year in train_years:
X_trains.append(np.column_stack([ratings_diff, home_indicator]))
y_trains.append(outcomes)
else:
X_tests.append(np.column_stack([ratings_diff, home_indicator]))
y_tests.append(outcomes)
X_train, y_train = np.vstack(X_trains), np.concatenate(y_trains)
X_test, y_test = np.vstack(X_tests), np.concatenate(y_tests)
model = LogisticRegression(fit_intercept=False, C=10.)
model.fit(X_train, y_train)
score = log_loss(y_test, model.predict_proba(X_test)[:, 1])
scores.append(score)
return np.mean(scores)
running the grid search:
alphas = np.arange(.75, .97, .03)
betas = np.arange(.0, .5, .1)
zetas = np.arange(.05, .2, .05)
hcas = np.arange(0., 3., 1.)
results = {}
combos = list(itertools.product(alphas, betas, zetas, hcas))
combos = [(a, b, z, h) for a, b, z, h in combos if not (h > 0 and b == 0)]
print(f"{len(combos)} combos")
for i, (alpha, beta, zeta, hca) in enumerate(combos):
result = get_score(alpha, beta, zeta, hca)
if i % 25 == 0:
print(i)
if results and result < min(results.values()):
print('found new best', alpha, beta, zeta, hca, result)
results[(alpha, beta, zeta, hca)] = result
416 combos 0 found new best 0.75 0.0 0.1 0.0 0.6480628244501047 found new best 0.75 0.0 0.15000000000000002 0.0 0.624640445530048 found new best 0.75 0.0 0.2 0.0 0.6070883832458315 found new best 0.75 0.1 0.05 0.0 0.567147865349118 found new best 0.75 0.1 0.1 0.0 0.5636435437019677 found new best 0.75 0.1 0.15000000000000002 0.0 0.5614045930804463 found new best 0.75 0.1 0.2 0.0 0.5599827410962219 found new best 0.75 0.2 0.05 0.0 0.5512674855040569 25 50 found new best 0.78 0.2 0.05 0.0 0.550735725869885 75 100 found new best 0.81 0.2 0.05 0.0 0.5503019307166305 125 150 found new best 0.8400000000000001 0.2 0.05 0.0 0.5499931069504221 175 200 found new best 0.8700000000000001 0.2 0.05 0.0 0.5498550794207714 225 250 275 300 325 350 375 400
best combination:
alpha, beta, zeta, hca = min(results, key=results.get)
alpha, beta, zeta, hca
(0.8700000000000001, 0.2, 0.05, 0.0)
computing the ratings using the best parameters:
year, week = 2022, 14
ratings = get_ratings(year, week, alpha, beta, zeta, hca)
assembling the rankings into a dataframe:
current_cfp_rankings = {
'Georgia': 1,
'Michigan': 2,
'TCU': 3,
'USC': 4,
'Ohio St': 5,
'Alabama': 6,
'Tennessee': 7,
'Penn St': 8,
'Clemson': 9,
'Kansas St': 10,
'Utah': 11,
'Washington': 12,
'Florida St': 13,
'LSU': 14,
'Oregon St': 15,
'Oregon': 16,
'UCLA': 17,
'Tulane': 18,
'South Carolina': 19,
'Texas': 20,
'Notre Dame': 21,
'UCF': 22,
'North Carolina': 23,
'Mississippi St': 24,
'NC State': 25
}
final_rankings = pd.DataFrame(zip(
range(1, len(ratings) + 1),
pd.Series(np.argsort(ratings)[::-1]).map(id_mappings[year]),
np.sort(ratings)[::-1]
), columns=['ranking', 'school', 'rating'], )
final_rankings['cfp_ranking'] = final_rankings.school.map(current_cfp_rankings)
here are the final rankings alongside what the committee is currently saying. Mostly there is a lot of agreement. Noteable exceptions where the committee disagrees with these rankings are USC (committee higher), Texas (committee lower) and NC State (committee higher). In general the committee seems to be being kind to the Pac-12 this year, which I am not complaining about :).
final_rankings
ranking | school | rating | cfp_ranking | |
---|---|---|---|---|
0 | 1 | Georgia | 19.951193 | 1.0 |
1 | 2 | TCU | 16.667676 | 3.0 |
2 | 3 | Alabama | 15.589647 | 6.0 |
3 | 4 | Michigan | 15.536973 | 2.0 |
4 | 5 | Tennessee | 15.206362 | 7.0 |
5 | 6 | Ohio St | 14.788169 | 5.0 |
6 | 7 | Texas | 13.739468 | 20.0 |
7 | 8 | Kansas St | 13.520607 | 10.0 |
8 | 9 | USC | 13.283809 | 4.0 |
9 | 10 | Clemson | 13.227090 | 9.0 |
10 | 11 | Penn St | 12.205245 | 8.0 |
11 | 12 | LSU | 12.076505 | 14.0 |
12 | 13 | Florida St | 11.492409 | 13.0 |
13 | 14 | Utah | 10.940231 | 11.0 |
14 | 15 | Oregon | 10.656330 | 16.0 |
15 | 16 | Tulane | 10.634370 | 18.0 |
16 | 17 | Oregon St | 10.149833 | 15.0 |
17 | 18 | Notre Dame | 9.948450 | 21.0 |
18 | 19 | Mississippi St | 9.910323 | 24.0 |
19 | 20 | Mississippi | 9.817282 | NaN |
20 | 21 | Washington | 9.739654 | 12.0 |
21 | 22 | UCLA | 9.434639 | 17.0 |
22 | 23 | Louisville | 8.798316 | NaN |
23 | 24 | South Carolina | 8.650026 | 19.0 |
24 | 25 | UCF | 8.552051 | 22.0 |
25 | 26 | Illinois | 8.468478 | NaN |
26 | 27 | Kentucky | 8.417337 | NaN |
27 | 28 | Arkansas | 8.087891 | NaN |
28 | 29 | CS Sacramento | 7.898523 | NaN |
29 | 30 | Troy | 7.660884 | NaN |
30 | 31 | North Carolina | 7.653496 | 23.0 |
31 | 32 | Pittsburgh | 7.526987 | NaN |
32 | 33 | UT San Antonio | 7.488730 | NaN |
33 | 34 | Cincinnati | 7.451187 | NaN |
34 | 35 | Texas Tech | 7.417065 | NaN |
35 | 36 | South Alabama | 7.400338 | NaN |
36 | 37 | Baylor | 7.301112 | NaN |
37 | 38 | Oklahoma | 7.243573 | NaN |
38 | 39 | Oklahoma St | 7.147379 | NaN |
39 | 40 | Holy Cross | 7.062713 | NaN |
40 | 41 | Wake Forest | 7.056135 | NaN |
41 | 42 | S Dakota St | 7.042007 | NaN |
42 | 43 | Florida | 6.903087 | NaN |
43 | 44 | William & Mary | 6.856718 | NaN |
44 | 45 | NC State | 6.843997 | 25.0 |
45 | 46 | Jackson St | 6.822085 | NaN |
46 | 47 | Delta St | 6.713427 | NaN |
47 | 48 | James Madison | 6.691507 | NaN |
48 | 49 | Coastal Car | 6.537978 | NaN |
49 | 50 | Washington St | 6.454943 | NaN |
50 | 51 | Missouri S&T | 6.192580 | NaN |
51 | 52 | Missouri | 6.116855 | NaN |
52 | 53 | Iowa | 6.099140 | NaN |
53 | 54 | Purdue | 6.097954 | NaN |
54 | 55 | Kansas | 6.081273 | NaN |
55 | 56 | Duke | 6.069801 | NaN |
56 | 57 | Maryland | 6.054920 | NaN |
57 | 58 | Syracuse | 5.921051 | NaN |
58 | 59 | N Dakota St | 5.852531 | NaN |
59 | 60 | Boise St | 5.847480 | NaN |
60 | 61 | Minnesota | 5.844059 | NaN |
61 | 62 | Marshall | 5.834362 | NaN |
62 | 63 | Princeton | 5.738110 | NaN |
63 | 64 | SMU | 5.591731 | NaN |
64 | 65 | Samford | 5.564950 | NaN |
65 | 66 | Yale | 5.501422 | NaN |
66 | 67 | Air Force | 5.314200 | NaN |
67 | 68 | Weber St | 5.170600 | NaN |
68 | 69 | Montana St | 5.065328 | NaN |
69 | 70 | Iowa St | 5.039742 | NaN |
70 | 71 | Ohio | 4.949886 | NaN |
71 | 72 | Incarnate Word | 4.920613 | NaN |
72 | 73 | Auburn | 4.857711 | NaN |
73 | 74 | East Carolina | 4.802734 | NaN |
74 | 75 | Texas A&M | 4.664974 | NaN |
75 | 76 | Houston | 4.648101 | NaN |
76 | 77 | Memphis | 4.403205 | NaN |
77 | 78 | Wisconsin | 4.317273 | NaN |
78 | 79 | Fresno St | 4.238352 | NaN |
79 | 80 | BYU | 4.202426 | NaN |
80 | 81 | Furman | 4.043615 | NaN |
81 | 82 | Richmond | 3.919282 | NaN |
82 | 83 | Delaware | 3.890215 | NaN |
83 | 84 | Penn | 3.760171 | NaN |
84 | 85 | Jacksonville St | 3.533387 | NaN |
85 | 86 | West Virginia | 3.467647 | NaN |
86 | 87 | Liberty | 3.319831 | NaN |
87 | 88 | WKU | 3.315355 | NaN |
88 | 89 | Harvard | 3.220894 | NaN |
89 | 90 | Elon | 3.177852 | NaN |
90 | 91 | Montana | 2.957866 | NaN |
91 | 92 | Michigan St | 2.935823 | NaN |
92 | 93 | Louisiana | 2.924911 | NaN |
93 | 94 | UC Davis | 2.829245 | NaN |
94 | 95 | San Jose St | 2.788468 | NaN |
95 | 96 | Albany GA | 2.784256 | NaN |
96 | 97 | Vanderbilt | 2.726908 | NaN |
97 | 98 | St Thomas FL | 2.552671 | NaN |
98 | 99 | North Texas | 2.534444 | NaN |
99 | 100 | Appalachian St | 2.509055 | NaN |
100 | 101 | Mercer | 2.504366 | NaN |
101 | 102 | UAB | 2.482240 | NaN |
102 | 103 | Arizona | 2.476542 | NaN |
103 | 104 | Navy | 2.434591 | NaN |
104 | 105 | Army | 2.130146 | NaN |
105 | 106 | IN Wesleyan | 2.097075 | NaN |
106 | 107 | Miles | 2.093753 | NaN |
107 | 108 | Rhode Island | 1.980268 | NaN |
108 | 109 | Lane | 1.898907 | NaN |
109 | 110 | California | 1.664304 | NaN |
110 | 111 | Bloomsburg | 1.638954 | NaN |
111 | 112 | SE Missouri St | 1.607124 | NaN |
112 | 113 | W Oregon | 1.602201 | NaN |
113 | 114 | New Hampshire | 1.576071 | NaN |
114 | 115 | Northern Iowa | 1.541436 | NaN |
115 | 116 | Chattanooga | 1.418259 | NaN |
116 | 117 | Idaho | 1.414201 | NaN |
117 | 118 | Toledo | 1.403216 | NaN |
118 | 119 | Fordham | 1.336145 | NaN |
119 | 120 | Virginia | 1.284026 | NaN |
120 | 121 | Buffalo | 1.269730 | NaN |
121 | 122 | Michigan Tech | 1.224092 | NaN |
122 | 123 | San Diego St | 1.049207 | NaN |
123 | 124 | W New Mexico | 1.036203 | NaN |
124 | 125 | SW Baptist | 0.992975 | NaN |
125 | 126 | Georgia Tech | 0.987648 | NaN |
126 | 127 | Southern Miss | 0.961807 | NaN |
127 | 128 | NC Central | 0.952596 | NaN |
128 | 129 | Ga Southern | 0.895373 | NaN |
129 | 130 | St Thomas MN | 0.828540 | NaN |
130 | 131 | Limestone | 0.784220 | NaN |
131 | 132 | Barton | 0.745723 | NaN |
132 | 133 | Indiana | 0.744868 | NaN |
133 | 134 | North Greenville | 0.637388 | NaN |
134 | 135 | FL Atlantic | 0.605553 | NaN |
135 | 136 | Assumption | 0.505991 | NaN |
136 | 137 | W Salem St | 0.493514 | NaN |
137 | 138 | S Illinois | 0.489719 | NaN |
138 | 139 | Nebraska | 0.450837 | NaN |
139 | 140 | Florida A&M | 0.399019 | NaN |
140 | 141 | Miami FL | 0.353584 | NaN |
141 | 142 | Wyoming | 0.328557 | NaN |
142 | 143 | SE Louisiana | 0.269483 | NaN |
143 | 144 | MTSU | 0.268041 | NaN |
144 | 145 | Tulsa | 0.208043 | NaN |
145 | 146 | Concordia MI | 0.179353 | NaN |
146 | 147 | Youngstown St | 0.141287 | NaN |
147 | 148 | Gardner Webb | 0.135672 | NaN |
148 | 149 | Faulkner | 0.117002 | NaN |
149 | 150 | E Michigan | 0.101655 | NaN |
150 | 151 | Mississippi Col | 0.099512 | NaN |
151 | 152 | Edward Waters | 0.084357 | NaN |
152 | 153 | Austin Peay | 0.035151 | NaN |
153 | 154 | Columbia | 0.034064 | NaN |
154 | 155 | North Dakota | 0.008890 | NaN |
155 | 156 | McKendree | -0.031140 | NaN |
156 | 157 | Thomas More | -0.110327 | NaN |
157 | 158 | Georgia St | -0.150441 | NaN |
158 | 159 | St Francis PA | -0.152542 | NaN |
159 | 160 | Taylor IN | -0.181927 | NaN |
160 | 161 | Morehouse | -0.189213 | NaN |
161 | 162 | Stanford | -0.269110 | NaN |
162 | 163 | E New Mexico | -0.289517 | NaN |
163 | 164 | Keiser | -0.290639 | NaN |
164 | 165 | Abilene Chr | -0.419643 | NaN |
165 | 166 | Utah St | -0.436331 | NaN |
166 | 167 | Kent | -0.462956 | NaN |
167 | 168 | Kentucky St | -0.500371 | NaN |
168 | 169 | Mars Hill | -0.511493 | NaN |
169 | 170 | Arizona St | -0.515764 | NaN |
170 | 171 | Wm Jewell | -0.542231 | NaN |
171 | 172 | St Andrew's | -0.545712 | NaN |
172 | 173 | Tuskegee | -0.581151 | NaN |
173 | 174 | Chadron St | -0.672004 | NaN |
174 | 175 | S Connecticut | -0.856804 | NaN |
175 | 176 | FL Memorial | -0.985127 | NaN |
176 | 177 | Sam Houston St | -0.991370 | NaN |
177 | 178 | Lincoln PA | -0.995064 | NaN |
178 | 179 | Southern Univ | -1.012459 | NaN |
179 | 180 | La Verne | -1.074068 | NaN |
180 | 181 | TN Martin | -1.095462 | NaN |
181 | 182 | Louisiana Chr | -1.103126 | NaN |
182 | 183 | Connecticut | -1.137986 | NaN |
183 | 184 | Bowling Green | -1.143575 | NaN |
184 | 185 | UVA-Wise | -1.219923 | NaN |
185 | 186 | Warner | -1.637233 | NaN |
186 | 187 | Illinois St | -1.657703 | NaN |
187 | 188 | Missouri St | -1.710604 | NaN |
188 | 189 | Virginia Tech | -1.737634 | NaN |
189 | 190 | UNLV | -1.791550 | NaN |
190 | 191 | Kentucky Chr | -1.802591 | NaN |
191 | 192 | E Kentucky | -1.826442 | NaN |
192 | 193 | NC A&T | -1.831648 | NaN |
193 | 194 | Rutgers | -1.859322 | NaN |
194 | 195 | Rice | -1.972408 | NaN |
195 | 196 | Cent Arkansas | -1.995411 | NaN |
196 | 197 | Villanova | -2.038528 | NaN |
197 | 198 | Campbell | -2.060742 | NaN |
198 | 199 | Post | -2.313419 | NaN |
199 | 200 | Merrimack | -2.330931 | NaN |
200 | 201 | W Michigan | -2.378867 | NaN |
201 | 202 | W Carolina | -2.471476 | NaN |
202 | 203 | Miami OH | -2.496056 | NaN |
203 | 204 | Dartmouth | -2.632242 | NaN |
204 | 205 | Southern Utah | -2.709928 | NaN |
205 | 206 | UTEP | -2.720737 | NaN |
206 | 207 | Ball St | -2.752110 | NaN |
207 | 208 | Towson | -2.758605 | NaN |
208 | 209 | Boston College | -2.902685 | NaN |
209 | 210 | New Mexico St | -3.006257 | NaN |
210 | 211 | SF Austin | -3.013434 | NaN |
211 | 212 | Monmouth NJ | -3.159396 | NaN |
212 | 213 | Cornell | -3.194413 | NaN |
213 | 214 | ULM | -3.274284 | NaN |
214 | 215 | C Michigan | -3.366400 | NaN |
215 | 216 | Texas St | -3.519217 | NaN |
216 | 217 | Prairie View | -3.611589 | NaN |
217 | 218 | Old Dominion | -3.651485 | NaN |
218 | 219 | South Dakota | -3.678128 | NaN |
219 | 220 | Lindenwood | -3.806438 | NaN |
220 | 221 | Kennesaw | -3.843476 | NaN |
221 | 222 | Alabama St | -3.914791 | NaN |
222 | 223 | TX Southern | -3.917119 | NaN |
223 | 224 | Northwestern | -3.965733 | NaN |
224 | 225 | Temple | -3.966318 | NaN |
225 | 226 | North American | -3.978959 | NaN |
226 | 227 | E Washington | -3.998206 | NaN |
227 | 228 | Davidson | -4.053414 | NaN |
228 | 229 | San Diego | -4.152413 | NaN |
229 | 230 | Butler | -4.186960 | NaN |
230 | 231 | Dayton | -4.189590 | NaN |
231 | 232 | Portland St | -4.217445 | NaN |
232 | 233 | Louisiana Tech | -4.341291 | NaN |
233 | 234 | Colorado St | -4.397055 | NaN |
234 | 235 | Arkansas St | -4.446804 | NaN |
235 | 236 | Utah Tech | -4.654362 | NaN |
236 | 237 | Northern Arizona | -4.710085 | NaN |
237 | 238 | Tarleton St | -4.715637 | NaN |
238 | 239 | Alcorn St | -4.754446 | NaN |
239 | 240 | Colorado | -4.823167 | NaN |
240 | 241 | Brown | -4.959002 | NaN |
241 | 242 | Howard | -5.129221 | NaN |
242 | 243 | ETSU | -5.154704 | NaN |
243 | 244 | Akron | -5.163017 | NaN |
244 | 245 | SUNY Albany | -5.475824 | NaN |
245 | 246 | Citadel | -5.484321 | NaN |
246 | 247 | South Florida | -5.501069 | NaN |
247 | 248 | Wofford | -5.770905 | NaN |
248 | 249 | Bryant | -5.785882 | NaN |
249 | 250 | N Illinois | -5.867416 | NaN |
250 | 251 | Lafayette | -5.926783 | NaN |
251 | 252 | TX A&M Commerce | -5.962650 | NaN |
252 | 253 | Nevada | -6.085639 | NaN |
253 | 254 | Tennessee Tech | -6.266275 | NaN |
254 | 255 | Charlotte | -6.517040 | NaN |
255 | 256 | Indiana St | -6.598354 | NaN |
256 | 257 | Lincoln CA | -6.729517 | NaN |
257 | 258 | Hawaii | -6.976316 | NaN |
258 | 259 | Tennessee St | -6.996346 | NaN |
259 | 260 | Hampton | -7.047185 | NaN |
260 | 261 | Alabama A&M | -7.104948 | NaN |
261 | 262 | New Mexico | -7.113931 | NaN |
262 | 263 | Charleston So | -7.231389 | NaN |
263 | 264 | Florida Intl | -7.372671 | NaN |
264 | 265 | Colgate | -7.640828 | NaN |
265 | 266 | Stonehill | -7.681304 | NaN |
266 | 267 | Duquesne | -7.757698 | NaN |
267 | 268 | Sacred Heart | -7.799025 | NaN |
268 | 269 | Morgan St | -7.862250 | NaN |
269 | 270 | Cal Poly | -7.965747 | NaN |
270 | 271 | Northwestern LA | -8.044503 | NaN |
271 | 272 | Maine | -8.154745 | NaN |
272 | 273 | McNeese St | -8.250213 | NaN |
273 | 274 | N Colorado | -8.390109 | NaN |
274 | 275 | Valparaiso | -8.496765 | NaN |
275 | 276 | Grambling | -8.606401 | NaN |
276 | 277 | Stetson | -8.637578 | NaN |
277 | 278 | Delaware St | -8.668998 | NaN |
278 | 279 | S Carolina St | -8.783732 | NaN |
279 | 280 | Lehigh | -9.206290 | NaN |
280 | 281 | Nicholls St | -9.247364 | NaN |
281 | 282 | LIU Post | -9.332291 | NaN |
282 | 283 | Drake | -9.399163 | NaN |
283 | 284 | Idaho St | -9.560482 | NaN |
284 | 285 | VMI | -9.702658 | NaN |
285 | 286 | Bucknell | -9.769923 | NaN |
286 | 287 | Georgetown | -9.800058 | NaN |
287 | 288 | W Illinois | -9.905696 | NaN |
288 | 289 | E Illinois | -9.987174 | NaN |
289 | 290 | Stony Brook | -10.008251 | NaN |
290 | 291 | North Alabama | -10.255156 | NaN |
291 | 292 | Ark Pine Bluff | -10.278144 | NaN |
292 | 293 | Murray St | -10.286789 | NaN |
293 | 294 | Massachusetts | -10.553665 | NaN |
294 | 295 | Marist | -10.985417 | NaN |
295 | 296 | MS Valley St | -11.096806 | NaN |
296 | 297 | Bethune-Cookman | -11.204950 | NaN |
297 | 298 | Central Conn | -11.793550 | NaN |
298 | 299 | VA-Lynchburg | -12.028063 | NaN |
299 | 300 | Norfolk St | -12.241488 | NaN |
300 | 301 | Lamar | -12.368052 | NaN |
301 | 302 | Houston Chr | -12.569194 | NaN |
302 | 303 | Morehead St | -13.010842 | NaN |
303 | 304 | Wagner | -14.829867 | NaN |
304 | 305 | Robert Morris | -15.232463 | NaN |
305 | 306 | Presbyterian | -16.884224 | NaN |