Win/Loss Rating Model Prediction

Load the model and make predictions


In [3]:
import pandas as pd
import numpy as np
import pymc3 as pm
import seaborn as sns
import datetime as dt
import matplotlib.pyplot as plt
from scipy.stats import norm
from wl_model.spcl_case import *
plt.style.use('fivethirtyeight')
%matplotlib inline

Get a list of all CS Games


In [4]:
import cfscrape # need nodejs
import json
scraper = cfscrape.create_scraper()
games = json.loads(scraper.get("http://thunderpick.com/api/matches").content)
games = pd.DataFrame(games['data'])
games = games[games.gameId == 6].sort_values('startTime')

bet_games = []
for i,v in games.iterrows():
    if((v['isTournament'] == False )& (v['canWager'] == True)):
        ratio = v['matchBet']['buckets'][0]['amount']/v['matchBet']['buckets'][1]['amount']
        odds = (ratio**-1+1, ratio+1)
        wr = (odds[1]/np.sum(odds)*100., odds[0]/np.sum(odds)*100.)
        det = json.loads(scraper.get('https://thunderpick.com/api/matches/'+str(v['id'])).content)['data']
        print('Date: %s  |  Event: %s  | (BO%s) %s vs. %s  |  (%.1f:%.1f) | Total Coins: %i' % 
              (v['startTime'][:10], v['championship'], det['bestOfMaps'], v['matchBet']['buckets'][0]['label'], 
               v['matchBet']['buckets'][1]['label'], wr[0], wr[1], v['matchBet']['amount']))
        bet_games.append({'1': v['matchBet']['buckets'][0]['label'], '2': v['matchBet']['buckets'][1]['label'], 'bo': det['bestOfMaps'], 'o1': odds[0], 'o2': odds[1], 'wr': wr[0]})
bet_games = pd.DataFrame(bet_games)


Date: 2017-11-14  |  Event: Stream.me Gauntlet  | (BO3) pro100 vs. Elements Pro Gaming  |  (74.9:25.1) | Total Coins: 9427
Date: 2017-11-14  |  Event: Legend Series  | (BO1) Spirit Academy vs. Planetkey  |  (69.4:30.6) | Total Coins: 7520
Date: 2017-11-14  |  Event: Legend Series  | (BO1) Valiance vs. ALTERNATE aTTaX  |  (42.2:57.8) | Total Coins: 5883
Date: 2017-11-14  |  Event: ESEA MDL Season 26  | (BO3) Fragsters vs. eXtatus  |  (40.0:60.0) | Total Coins: 5779
Date: 2017-11-14  |  Event: X-BET.co Invitational  | (BO1) aAa vs. TTFU  |  (72.3:27.7) | Total Coins: 5410
Date: 2017-11-14  |  Event: ESEA MDL Season 26  | (BONone) Space Soldiers vs. aAa  |  (76.5:23.5) | Total Coins: 6390
Date: 2017-11-14  |  Event: Intel Extreme Masters 2017  | (BO1) EnVyUs vs. SK  |  (27.9:72.1) | Total Coins: 19146
Date: 2017-11-14  |  Event: Intel Extreme Masters 2017  | (BO1) NiP vs. Astralis  |  (60.3:39.7) | Total Coins: 33424
Date: 2017-11-14  |  Event: ESEA MDL Season 26  | (BO3) Virtus.pro vs. SuperJymy  |  (78.4:21.6) | Total Coins: 6937
Date: 2017-11-14  |  Event: ESEA MDL Season 26  | (BO3) AGO vs. Singularity  |  (69.0:31.0) | Total Coins: 5167
Date: 2017-11-14  |  Event: Intel Extreme Masters 2017  | (BO1) Cloud9 vs. TheMongolz  |  (75.8:24.2) | Total Coins: 15504
Date: 2017-11-14  |  Event: Intel Extreme Masters 2017  | (BO1) NiP vs. SK  |  (34.0:66.0) | Total Coins: 16310
Date: 2017-11-14  |  Event: Intel Extreme Masters 2017  | (BO1) EnVyUs vs. TheMongolz  |  (73.3:26.7) | Total Coins: 15150
Date: 2017-11-14  |  Event: Intel Extreme Masters 2017  | (BO1) Cloud9 vs. Astralis  |  (41.6:58.4) | Total Coins: 16450
Date: 2017-11-14  |  Event: Intel Extreme Masters 2017  | (BO1) SK vs. TheMongolz  |  (75.4:24.6) | Total Coins: 15269
Date: 2017-11-14  |  Event: Intel Extreme Masters 2017  | (BO1) EnVyUs vs. Astralis  |  (37.6:62.4) | Total Coins: 15150
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) G2 vs. FaZe  |  (39.5:60.5) | Total Coins: 15200
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) OpTic vs. Gambit  |  (52.6:47.4) | Total Coins: 17600
Date: 2017-11-15  |  Event: ESEA MDL Season 26  | (BO3) Ghost Academy vs. FRENCH CANADIANS  |  (40.0:60.0) | Total Coins: 5000
Date: 2017-11-15  |  Event: ESEA MDL Season 26  | (BO3) SoaR vs. Beacon  |  (60.0:40.0) | Total Coins: 5000
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) OpTic vs. FaZe  |  (40.0:60.0) | Total Coins: 15000
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) Liquid vs. Renegades  |  (59.6:40.4) | Total Coins: 15100
Date: 2017-11-15  |  Event: ESEA MDL Season 26  | (BO3) Gale Force vs. GX  |  (60.0:40.0) | Total Coins: 5000
Date: 2017-11-15  |  Event: ESEA MDL Season 26  | (BO3) Rise Nation vs. Mythic  |  (60.0:40.0) | Total Coins: 5000
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) Liquid vs. Gambit  |  (47.6:52.4) | Total Coins: 15180
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) G2 vs. Renegades  |  (60.0:40.0) | Total Coins: 15000
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) FaZe vs. Renegades  |  (60.0:40.0) | Total Coins: 15000
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) G2 vs. Gambit  |  (65.7:34.3) | Total Coins: 17500
Date: 2017-11-15  |  Event: ESEA MDL Season 26  | (BO1) Riotous vs. Grayhound  |  (35.0:65.0) | Total Coins: 5000
Date: 2017-11-15  |  Event: ESEA MDL Season 26  | (BO1) Athletico vs. Kings  |  (40.0:60.0) | Total Coins: 5000
Date: 2017-11-15  |  Event: X-BET.co Invitational  | (BO1) forZE vs. GoodJob  |  (60.0:40.0) | Total Coins: 5000
Date: 2017-11-15  |  Event: Mother Russia  | (BO1) GoodJob vs. Tricked  |  (39.6:60.4) | Total Coins: 5050
Date: 2017-11-15  |  Event: WCA 2017  | (BO3) BIG vs. Natus Vincere  |  (40.1:59.9) | Total Coins: 15366
Date: 2017-11-15  |  Event: X-BET.co Invitational  | (BO1) HAVU vs. EnVyUs Academy  |  (59.4:40.6) | Total Coins: 5050
Date: 2017-11-15  |  Event: CEVO Main Season 13  | (BO3) WASD vs. Movistar Riders  |  (60.0:40.0) | Total Coins: 5000
Date: 2017-11-15  |  Event: Mother Russia  | (BO1) GameAgents vs. Nexus  |  (40.0:60.0) | Total Coins: 5000
Date: 2017-11-15  |  Event: CEVO Main Season 13  | (BO3) Pompa Team vs. PURSI$EURA  |  (55.0:45.0) | Total Coins: 5000
Date: 2017-11-15  |  Event: Skinhub CS:GO Championship  | (BO3) North Academy vs. Red Reserve  |  (73.3:26.7) | Total Coins: 7500
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) SK vs. Astralis  |  (60.0:40.0) | Total Coins: 15000
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) NiP vs. Cloud9  |  (45.0:55.0) | Total Coins: 15000
Date: 2017-11-15  |  Event: CEVO Main Season 13  | (BO3) Nexus vs. ZetMac  |  (65.0:35.0) | Total Coins: 5000
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) EnVyUs vs. Cloud9  |  (40.0:60.0) | Total Coins: 15000
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) NiP vs. TheMongolz  |  (65.0:35.0) | Total Coins: 15000
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) EnVyUs vs. NiP  |  (43.0:57.0) | Total Coins: 15000
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) Cloud9 vs. SK  |  (40.0:60.0) | Total Coins: 15000
Date: 2017-11-15  |  Event: Intel Extreme Masters 2017  | (BO1) TheMongolz vs. Astralis  |  (35.0:65.0) | Total Coins: 15000
Date: 2017-11-16  |  Event: ESEA MDL Season 26  | (BO1) Grayhound vs. Dark Sided  |  (60.0:40.0) | Total Coins: 5000
Date: 2017-11-16  |  Event: Legend Series  | (BO1) Venatores vs. Windigo  |  (45.0:55.0) | Total Coins: 5000
Date: 2017-11-16  |  Event: Legend Series  | (BO1) Nexus vs. WASD  |  (60.0:40.0) | Total Coins: 5000

Load Ratings Model


In [5]:
TEAM_SET = 'all_time'

teams = np.load('wl_model/saved_model/'+TEAM_SET+'/teams.npy')
maps = np.load('wl_model/saved_model/'+TEAM_SET+'/maps.npy')
periods = np.load('wl_model/saved_model/'+TEAM_SET+'/periods.npy')
h_teams = pd.read_csv('wl_model/hltv_csv/teams_w_ranking.csv').set_index('ID').loc[teams]
h_teams = fix_teams(h_teams)
h_teams.Name = h_teams.Name.str.lower()
h_teams_filt = h_teams.loc[teams]

rating_model = prep_pymc_time_model(len(teams), len(maps), len(periods))
trace = pm.backends.text.load('wl_model/saved_model/'+TEAM_SET+'/trace', model=rating_model)

Ban/Pick Predictions


In [6]:
h_bp = pd.read_csv('wl_model/hltv_csv/picksAndBans.csv').set_index('Match ID')
h_matches = pd.read_csv('wl_model/hltv_csv/matchResults.csv').set_index('Match ID')
h_matches['Date'] = pd.to_datetime(h_matches['Date'])
h_matches = h_matches[h_matches['Date'] >= dt.datetime(2017,1,1)]
h_bp = h_bp.join(h_matches[['Date']], how='left')
h_bp['Date'] = pd.to_datetime(h_bp['Date'])
h_bp = h_bp[h_bp['Date'] >= dt.datetime(2017,1,1)]

In [7]:
def model_mp(train, t1, t2):
    tab = train[train['Team'].isin([t1, t2])].groupby(['Team', ' Pick Type', 'Map'])['Date'].count().unstack([' Pick Type', 'Team']).fillna(0)
    return (tab/tab.sum(axis=0)).mean(level=0,axis=1)# get average

def model_played(train, t1, t2):
    a = train[train['Team 1 ID'].isin([t1,t2])].groupby(['Team 1 ID', 'Map'])['Date'].count()
    b = train[train['Team 2 ID'].isin([t1,t2])].groupby(['Team 2 ID', 'Map'])['Date'].count()
    c = pd.DataFrame([a,b], index=['a','b']).T.fillna(0)
    c = (c['a']+c['b']).unstack(level=0).fillna(0)
    return (c/c.sum()).mean(axis=1)

def predict_map(func, data, t1, t2):
    res = func(data, t1, t2)
    return res.loc[res.index != 'Default'].sort_values(ascending=False)

Bet Predictions


In [8]:
money = 4500.
bet_games['1'] = bet_games['1'].str.lower().replace({'ex-denial': 'denial', 'red reserve': 'japaleno'})
bet_games['2'] = bet_games['2'].str.lower().replace({'ex-denial': 'denial', 'red reserve': 'japaleno'})
matches = bet_games[bet_games['1'].isin(h_teams_filt.Name.values) & bet_games['2'].isin(h_teams_filt.Name.values)].drop_duplicates()
def sig(x):
    return 1 / (1 + np.exp(-x))
def abs_norm_interval(start,end,loc,scale):
    return (norm.cdf(end,loc,scale) - norm.cdf(start,loc,scale)) + (norm.cdf(-1*start,loc,scale) - norm.cdf(-1*end,loc,scale))


per_ind = np.where(periods == pd.Period(dt.datetime.today(), 'M')-1)[0][0]
t_rating = trace['rating_%s'%per_ind]
t_map_rating = trace['rating_%s | map' %per_ind]
t_alpha = 0.3485
for i,v in matches.iterrows():
    t1_id = h_teams_filt[h_teams_filt.Name == v['1']].index[0]; t1_ind = np.where(teams == t1_id)[0][0];
    t2_id = h_teams_filt[h_teams_filt.Name == v['2']].index[0]; t2_ind = np.where(teams == t2_id)[0][0];
    trace_1 = t_rating[:,t1_ind]; trace_2 = t_rating[:,t2_ind]
    mr_1 = trace_1.mean(); mr_2 = trace_2.mean();
    diff = trace_1-trace_2
    p_wl = 0.5*np.tanh(diff)+0.5
    wr_25 = np.percentile(p_wl, 25); wr_75 = np.percentile(p_wl, 75)
    kelly_pct_1 = ((v['o1']*np.percentile(p_wl, 47)-(1.-np.percentile(p_wl, 47)))/v['o1'])*0.10
    kelly_pct_2 = ((v['o2']*(1.-np.percentile(p_wl, 47))-(np.percentile(p_wl, 47)))/v['o2'])*0.10
    print('%s (%.3f) vs %s (%.3f) - P:%.2f%% - %.2f%% - %.2f%%  -  K: %.1f%% (%i) - %.1f%% (%i)' % 
          (v['1'], mr_1, v['2'], mr_2,  wr_25*100, v['wr'], wr_75*100, kelly_pct_1*100., 
           kelly_pct_1*money, kelly_pct_2*100., kelly_pct_2*money))


spirit academy (0.398) vs planetkey (0.062) - P:55.26% - 69.41% - 75.40%  -  K: 4.1% (184) - 1.5% (67)
valiance (0.907) vs alternate attax (0.363) - P:62.05% - 42.21% - 83.83%  -  K: 6.2% (278) - -1.6% (-70)
fragsters (0.866) vs extatus (0.881) - P:41.31% - 39.97% - 56.80%  -  K: 2.7% (122) - 2.3% (104)
aaa (0.737) vs ttfu (-0.176) - P:78.04% - 72.27% - 91.52%  -  K: 7.5% (335) - -0.9% (-40)
space soldiers (1.412) vs aaa (0.737) - P:70.21% - 76.53% - 86.47%  -  K: 6.3% (281) - 0.3% (12)
envyus (1.218) vs sk (1.510) - P:28.54% - 27.94% - 43.68%  -  K: 1.7% (75) - 4.0% (179)
nip (1.279) vs astralis (1.476) - P:33.64% - 60.31% - 47.45%  -  K: 0.3% (15) - 4.5% (200)
virtus.pro (1.089) vs superjymy (0.189) - P:74.46% - 78.38% - 92.80%  -  K: 7.3% (329) - -0.3% (-14)
ago (1.160) vs singularity (0.778) - P:60.73% - 69.03% - 75.39%  -  K: 4.5% (200) - 1.2% (54)
cloud9 (1.566) vs themongolz (0.499) - P:85.20% - 75.81% - 92.50%  -  K: 8.0% (361) - -1.0% (-46)
nip (1.279) vs sk (1.510) - P:31.59% - 34.03% - 46.48%  -  K: 1.7% (75) - 3.7% (166)
envyus (1.218) vs themongolz (0.499) - P:74.26% - 73.27% - 86.12%  -  K: 6.6% (296) - -0.2% (-7)
cloud9 (1.566) vs astralis (1.476) - P:46.98% - 41.64% - 61.88%  -  K: 3.5% (155) - 1.5% (66)
sk (1.510) vs themongolz (0.499) - P:83.46% - 75.44% - 91.85%  -  K: 7.8% (352) - -0.9% (-41)
envyus (1.218) vs astralis (1.476) - P:30.38% - 37.62% - 45.35%  -  K: 1.3% (59) - 4.0% (179)
g2 (1.536) vs faze (1.651) - P:36.39% - 39.47% - 52.43%  -  K: 2.1% (96) - 3.0% (134)
optic (1.340) vs gambit (1.292) - P:44.11% - 52.56% - 60.77%  -  K: 2.6% (116) - 2.4% (109)
soar (0.550) vs beacon (0.224) - P:54.44% - 60.00% - 75.95%  -  K: 4.4% (197) - 0.9% (40)
optic (1.340) vs faze (1.651) - P:28.45% - 40.00% - 42.12%  -  K: 0.8% (35) - 4.5% (203)
liquid (1.390) vs renegades (1.204) - P:52.02% - 59.60% - 66.56%  -  K: 3.3% (146) - 1.9% (84)
gale force (0.554) vs gx (0.600) - P:35.00% - 60.00% - 61.55%  -  K: 1.6% (72) - 3.3% (150)
rise nation (0.752) vs mythic (0.324) - P:61.38% - 60.00% - 77.72%  -  K: 5.1% (228) - 0.3% (14)
liquid (1.390) vs gambit (1.292) - P:46.59% - 47.63% - 63.66%  -  K: 3.1% (140) - 1.9% (83)
g2 (1.536) vs renegades (1.204) - P:58.48% - 60.00% - 73.15%  -  K: 4.4% (197) - 0.9% (41)
faze (1.651) vs renegades (1.204) - P:64.11% - 60.00% - 77.30%  -  K: 5.2% (235) - 0.2% (7)
g2 (1.536) vs gambit (1.292) - P:53.37% - 65.71% - 69.93%  -  K: 3.6% (160) - 1.8% (80)
riotous (0.174) vs grayhound (1.075) - P:7.38% - 35.00% - 24.11%  -  K: -1.8% (-79) - 7.9% (354)
athletico (0.561) vs kings (1.140) - P:18.18% - 40.00% - 30.78%  -  K: -0.8% (-34) - 6.3% (283)
forze (0.073) vs goodjob (0.187) - P:35.66% - 60.00% - 53.78%  -  K: 0.9% (40) - 4.0% (177)
goodjob (0.187) vs tricked (0.909) - P:14.17% - 39.60% - 25.20%  -  K: -1.4% (-61) - 7.0% (315)
big (1.051) vs natus vincere (1.042) - P:42.33% - 40.06% - 59.01%  -  K: 2.9% (128) - 2.2% (96)
havu (0.947) vs envyus academy (0.419) - P:67.25% - 59.41% - 80.37%  -  K: 5.7% (257) - -0.3% (-13)
wasd (-0.129) vs movistar riders (-0.106) - P:27.85% - 60.00% - 71.60%  -  K: 1.3% (58) - 3.6% (162)
gameagents (0.093) vs nexus (0.591) - P:19.67% - 40.00% - 36.14%  -  K: -0.4% (-16) - 5.8% (263)
north academy (0.792) vs japaleno (0.605) - P:49.07% - 73.33% - 68.62%  -  K: 2.7% (123) - 2.6% (118)
sk (1.510) vs astralis (1.476) - P:44.14% - 60.00% - 59.36%  -  K: 2.1% (93) - 2.9% (132)
nip (1.279) vs cloud9 (1.566) - P:28.98% - 45.00% - 43.74%  -  K: 0.6% (27) - 4.5% (204)
envyus (1.218) vs cloud9 (1.566) - P:26.28% - 40.00% - 40.42%  -  K: 0.5% (23) - 4.8% (217)
nip (1.279) vs themongolz (0.499) - P:76.90% - 65.00% - 87.18%  -  K: 7.0% (316) - -1.1% (-48)
envyus (1.218) vs nip (1.279) - P:39.79% - 43.00% - 54.10%  -  K: 2.3% (103) - 2.8% (124)
cloud9 (1.566) vs sk (1.510) - P:44.78% - 40.00% - 60.17%  -  K: 3.3% (148) - 1.7% (74)
themongolz (0.499) vs astralis (1.476) - P:8.96% - 35.00% - 17.36%  -  K: -1.9% (-85) - 8.0% (361)
grayhound (1.075) vs dark sided (0.302) - P:76.14% - 60.00% - 87.06%  -  K: 7.1% (319) - -1.5% (-65)
nexus (0.591) vs wasd (-0.129) - P:64.16% - 60.00% - 90.96%  -  K: 6.7% (299) - -1.1% (-48)

In [ ]:
PRINT_RD_DIFF = False
for i,v in matches.iterrows():
    t1_id = h_teams_filt[h_teams_filt.Name == v['1']].index[0]; t1_ind = np.where(teams == t1_id)[0][0];
    t2_id = h_teams_filt[h_teams_filt.Name == v['2']].index[0]; t2_ind = np.where(teams == t2_id)[0][0];
    print('---------- %s vs %s ---------------------------------' % (v['1'], v['2']))
    pred_maps = predict_map(model_played, h_matches, t1_id, t2_id)
    pred_maps = pred_maps/pred_maps.sum()
    for m,s in pred_maps.iteritems():
        m_ind = np.where(maps == m)[0][0]
        trace_1 = t_map_rating[:,m_ind,t1_ind]; trace_2 = t_map_rating[:,m_ind,t2_ind]
        mr_1 = trace_1.mean(); mr_2 = trace_2.mean();
        diff = trace_1-trace_2
        p_wl = sig(diff)
        wr_25 = np.percentile(p_wl, 25); wr_75 = np.percentile(p_wl, 75)
        kappa = 32*sig(t_alpha*diff)-16.
        kelly_pct_1 = ((v['o1']*np.percentile(p_wl, 45)-(1.-np.percentile(p_wl, 45)))/v['o1'])*0.1
        kelly_pct_2 = ((v['o2']*(1.-np.percentile(p_wl, 45))-(np.percentile(p_wl, 45)))/v['o2'])*0.1
        print('    Map: %s (%.2f)  -  %s (%.3f) vs %s (%.3f) - P:%.2f%% - %.2f%% - %.2f%%  -  K: %.1f%% (%i) - %.1f%% (%i)' % 
             (m, s*100., v['1'], mr_1, v['2'], mr_2,  wr_25*100, v['wr'], wr_75*100, kelly_pct_1*100., 
               kelly_pct_1*money, kelly_pct_2*100., kelly_pct_2*money))
        
        if(PRINT_RD_DIFF):
            p_sc = [abs_norm_interval(x[0],x[1],kappa,trace['sigma'][:,m_ind]) for x in [[1.5,3.5],[3.5,5.5],[5.5,7.5],[7.5,9.5],[9.5,16]]]
            for i,sd in enumerate(['2 - 3 Rounds', '4 - 5 rounds', '6 - 7 rounds', '8 - 9 rounds', '10 rounds or more']):
                sc_25 = np.percentile(p_sc[i], 25); sc_75 = np.percentile(p_sc[i], 75)
                print('      %s : %.2f%% - %.2f%%' % (sd, sc_25*100, sc_75*100))

In [ ]:
np.inner(np.ones((8,25)), np.ones((8,25))).shape

In [ ]:
np.ones((8,25)).flatten().shape

In [ ]:
np.zeros((8000, 200))

In [ ]:
plt.ylim(0,1.2)
sns.kdeplot(trace_1, shade=True, alpha=0.65, legend=True, label=v['1'])
sns.kdeplot(trace_2, shade=True, alpha=0.65, legend=True, label=v['2'])

In [ ]:
h_bp.groupby('Match ID').first().count()

In [ ]:
h_bp

In [ ]: