Absolutely. Attached are the results of running the following code, with minor analysis:
import sys
import requests
import numpy as np
from enum import Enum
BASE_ROUTE = "http://www.thebluealliance.com/api/v3"
AUTH_KEY = PUT YOUR TBA AUTH KEY HERE ]]
class ModelType(Enum):
SIMPLE = 'simple'
KEYS = 'keys'
class DetailType(Enum):
ALLIANCES = 'alliances'
DISTRICT_POINTS = 'district_points'
INSIGHTS = 'insights'
OPRS = 'oprs'
RANKINGS = 'rankings'
def format_route(route, **kwargs):
for arg in kwargs:
route = route.replace("<{}>".format(arg), kwargs[arg])
return route
def call_tba(route):
return requests.get(BASE_ROUTE + route, headers={'X-TBA-Auth-Key': AUTH_KEY})
def get_tba_matches(event_key, model_type):
return call_tba(format_route('/event/<event_key>/matches/<model_type>',
event_key=event_key, model_type=model_type.value))
def get_tba_teams(event_key, model_type):
return call_tba(format_route('/event/<event_key>/teams/<model_type>',
event_key=event_key, model_type=model_type.value))
def get_tba_event_detail(event_key, detail_type):
return call_tba(format_route('/event/<event_key>/<detail_type>',
event_key=event_key, detail_type=detail_type.value))
def get_tba_event_list(year, model_type):
return call_tba(format_route('/events/<year>/<model_type>',
year=year, model_type=model_type.value))
def get_tba_match_detail(match_key):
return call_tba(format_route('/match/<match_key>',
match_key=match_key))
def teams_that_played(match_simple):
teams = ]
for match in match_simple:
teams.extend(match'alliances']'red']'team_keys'])
teams.extend(match'alliances']'blue']'team_keys'])
return list(set(teams))
def build_base_matrices(quals, team_keys):
Ar = ]
Ab = ]
Mr = ]
Mb = ]
for match in quals:
_ar = [0]*len(team_keys)
_ab = [0]*len(team_keys)
red = match'alliances']'red']
blue = match'alliances']'blue']
for team in red'team_keys']:
_ar[team_keys.index(team)] = 1
for team in blue'team_keys']:
_ab[team_keys.index(team)] = 1
Ar.append(_ar)
Ab.append(_ab)
Mr.append([red['score']])
Mb.append([blue['score']])
return np.array(Ar), np.array(Ab), np.array(Mr), np.array(Mb)
def print_teams_and_stat(stat, team_keys, team_stats):
stats = {team_key: power for (team_key, power) in
zip(team_keys, team_stats)}
print('Team : {}
----------------------'.format(stat))
for team in stats:
val = stats[team][0]
div = str(int(val))
rem = str(val)[len(div)+1:]:3]
print(str.format('{: 5d} : {: >4s}.{}', int(team[3:]), div, rem))
def diag_solve(A, M):
tmp_1 = np.matmul(np.transpose(A), A)
tmp_2 = np.matmul(np.transpose(A), M)
return np.matmul(np.linalg.inv(np.diag(np.diag(tmp_1))), tmp_2)
def inv_solve(A, M):
tmp_1 = np.matmul(np.transpose(A), A)
tmp_2 = np.matmul(np.transpose(A), M)
return np.matmul(np.linalg.inv(tmp_1), tmp_2)
def pinv_solve(A, M):
tmp_1 = np.matmul(np.transpose(A), A)
tmp_2 = np.matmul(np.transpose(A), M)
return np.matmul(np.linalg.pinv(tmp_1), tmp_2)
def sopr_solve(Ao, Ad, Mo):
tmp_1 = np.matmul(np.transpose(Ao), Ao)
tmp_2 = np.matmul(np.transpose(-Ao), Ad)
tmp_3 = np.matmul(np.transpose(-Ad), Ao)
tmp_4 = np.matmul(np.transpose(Ad), Ad)
tmp_5 = np.concatenate((np.transpose(Ao), np.transpose(Ad)), axis=0)
tmp_6 = np.concatenate((tmp_1, tmp_2), axis=1)
tmp_7 = np.concatenate((tmp_3, tmp_4), axis=1)
tmp_8 = np.concatenate((tmp_6, tmp_7), axis=0)
tmp_9 = np.matmul(tmp_5, Mo)
return np.matmul(np.linalg.pinv(tmp_8), tmp_9)
def event_prediction_accuracy(event_code):
match_keys = get_tba_matches(event_code, ModelType.SIMPLE).json()
team_keys = teams_that_played(match_keys)
quals = [key for key in match_keys if key['comp_level'] == 'qm']
Ar, Ab, Mr, Mb = build_base_matrices(quals, team_keys)
Ao = np.concatenate((Ar, Ab), axis=0)
Ad = np.concatenate((Ab, Ar), axis=0)
Aw = np.subtract(Ar, Ab)
Mw = np.subtract(Mr, Mb)
Mo = np.concatenate((Mr, Mb), axis=0)
Mwp = np.concatenate((Mw, -Mw), axis=0)
Oave = sum(Mo)/(3 * len(Mo))
# Contributions based on average offensive production
# O_ave = diag_solve(Ao, Mo)
# O_a = O_ave - 2*Oave
# D_ave = diag_solve(Ad, Mo)
# D_a = 3*Oave - D_ave
# W_ave = np.add(O_a - Oave, D_a)
# OPR
O_opr = inv_solve(Ao, Mo)
# CCWM
# W_ccwm = inv_solve(Ao, Mwp)
# WMPR
W_wmpr = pinv_solve(Aw, Mw)
# C_cpr = W_wmpr + Oave
# DPR
# D_dpr = inv_solve(Ad, Mo)
# D_dprb = Oave - D_dpr
# Simultaneous OPR, DPR
# S_sopr = sopr_solve(Ao, Ad, Mo)
# [O_sopr, D_sdpr] = np.split(S_sopr, 2)
# W_swmpr = np.add(O_sopr - Oave, D_sdpr)
# C_scpr = np.add(O_sopr, D_sdpr)
# print_teams_and_stat("C_cpr", team_keys, C_cpr)
wmprs = {team_key: power for (team_key, power) in zip(team_keys, W_wmpr)}
oprs = {team_key: power for (team_key, power) in zip(team_keys, O_opr)}
opr_acc = 0
wmpr_acc = 0
for match in quals:
red_opr_score = sum([oprs[team] for team in match'alliances']'red']'team_keys']])
blue_opr_score = sum([oprs[team] for team in match'alliances']'blue']'team_keys']])
opr_prediction = red_opr_score > blue_opr_score
red_wmpr_score = sum([wmprs[team] for team in match'alliances']'red']'team_keys']])
blue_wmpr_score = sum([wmprs[team] for team in match'alliances']'blue']'team_keys']])
wmpr_prediction = red_wmpr_score > blue_wmpr_score
result = match'alliances']'red']'score'] > match'alliances']'blue']'score']
opr_acc += 1 if opr_prediction == result else 0
wmpr_acc += 1 if wmpr_prediction == result else 0
print('{},{},{}'.format(event_code, opr_acc/len(quals)*100, wmpr_acc/len(quals)*100))
def mass_evaluation():
event_keys = get_tba_event_list('2017', ModelType.KEYS).json()
print('Event, OPR Accuracy, WMPR Accuracy')
for event in event_keys:
try:
event_prediction_accuracy(event)
except:
pass
def main():
if len(sys.argv) > 1:
event_prediction_accuracy(sys.argv[1])
else:
mass_evaluation()
if __name__ == "__main__":
main()
Based on the results, at 83% of events so far this year, WMPR has provided a better fit than OPR at predicting matches. Over all events this year, WMPR is 4.75% better at predicting match outcomes than OPR.
Results.csv (6.99 KB)
Results.csv (6.99 KB)