LoL AI Model Part 1: Initial EDA and First MDP

AI in Video Games: Improving Decision Making in League of Legends using Real Match Statistics and Personal Preferences

Part 1: Initial Exploratory Analysis and First Markov Decision Process Model

Motivations and Objectives

League of Legends is a team oriented video game where on two team teams (with 5 players in each) compete for objectives and kills. Gaining an advantage enables the players to become stronger (obtain better items and level up faster) than their opponents and, as their advantage increases, the likelihood of winning the game also increases. We therefore have a sequence of events dependent on previous events that lead to one team destroying the other’s base and winning the game.

Sequences like this being modelled statistically is nothing new; for years now researchers have considered how this is applied in sports, such as basketball (https://arxiv.org/pdf/1507.01816.pdf), where a sequence of passing, dribbling and foul plays lead to a team obtaining or losing points. The aim of research such as this one mentioned is to provide more detailed insight beyond a simple box score (number of points or kill gained by player in basketball or video games respectively) and consider how teams perform when modelled as a sequence of events connected in time.

Modelling the events in this way is even more important in games such as League of Legends as taking objectives and kills lead towards both an item and level advantage. For example, a player obtaining the first kill of the game nets them gold that can be used to purchase more powerful items. With this item they are then strong enough to obtain more kills and so on until they can lead their team to a win. Facilitating a lead like this is often referred to as ‘snowballing’ as the players cumulatively gain advantages but often games are not this one sided and objects and team plays are more important.

The aim of this is project is simple; can we calculate the next best event given what has occurred previously in the game so that the likelihood of eventually leading to a win increases based on real match statistics?

However, there are many factors that lead to a player’s decision making in a game that cannot be easily measured. No how matter how much data collected, the amount of information a player can capture is beyond any that a computer can detect (at least for now!). For example, players may be over or underperforming in this game or may simply have a preference for the way they play (often defined by the types of characters they play). Some players will naturally be more aggressive and look for kills while others will play passively and push for objectives instead. Therefore, we further develop our model to allow the player to adjust the recommended play on their preferences.

Import Packages and Data

In [1]:

import pandas as pd
import numpy as np
import datetime
import matplotlib.pyplot as plt
import seaborn as sns
from IPython.display import Image
import math
from scipy.stats import kendalltau

import timeit

import warnings
warnings.filterwarnings('ignore')

In [2]:

#kills = pd.read_csv('C:\\Users\\Phil\\Documents\\LoL Model\\kills.csv')
#matchinfo = pd.read_csv('C:\\Users\\Phil\\Documents\\LoL Model\\matchinfo.csv')
#monsters = pd.read_csv('C:\\Users\\Phil\\Documents\\LoL Model\\monsters.csv')
#structures = pd.read_csv('C:\\Users\\Phil\\Documents\\LoL Model\\structures.csv')

kills = pd.read_csv('../input/kills.csv')
matchinfo = pd.read_csv('../input/matchinfo.csv')
monsters = pd.read_csv('../input/monsters.csv')
structures = pd.read_csv('../input/structures.csv')

Pre-Processing and Exploratory Analysis

In [3]:

matchinfo.head()

Out[3]:

 LeagueYearSeasonTypeblueTeamTagbResultrResultredTeamTaggamelengthblueTopblueTopChampblueJungleblueJungleChampblueMiddleblueMiddleChampblueADCblueADCChampblueSupportblueSupportChampredTopredTopChampredJungleredJungleChampredMiddleredMiddleChampredADCredADCChampredSupportredSupportChampAddress
0NALCS2015SpringSeasonTSM10C940DyrusIreliaSantorinRekSaiBjergsenAhriWildTurtleJinxLustboyJannaBallsGnarMeteosEliseHaiFizzSneakySivirLemonNationThreshhttp://matchhistory.na.leagueoflegends.com/en/...
1NALCS2015SpringSeasonCST01DIG38CrisGnarImpalerRengarJesizAhriMashCaitlynSheepLeonaGamsuIreliaCrumbzzJarvanIVShiphturAzirCoreJJCorkiKiWiKiDAnniehttp://matchhistory.na.leagueoflegends.com/en/...
2NALCS2015SpringSeasonWFX10GV40FlareszRenektonShorterACERengarPobelterFizzAltecSivirGleebAnnieHauntzerSionSaintviciousLeeSinKeaneAzirCopCorkiBunnyFuFuuJannahttp://matchhistory.na.leagueoflegends.com/en/...
3NALCS2015SpringSeasonTIP01TL41RhuxIreliaRushJarvanIVXiaoWeiXiaoLeblancApolloSivirAdrianThreshQuasGnarIWDominateNunuFenixLuluKEITHKogMawXpecialJannahttp://matchhistory.na.leagueoflegends.com/en/...
4NALCS2015SpringSeasonCLG10T835BennyGnarXmithieJarvanIVLinkLissandraDoubleliftTristanaaphromooJannaCaliTrlolz8SionPorpoise8RekSaiSlooshi8LuluMaplestreet8CorkiDodo8Anniehttp://matchhistory.na.leagueoflegends.com/en/...

In [4]:

# Add ID column based on last 16 digits in match address for simpler matching

matchinfo['id'] = matchinfo['Address'].astype(str).str[-16:]
kills['id'] = kills['Address'].astype(str).str[-16:]
monsters['id'] = monsters['Address'].astype(str).str[-16:]
structures['id'] = structures['Address'].astype(str).str[-16:]
matchinfo.head()

Out[4]:

 LeagueYearSeasonTypeblueTeamTagbResultrResultredTeamTaggamelengthblueTopblueTopChampblueJungleblueJungleChampblueMiddleblueMiddleChampblueADCblueADCChampblueSupportblueSupportChampredTopredTopChampredJungleredJungleChampredMiddleredMiddleChampredADCredADCChampredSupportredSupportChampAddressid
0NALCS2015SpringSeasonTSM10C940DyrusIreliaSantorinRekSaiBjergsenAhriWildTurtleJinxLustboyJannaBallsGnarMeteosEliseHaiFizzSneakySivirLemonNationThreshhttp://matchhistory.na.leagueoflegends.com/en/...fbb300951ad8327c
1NALCS2015SpringSeasonCST01DIG38CrisGnarImpalerRengarJesizAhriMashCaitlynSheepLeonaGamsuIreliaCrumbzzJarvanIVShiphturAzirCoreJJCorkiKiWiKiDAnniehttp://matchhistory.na.leagueoflegends.com/en/...055b17da8456fdc8
2NALCS2015SpringSeasonWFX10GV40FlareszRenektonShorterACERengarPobelterFizzAltecSivirGleebAnnieHauntzerSionSaintviciousLeeSinKeaneAzirCopCorkiBunnyFuFuuJannahttp://matchhistory.na.leagueoflegends.com/en/...8e8a9b58df366e2d
3NALCS2015SpringSeasonTIP01TL41RhuxIreliaRushJarvanIVXiaoWeiXiaoLeblancApolloSivirAdrianThreshQuasGnarIWDominateNunuFenixLuluKEITHKogMawXpecialJannahttp://matchhistory.na.leagueoflegends.com/en/...0ed1cd0e0e57329c
4NALCS2015SpringSeasonCLG10T835BennyGnarXmithieJarvanIVLinkLissandraDoubleliftTristanaaphromooJannaCaliTrlolz8SionPorpoise8RekSaiSlooshi8LuluMaplestreet8CorkiDodo8Anniehttp://matchhistory.na.leagueoflegends.com/en/...f932becf86175f38

In [5]:

# Dragon became multiple types in patch v6.9 (http://leagueoflegends.wikia.com/wiki/V6.9) 
# so we remove and games before this change occured and only use games with the new dragon system
monsters['Type'].unique()

Out[5]:

array(['DRAGON', 'EARTH_DRAGON', 'WATER_DRAGON', 'AIR_DRAGON',
       'FIRE_DRAGON', 'ELDER_DRAGON', 'BARON_NASHOR', 'RIFT_HERALD'],
      dtype=object)

In [6]:

old_dragon_id = monsters[ monsters['Type']=="DRAGON"]['id'].unique()
old_dragon_id

Out[6]:

array(['fbb300951ad8327c', '055b17da8456fdc8', '8e8a9b58df366e2d', ...,
       'd2eaf13bcbb3c021', '035394afd3bfc218', '94537494cdbc8b4c'],
      dtype=object)

In [7]:

monsters = monsters[ ~monsters['id'].isin(old_dragon_id)]
monsters[monsters['Type']=="DRAGON"]

Out[7]:

In [8]:

# Again remove old games, we have some missing values (probably for other events) so remove this
# Create a column for the minute in which the kill took place
# Reassign the team column to a simpler Red/Blue accordingly for matching with other tables

kills = kills[ ~kills['id'].isin(old_dragon_id)]
kills = kills[ kills['Time']>0]

kills['Minute'] = kills['Time'].astype(int)

kills['Team'] = np.where( kills['Team']=="rKills","Red","Blue")
kills.head()

Out[8]:

 AddressTeamTimeVictimKillerAssist_1Assist_2Assist_3Assist_4x_posy_posidMinute
4462http://matchhistory.na.leagueoflegends.com/en/...Blue6.032CLG HuhiTSM SvenskerenTSM BjergsenNaNNaNNaN7825866655109b5a7a91ae876
4463http://matchhistory.na.leagueoflegends.com/en/...Blue9.428CLG HuhiTSM BiofrostTSM BjergsenTSM DoubleliftNaNNaN8728875155109b5a7a91ae879
4464http://matchhistory.na.leagueoflegends.com/en/...Blue9.780CLG XmithieTSM BjergsenTSM HauntzerTSM SvenskerenNaNNaN8655117255109b5a7a91ae879
4465http://matchhistory.na.leagueoflegends.com/en/...Blue10.252CLG StixxayTSM DoubleliftTSM BiofrostNaNNaNNaN36211160755109b5a7a91ae8710
4466http://matchhistory.na.leagueoflegends.com/en/...Blue12.993CLG DarshanTSM DoubleliftTSM BiofrostNaNNaNNaN56741290355109b5a7a91ae8712

In [9]:

# For the Kills table, we need decided to group by the minute in which the kills took place and averaged 
# the time of the kills which we use later for the order of events

f = {'Time':['mean','count']}

killsGrouped = kills.groupby( ['id','Team','Minute'] ).agg(f).reset_index()
killsGrouped.columns = ['id','Team','Minute','Time Avg','Count']
killsGrouped = killsGrouped.sort_values(by=['id','Minute'])
killsGrouped.head(13)

Out[9]:

 idTeamMinuteTime AvgCount
40001f4374a03c133Red44.63501
50001f4374a03c133Red66.06401
00001f4374a03c133Blue88.19401
60001f4374a03c133Red1010.47801
70001f4374a03c133Red1717.38702
10001f4374a03c133Blue1818.09801
80001f4374a03c133Red1818.06702
90001f4374a03c133Red2121.79001
20001f4374a03c133Blue2222.51252
100001f4374a03c133Red2222.45785
30001f4374a03c133Blue3131.16852
110001f4374a03c133Red3131.38801
120001f4374a03c133Red3232.76345

In [10]:

# Repeat similar steps for the structures table

structures = structures[ ~structures['id'].isin(old_dragon_id)]
structures = structures[ structures['Time']>0]

structures['Minute'] = structures['Time'].astype(int)
structures['Team'] = np.where(structures['Team']=="bTowers","Blue",
                        np.where(structures['Team']=="binhibs","Blue","Red"))
structures2 = structures.sort_values(by=['id','Minute'])
structures2.head(13)

Out[10]:

 AddressTeamTimeLaneTypeidMinute
6740http://matchhistory.na.leagueoflegends.com/en/...Blue11.182TOP_LANEOUTER_TURRET0001f4374a03c13311
57600http://matchhistory.na.leagueoflegends.com/en/...Red11.006BOT_LANEOUTER_TURRET0001f4374a03c13311
6741http://matchhistory.na.leagueoflegends.com/en/...Blue16.556BOT_LANEOUTER_TURRET0001f4374a03c13316
57601http://matchhistory.na.leagueoflegends.com/en/...Red16.145TOP_LANEOUTER_TURRET0001f4374a03c13316
57598http://matchhistory.na.leagueoflegends.com/en/...Red18.378MID_LANEOUTER_TURRET0001f4374a03c13318
57602http://matchhistory.na.leagueoflegends.com/en/...Red24.943MID_LANEINNER_TURRET0001f4374a03c13324
57599http://matchhistory.na.leagueoflegends.com/en/...Red25.463TOP_LANEINNER_TURRET0001f4374a03c13325
57597http://matchhistory.na.leagueoflegends.com/en/...Red26.330BOT_LANEINNER_TURRET0001f4374a03c13326
57594http://matchhistory.na.leagueoflegends.com/en/...Red33.153MID_LANEBASE_TURRET0001f4374a03c13333
57595http://matchhistory.na.leagueoflegends.com/en/...Red33.326MID_LANENEXUS_TURRET0001f4374a03c13333
57596http://matchhistory.na.leagueoflegends.com/en/...Red33.408MID_LANENEXUS_TURRET0001f4374a03c13333
111295http://matchhistory.na.leagueoflegends.com/en/...Red33.222MID_LANEINHIBITOR0001f4374a03c13333
35900http://matchhistory.oce.leagueoflegends.com/en...Blue12.603BOT_LANEOUTER_TURRET0016710a48fdd46d12

In [11]:

# Merge the two together
kills_structures = killsGrouped.merge(structures2[['id','Minute','Team','Time','Lane','Type']],
                                      on=['id','Minute','Team'],how='outer')
kills_structures.head(20)

Out[11]:

 idTeamMinuteTime AvgCountTimeLaneType
00001f4374a03c133Red44.63501.0NaNNaNNaN
10001f4374a03c133Red66.06401.0NaNNaNNaN
20001f4374a03c133Blue88.19401.0NaNNaNNaN
30001f4374a03c133Red1010.47801.0NaNNaNNaN
40001f4374a03c133Red1717.38702.0NaNNaNNaN
50001f4374a03c133Blue1818.09801.0NaNNaNNaN
60001f4374a03c133Red1818.06702.018.378MID_LANEOUTER_TURRET
70001f4374a03c133Red2121.79001.0NaNNaNNaN
80001f4374a03c133Blue2222.51252.0NaNNaNNaN
90001f4374a03c133Red2222.45785.0NaNNaNNaN
100001f4374a03c133Blue3131.16852.0NaNNaNNaN
110001f4374a03c133Red3131.38801.0NaNNaNNaN
120001f4374a03c133Red3232.76345.0NaNNaNNaN
130016710a48fdd46dBlue55.97101.0NaNNaNNaN
140016710a48fdd46dRed88.37501.0NaNNaNNaN
150016710a48fdd46dBlue1111.97201.0NaNNaNNaN
160016710a48fdd46dRed1111.91301.0NaNNaNNaN
170016710a48fdd46dRed1313.10301.0NaNNaNNaN
180016710a48fdd46dBlue1717.20701.0NaNNaNNaN
190016710a48fdd46dRed1717.91401.0NaNNaNNaN

In [12]:

# Again repeat same steps, we also map the types of dragon to a simpler 'Dragon' label

monsters = monsters[ ~monsters['id'].isin(old_dragon_id)]
monsters['Type2'] = np.where( monsters['Type']=="FIRE_DRAGON", "DRAGON",
                    np.where( monsters['Type']=="EARTH_DRAGON","DRAGON",
                    np.where( monsters['Type']=="WATER_DRAGON","DRAGON",       
                    np.where( monsters['Type']=="AIR_DRAGON","DRAGON",   
                             monsters['Type']))))

monsters = monsters[ monsters['Time']>0]

monsters['Minute'] = monsters['Time'].astype(int)

monsters['Team'] = np.where( monsters['Team']=="bDragons","Blue",
                   np.where( monsters['Team']=="bHeralds","Blue",
                   np.where( monsters['Team']=="bBarons", "Blue", 
                           "Red")))



monsters.head()

Out[12]:

 AddressTeamTimeTypeidType2Minute
696http://matchhistory.na.leagueoflegends.com/en/...Blue23.444EARTH_DRAGON55109b5a7a91ae87DRAGON23
697http://matchhistory.na.leagueoflegends.com/en/...Blue31.069WATER_DRAGON55109b5a7a91ae87DRAGON31
698http://matchhistory.na.leagueoflegends.com/en/...Blue16.419AIR_DRAGON55109b5a7a91ae87DRAGON16
699http://matchhistory.na.leagueoflegends.com/en/...Blue32.022EARTH_DRAGONe147296c928da5b4DRAGON32
700http://matchhistory.na.leagueoflegends.com/en/...Blue25.304WATER_DRAGONe147296c928da5b4DRAGON25

In [13]:

# Merge the monsters to our previously merged table
# This provides us with a table that has each event seperated by columns depending on what type of event it was
kills_structures_monsters = kills_structures.merge(monsters[['id','Minute','Team','Time','Type2']], on=['id','Minute'],how='outer')
kills_structures_monsters = kills_structures_monsters.sort_values(by=['id','Minute'])
kills_structures_monsters.head(5)

Out[13]:

 idTeam_xMinuteTime AvgCountTime_xLaneTypeTeam_yTime_yType2
00001f4374a03c133Red44.6351.0NaNNaNNaNNaNNaNNaN
10001f4374a03c133Red66.0641.0NaNNaNNaNNaNNaNNaN
20001f4374a03c133Blue88.1941.0NaNNaNNaNNaNNaNNaN
30001f4374a03c133Red1010.4781.0NaNNaNNaNNaNNaNNaN
945020001f4374a03c133Blue11NaNNaN11.182TOP_LANEOUTER_TURRETRed11.261DRAGON

In [14]:

# Although this is a good start, information is repeated on the rows if multiple 
# events occured in the same minute.
#
# Therefore, I decided to let each event have its own row by stacking the tables
# on top of one another. We then add a more detailed time column and sort by this 
# so we know exactly which event came first (allowing for some errors with kill time
# being averaged).


stackedData = killsGrouped.append(structures2)
stackedData = stackedData.append(monsters[['id','Address','Team','Minute','Time','Type2']])

stackedData['Time2'] = stackedData['Time'].fillna(stackedData['Time Avg'])

stackedData = stackedData.sort_values(by=['id','Time2'])

stackedData['EventNum'] = stackedData.groupby('id').cumcount()+1

stackedData = stackedData[['id','EventNum','Team','Minute','Time2','Count','Type','Lane','Type2']]

stackedData.columns = ['id','EventNum','Team','Minute','Time','KillCount','StructType','StructLane','Monster']

stackedData.head(5)

Out[14]:

 idEventNumTeamMinuteTimeKillCountStructTypeStructLaneMonster
40001f4374a03c1331Red44.6351.0NaNNaNNaN
50001f4374a03c1332Red66.0641.0NaNNaNNaN
00001f4374a03c1333Blue88.1941.0NaNNaNNaN
60001f4374a03c1334Red1010.4781.0NaNNaNNaN
576000001f4374a03c1335Red1111.006NaNOUTER_TURRETBOT_LANENaN

In [15]:

# We then add an 'Event' column to merge the columns into one, where kills are now
# simple labelled as 'KILLS'

stackedData['Event'] = np.where(stackedData['KillCount']>0,"KILLS",None)
stackedData['Event'] = stackedData['Event'].fillna(stackedData['StructType'])
stackedData['Event'] = stackedData['Event'].fillna(stackedData['Monster'])

                        

stackedData.head(10)

Out[15]:

 idEventNumTeamMinuteTimeKillCountStructTypeStructLaneMonsterEvent
40001f4374a03c1331Red44.6351.0NaNNaNNaNKILLS
50001f4374a03c1332Red66.0641.0NaNNaNNaNKILLS
00001f4374a03c1333Blue88.1941.0NaNNaNNaNKILLS
60001f4374a03c1334Red1010.4781.0NaNNaNNaNKILLS
576000001f4374a03c1335Red1111.006NaNOUTER_TURRETBOT_LANENaNOUTER_TURRET
67400001f4374a03c1336Blue1111.182NaNOUTER_TURRETTOP_LANENaNOUTER_TURRET
240270001f4374a03c1337Red1111.261NaNNaNNaNDRAGONDRAGON
425650001f4374a03c1338Red1515.777NaNNaNNaNRIFT_HERALDRIFT_HERALD
576010001f4374a03c1339Red1616.145NaNOUTER_TURRETTOP_LANENaNOUTER_TURRET
67410001f4374a03c13310Blue1616.556NaNOUTER_TURRETBOT_LANENaNOUTER_TURRET

In [16]:

stackedData['Event'].unique()

Out[16]:

array(['KILLS', 'OUTER_TURRET', 'DRAGON', 'RIFT_HERALD', 'BARON_NASHOR',
       'INNER_TURRET', 'BASE_TURRET', 'INHIBITOR', 'NEXUS_TURRET',
       'ELDER_DRAGON'], dtype=object)

In [17]:

NumEventAnalysis = stackedData[['id','EventNum']].groupby('id').max().reset_index()

NumEventAnalysis2 = NumEventAnalysis.groupby('EventNum').count().reset_index()

NumEventAnalysis2.head()

Out[17]:

 EventNumid
0141
1163
2174
3187
41912

In [18]:

plt.bar(NumEventAnalysis2['EventNum'],NumEventAnalysis2['id'] ,alpha=0.3)
plt.plot(NumEventAnalysis2['EventNum'],NumEventAnalysis2['id'])
plt.title('Distribution of Number of Events in Each Match (EXACT)')
plt.xlim(0,100)
plt.xlabel("Number of Events")
plt.ylabel("Number of Matches")
plt.show()

In [19]:

sns.distplot(NumEventAnalysis['EventNum'],bins=65)
plt.title('Distribution of Number of Events in Each Match (NORMAL DIST)')
plt.xlim(0,100)
plt.xlabel("Number of Events")
plt.ylabel("Number of Matches")
plt.show()

In [20]:

print("The max number of events for any team in a single game is:",NumEventAnalysis['EventNum'].max())
print("The min number of events for any team in a single game is:",NumEventAnalysis['EventNum'].min())
The max number of events for any team in a single game is: 79
The min number of events for any team in a single game is: 14

In [21]:

# We then create a table with just the unique match ids that we will use to merge our tables to shortly
matchevents = pd.DataFrame(stackedData['id'].unique())
matchevents.columns = ['id']

matchevents.head()

Out[21]:

 id
00001f4374a03c133
10016710a48fdd46d
20016c9df37278448
30021b45647424cd5
400405293fb859241

In [22]:

# WARNING: Takes a while to run

# This cell has a lot of steps but the idea is to:
#    1) Seperate the the events into each team (Red/Blue)
#    2) For each, go through each match and transpose the list of events into a single row
#    3) Stack a table that has the events for both team of the matches

bluerows = pd.DataFrame()
stackedData_blue = stackedData
stackedData_blue['EventBlue'] = np.where( stackedData_blue['Team']!="Red",stackedData_blue['Event'],np.nan)


redrows = pd.DataFrame()
stackedData_red = stackedData
stackedData_red['EventRed'] = np.where( stackedData_red['Team']=="Red",stackedData_red['Event'],np.nan)


for i in range(0,len(matchevents)):
    
    #Red Team Output
    stackedData_match_red = stackedData_red[stackedData_red['id'] == matchevents.iloc[i,0] ]
    
    redextract = stackedData_match_red.iloc[:,[1,11]]
    redextract.iloc[:,0] = redextract.iloc[:,0]-1
    redextract = redextract.set_index('EventNum')
    
    redrow = pd.DataFrame(redextract.transpose())
    redrow['id'] = (stackedData_match_red['id'].unique())
    
    redrows = redrows.append((redrow))
    redrows = redrows.reset_index(drop=True)
    
    
    
    #Blue Team Output
    stackedData_match_blue = stackedData_blue[stackedData_blue['id'] == matchevents.iloc[i,0] ]
    
    blueextract = stackedData_match_blue.iloc[:,[1,10]]
    blueextract.iloc[:,0] = blueextract.iloc[:,0]-1
    blueextract = blueextract.set_index('EventNum')
    
    bluerow = pd.DataFrame(blueextract.transpose())
    bluerow['id'] = (stackedData_match_blue['id'].unique())
    
    bluerows = bluerows.append((bluerow))
    bluerows = bluerows.reset_index(drop=True)
    
  
    

In [23]:

redrows = redrows.sort_values('id')
redrows.head(5)

Out[23]:

 0123456789101112131415161718192021222324252627282930id313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
0KILLSKILLSNaNKILLSOUTER_TURRETNaNDRAGONRIFT_HERALDOUTER_TURRETNaNKILLSDRAGONKILLSNaNOUTER_TURRETKILLSKILLSNaNBARON_NASHORDRAGONINNER_TURRETINNER_TURRETINNER_TURRETDRAGONNaNKILLSKILLSBASE_TURRETINHIBITORNEXUS_TURRETNEXUS_TURRET0001f4374a03c133NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
1NaNKILLSDRAGONKILLSNaNNaNKILLSNaNNaNKILLSKILLSNaNOUTER_TURRETNaNNaNNaNKILLSNaNKILLSOUTER_TURRETNaNKILLSOUTER_TURRETNaNKILLSNaNKILLSNaNKILLSBARON_NASHORKILLS0016710a48fdd46dINNER_TURRETINNER_TURRETINNER_TURRETNaNKILLSBASE_TURRETNaNINHIBITORKILLSNaNKILLSNEXUS_TURRETNEXUS_TURRETNaNNaNNaNKILLSINHIBITORNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2NaNNaNNaNNaNNaNNaNKILLSNaNNaNOUTER_TURRETNaNNaNKILLSNaNNaNNaNKILLSBARON_NASHOROUTER_TURRETDRAGONKILLSOUTER_TURRETINNER_TURRETINNER_TURRETBASE_TURRETINHIBITORINNER_TURRETBASE_TURRETBASE_TURRETINHIBITORNEXUS_TURRET0016c9df37278448NEXUS_TURRETNaNKILLSINHIBITORNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
3NaNKILLSKILLSKILLSNaNDRAGONRIFT_HERALDNaNOUTER_TURRETNaNOUTER_TURRETNaNDRAGONKILLSKILLSOUTER_TURRETNaNKILLSBARON_NASHORNaNDRAGONINNER_TURRETKILLSINNER_TURRETBASE_TURRETINHIBITORNaNKILLSNEXUS_TURRETBARON_NASHORINNER_TURRET0021b45647424cd5NaNDRAGONINHIBITORBASE_TURRETINHIBITORNaNNaNKILLSNaNKILLSNEXUS_TURRETNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
4KILLSNaNNaNNaNNaNOUTER_TURRETNaNNaNNaNNaNNaNNaNNaNKILLSNaNNaNOUTER_TURRETKILLSKILLSNaNOUTER_TURRETDRAGONBARON_NASHORKILLSINNER_TURRETBASE_TURRETINHIBITORKILLSINNER_TURRETKILLSBASE_TURRET00405293fb859241INHIBITORNaNKILLSINNER_TURRETNEXUS_TURRETNEXUS_TURRETKILLSNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN

In [24]:

bluerows = bluerows.sort_values('id')
bluerows.head(5)

Out[24]:

 0123456789101112131415161718192021222324252627282930id313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
0NaNNaNKILLSNaNNaNOUTER_TURRETNaNNaNNaNOUTER_TURRETNaNNaNNaNKILLSNaNNaNNaNKILLSNaNNaNNaNNaNNaNNaNKILLSNaNNaNNaNNaNNaNNaN0001f4374a03c133NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
1KILLSNaNNaNNaNKILLSOUTER_TURRETNaNDRAGONKILLSNaNNaNKILLSNaNOUTER_TURRETDRAGONKILLSNaNOUTER_TURRETNaNNaNKILLSNaNNaNKILLSNaNKILLSNaNDRAGONNaNNaNNaN0016710a48fdd46dNaNNaNNaNKILLSNaNNaNKILLSNaNNaNKILLSNaNNaNNaNKILLSKILLSDRAGONNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2KILLSKILLSKILLSOUTER_TURRETDRAGONKILLSNaNKILLSRIFT_HERALDNaNOUTER_TURRETDRAGONNaNDRAGONKILLSOUTER_TURRETNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN0016c9df37278448NaNKILLSNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
3KILLSNaNNaNNaNKILLSNaNNaNKILLSNaNKILLSNaNKILLSNaNNaNNaNNaNOUTER_TURRETNaNNaNKILLSNaNNaNNaNNaNNaNNaNKILLSNaNNaNNaNNaN0021b45647424cd5KILLSNaNNaNNaNNaNKILLSBARON_NASHORNaNKILLSNaNNaNKILLSNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
4NaNKILLSKILLSKILLSOUTER_TURRETNaNDRAGONKILLSOUTER_TURRETKILLSKILLSDRAGONOUTER_TURRETNaNKILLSDRAGONNaNNaNNaNKILLSNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN00405293fb859241NaNKILLSNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN

In [25]:

# We can now merge these two tables for each team's events in the match to
# our table with just the match ids. We also add a column for the result of 
# the red team for the match and change column names according to which team 
# made the event.



matchevents2 = matchevents.merge(redrows,how='left',on='id')
matchevents3 = matchevents2.merge(bluerows,how='left',on='id')
    

    
matchevents4 = matchevents3.merge(matchinfo[['id','rResult','gamelength']], on='id',how='left')


matchevents4.columns = ['id',
'RedEvent1','RedEvent2','RedEvent3',
'RedEvent4','RedEvent5','RedEvent6','RedEvent7',
'RedEvent8','RedEvent9','RedEvent10','RedEvent11',
'RedEvent12','RedEvent13','RedEvent14','RedEvent15',
'RedEvent16','RedEvent17','RedEvent18','RedEvent19',
'RedEvent20','RedEvent21','RedEvent22','RedEvent23',
'RedEvent24','RedEvent25','RedEvent26','RedEvent27',
'RedEvent28','RedEvent29','RedEvent30','RedEvent31',
'RedEvent32','RedEvent33','RedEvent34','RedEvent35',
'RedEvent36','RedEvent37','RedEvent38','RedEvent39',
'RedEvent40','RedEvent41','RedEvent42','RedEvent43',
'RedEvent44','RedEvent45','RedEvent46','RedEvent47',
'RedEvent48','RedEvent49','RedEvent50','RedEvent51',
'RedEvent52','RedEvent53','RedEvent54','RedEvent55',
'RedEvent56','RedEvent57','RedEvent58','RedEvent59',
'RedEvent60','RedEvent61','RedEvent62','RedEvent63',
'RedEvent64','RedEvent65','RedEvent66','RedEvent67',
'RedEvent68','RedEvent69','RedEvent70','RedEvent71',
'RedEvent72','RedEvent73','RedEvent74','RedEvent75',
'RedEvent76','RedEvent77','RedEvent78','RedEvent79',
                        
                        
'BlueEvent1','BlueEvent2','BlueEvent3','BlueEvent4',
'BlueEvent5','BlueEvent6','BlueEvent7','BlueEvent8',
'BlueEvent9','BlueEvent10','BlueEvent11','BlueEvent12',
'BlueEvent13','BlueEvent14','BlueEvent15','BlueEvent16',
'BlueEvent17','BlueEvent18','BlueEvent19','BlueEvent20',
'BlueEvent21','BlueEvent22','BlueEvent23','BlueEvent24',
'BlueEvent25','BlueEvent26','BlueEvent27','BlueEvent28',
'BlueEvent29','BlueEvent30','BlueEvent31','BlueEvent32',
'BlueEvent33','BlueEvent34','BlueEvent35','BlueEvent36',
'BlueEvent37','BlueEvent38','BlueEvent39','BlueEvent40',
'BlueEvent41','BlueEvent42','BlueEvent43','BlueEvent44',
'BlueEvent45','BlueEvent46','BlueEvent47','BlueEvent48',
'BlueEvent49','BlueEvent50','BlueEvent51','BlueEvent52',
'BlueEvent53','BlueEvent54','BlueEvent55','BlueEvent56',
'BlueEvent57','BlueEvent58','BlueEvent59','BlueEvent60',
'BlueEvent61','BlueEvent62','BlueEvent63',
'BlueEvent64','BlueEvent65','BlueEvent66','BlueEvent67',
'BlueEvent68','BlueEvent69','BlueEvent70','BlueEvent71',
'BlueEvent72','BlueEvent73','BlueEvent74','BlueEvent75',
'BlueEvent76','BlueEvent77','BlueEvent78','BlueEvent79',
                        
                        'rResult','gamelength']



matchevents4.head(20)

Out[25]:

 idRedEvent1RedEvent2RedEvent3RedEvent4RedEvent5RedEvent6RedEvent7RedEvent8RedEvent9RedEvent10RedEvent11RedEvent12RedEvent13RedEvent14RedEvent15RedEvent16RedEvent17RedEvent18RedEvent19RedEvent20RedEvent21RedEvent22RedEvent23RedEvent24RedEvent25RedEvent26RedEvent27RedEvent28RedEvent29RedEvent30RedEvent31RedEvent32RedEvent33RedEvent34RedEvent35RedEvent36RedEvent37RedEvent38RedEvent39...BlueEvent42BlueEvent43BlueEvent44BlueEvent45BlueEvent46BlueEvent47BlueEvent48BlueEvent49BlueEvent50BlueEvent51BlueEvent52BlueEvent53BlueEvent54BlueEvent55BlueEvent56BlueEvent57BlueEvent58BlueEvent59BlueEvent60BlueEvent61BlueEvent62BlueEvent63BlueEvent64BlueEvent65BlueEvent66BlueEvent67BlueEvent68BlueEvent69BlueEvent70BlueEvent71BlueEvent72BlueEvent73BlueEvent74BlueEvent75BlueEvent76BlueEvent77BlueEvent78BlueEvent79rResultgamelength
00001f4374a03c133KILLSKILLSNaNKILLSOUTER_TURRETNaNDRAGONRIFT_HERALDOUTER_TURRETNaNKILLSDRAGONKILLSNaNOUTER_TURRETKILLSKILLSNaNBARON_NASHORDRAGONINNER_TURRETINNER_TURRETINNER_TURRETDRAGONNaNKILLSKILLSBASE_TURRETINHIBITORNEXUS_TURRETNEXUS_TURRETNaNNaNNaNNaNNaNNaNNaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN134
10016710a48fdd46dNaNKILLSDRAGONKILLSNaNNaNKILLSNaNNaNKILLSKILLSNaNOUTER_TURRETNaNNaNNaNKILLSNaNKILLSOUTER_TURRETNaNKILLSOUTER_TURRETNaNKILLSNaNKILLSNaNKILLSBARON_NASHORKILLSINNER_TURRETINNER_TURRETINNER_TURRETNaNKILLSBASE_TURRETNaNINHIBITOR...NaNNaNNaNKILLSKILLSDRAGONNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN140
20016c9df37278448NaNNaNNaNNaNNaNNaNKILLSNaNNaNOUTER_TURRETNaNNaNKILLSNaNNaNNaNKILLSBARON_NASHOROUTER_TURRETDRAGONKILLSOUTER_TURRETINNER_TURRETINNER_TURRETBASE_TURRETINHIBITORINNER_TURRETBASE_TURRETBASE_TURRETINHIBITORNEXUS_TURRETNEXUS_TURRETNaNKILLSINHIBITORNaNNaNNaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN140
30021b45647424cd5NaNKILLSKILLSKILLSNaNDRAGONRIFT_HERALDNaNOUTER_TURRETNaNOUTER_TURRETNaNDRAGONKILLSKILLSOUTER_TURRETNaNKILLSBARON_NASHORNaNDRAGONINNER_TURRETKILLSINNER_TURRETBASE_TURRETINHIBITORNaNKILLSNEXUS_TURRETBARON_NASHORINNER_TURRETNaNDRAGONINHIBITORBASE_TURRETINHIBITORNaNNaNKILLS...NaNKILLSNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN142
400405293fb859241KILLSNaNNaNNaNNaNOUTER_TURRETNaNNaNNaNNaNNaNNaNNaNKILLSNaNNaNOUTER_TURRETKILLSKILLSNaNOUTER_TURRETDRAGONBARON_NASHORKILLSINNER_TURRETBASE_TURRETINHIBITORKILLSINNER_TURRETKILLSBASE_TURRETINHIBITORNaNKILLSINNER_TURRETNEXUS_TURRETNEXUS_TURRETKILLSNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN139
500416ed438e29f14NaNKILLSNaNNaNNaNOUTER_TURRETNaNKILLSKILLSDRAGONNaNNaNOUTER_TURRETKILLSKILLSBARON_NASHORNaNOUTER_TURRETNaNINNER_TURRETINNER_TURRETNaNBARON_NASHORKILLSBASE_TURRETNaNNaNNaNINHIBITORKILLSINNER_TURRETBASE_TURRETBASE_TURRETINHIBITORNaNKILLSNaNNEXUS_TURRETNEXUS_TURRET...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN141
6007802c051352561NaNKILLSDRAGONKILLSKILLSRIFT_HERALDNaNNaNOUTER_TURRETOUTER_TURRETINNER_TURRETNaNNaNNaNOUTER_TURRETNaNKILLSDRAGONINNER_TURRETDRAGONNaNNaNKILLSBARON_NASHORNaNKILLSKILLSELDER_DRAGONINNER_TURRETBASE_TURRETINHIBITORBARON_NASHORBASE_TURRETNaNKILLSINHIBITORKILLSNEXUS_TURRETNEXUS_TURRET...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN144
70091705b03924485NaNNaNNaNNaNNaNNaNDRAGONNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNINHIBITORINHIBITORNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN027
800986b51908a63c3NaNKILLSKILLSKILLSKILLSKILLSNaNOUTER_TURRETDRAGONOUTER_TURRETNaNINNER_TURRETKILLSOUTER_TURRETINNER_TURRETDRAGONKILLSNaNNaNDRAGONNaNNaNKILLSINNER_TURRETBASE_TURRETINHIBITORNaNNaNNaNKILLSBASE_TURRETNaNNaNINHIBITORNaNNaNKILLSINHIBITORBASE_TURRET...NaNNaNNaNKILLSINNER_TURRETNaNNaNNaNKILLSNaNBARON_NASHORKILLSKILLSELDER_DRAGONBASE_TURRETNaNNaNNaNKILLSKILLSNEXUS_TURRETNEXUS_TURRETNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN069
900b13dbf1bd7aff0NaNKILLSOUTER_TURRETNaNDRAGONNaNDRAGONOUTER_TURRETNaNOUTER_TURRETNaNKILLSNaNNaNKILLSBARON_NASHORNaNNaNNaNNaNDRAGONNaNKILLSINHIBITORNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN036
1000b2492ce66406e9KILLSKILLSKILLSNaNNaNNaNNaNKILLSKILLSKILLSKILLSKILLSOUTER_TURRETOUTER_TURRETDRAGONRIFT_HERALDOUTER_TURRETBARON_NASHORINNER_TURRETNaNINNER_TURRETKILLSINNER_TURRETDRAGONKILLSBASE_TURRETBASE_TURRETBASE_TURRETINHIBITORKILLSNEXUS_TURRETINHIBITORNEXUS_TURRETINHIBITORNaNKILLSNaNNaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN129
1100df84280ed2625bKILLSNaNNaNDRAGONNaNKILLSNaNKILLSKILLSOUTER_TURRETOUTER_TURRETKILLSNaNBARON_NASHORNaNKILLSOUTER_TURRETDRAGONINNER_TURRETNaNKILLSKILLSKILLSBARON_NASHORDRAGONINNER_TURRETKILLSNaNBASE_TURRETINHIBITORNEXUS_TURRETKILLSINNER_TURRETBASE_TURRETKILLSNEXUS_TURRETNaNKILLSNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN139
1200e4eb2c47c14065NaNOUTER_TURRETNaNOUTER_TURRETNaNDRAGONRIFT_HERALDNaNNaNNaNKILLSOUTER_TURRETNaNNaNBARON_NASHORINNER_TURRETNaNNaNNaNNaNNaNNaNNaNNaNNaNINHIBITORNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN031
1300ecc69a0f024adaNaNNaNNaNNaNNaNNaNOUTER_TURRETNaNNaNOUTER_TURRETNaNNaNNaNNaNNaNNaNNaNNaNINHIBITORNaNNaNNaNINHIBITORNaNNaNNaNINHIBITORNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN034
1400fc8c3957193d35KILLSKILLSOUTER_TURRETNaNINNER_TURRETNaNDRAGONNaNKILLSNaNOUTER_TURRETOUTER_TURRETDRAGONNaNKILLSNaNNaNNaNDRAGONKILLSBARON_NASHORNaNKILLSINNER_TURRETNaNKILLSBASE_TURRETINHIBITORKILLSINNER_TURRETKILLSNaNDRAGONNaNKILLSNaNKILLSNEXUS_TURRETNEXUS_TURRET...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN132
150122e715a37611f0NaNNaNKILLSNaNNaNNaNNaNDRAGONKILLSKILLSOUTER_TURRETKILLSNaNNaNNaNNaNOUTER_TURRETNaNNaNNaNKILLSNaNNaNKILLSNaNKILLSNaNNaNNaNNaNNaNNaNNaNINHIBITORNaNINHIBITORNaNINHIBITORNaN...NEXUS_TURRETNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN036
1601321351be01672fOUTER_TURRETNaNNaNOUTER_TURRETNaNDRAGONNaNNaNNaNOUTER_TURRETNaNKILLSKILLSNaNNaNNaNNaNNaNNaNINHIBITORNaNKILLSNaNNaNKILLSNaNINHIBITORNaNKILLSNaNINHIBITORNaNNaNNaNKILLSNaNNaNNaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN034
1701360c9cdc07e173NaNKILLSKILLSNaNNaNKILLSNaNDRAGONNaNNaNNaNOUTER_TURRETNaNKILLSNaNBARON_NASHORNaNDRAGONOUTER_TURRETOUTER_TURRETNaNINNER_TURRETKILLSNaNBASE_TURRETINNER_TURRETNaNINHIBITORKILLSNaNDRAGONNaNNEXUS_TURRETNaNINNER_TURRETNaNBASE_TURRETDRAGONINHIBITOR...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN144
1801368b5ce8fcef87NaNKILLSNaNKILLSNaNNaNNaNNaNNaNKILLSDRAGONKILLSNaNNaNNaNNaNNaNNaNNaNNaNNaNINHIBITORNaNINHIBITORNaNNaNNaNINHIBITORNaNKILLSNaNNaNNaNKILLSINHIBITORKILLSNaNNaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN034
1901417eebd0d9ccb3NaNNaNKILLSNaNKILLSNaNKILLSNaNNaNKILLSNaNNaNNaNNaNNaNNaNKILLSNaNNaNNaNNaNINHIBITORNaNNaNINHIBITORNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN031

In [26]:

# We now decided, for the purpose of calculating probabilities, to consider one team's perseperctive.
# Therefore, we make all events either positive or negative for red team but keep their label otherwise.

matchevents5=matchevents4
for j in range(1,len(list(redrows))):
    matchevents5['RedEvent'+str(j)] = '+'+ matchevents5['RedEvent'+str(j)].astype(str)
    matchevents5['BlueEvent'+str(j)] = '-'+ matchevents5['BlueEvent'+str(j)].astype(str)
    
    matchevents5 = matchevents5.replace('+nan',np.nan)
    matchevents5['RedEvent'+str(j)] =  matchevents5['RedEvent'+str(j)].fillna(
                                        (matchevents5['BlueEvent'+str(j)]).astype(str))
    
matchevents5.head()

Out[26]:

 idRedEvent1RedEvent2RedEvent3RedEvent4RedEvent5RedEvent6RedEvent7RedEvent8RedEvent9RedEvent10RedEvent11RedEvent12RedEvent13RedEvent14RedEvent15RedEvent16RedEvent17RedEvent18RedEvent19RedEvent20RedEvent21RedEvent22RedEvent23RedEvent24RedEvent25RedEvent26RedEvent27RedEvent28RedEvent29RedEvent30RedEvent31RedEvent32RedEvent33RedEvent34RedEvent35RedEvent36RedEvent37RedEvent38RedEvent39...BlueEvent42BlueEvent43BlueEvent44BlueEvent45BlueEvent46BlueEvent47BlueEvent48BlueEvent49BlueEvent50BlueEvent51BlueEvent52BlueEvent53BlueEvent54BlueEvent55BlueEvent56BlueEvent57BlueEvent58BlueEvent59BlueEvent60BlueEvent61BlueEvent62BlueEvent63BlueEvent64BlueEvent65BlueEvent66BlueEvent67BlueEvent68BlueEvent69BlueEvent70BlueEvent71BlueEvent72BlueEvent73BlueEvent74BlueEvent75BlueEvent76BlueEvent77BlueEvent78BlueEvent79rResultgamelength
00001f4374a03c133+KILLS+KILLS-KILLS+KILLS+OUTER_TURRET-OUTER_TURRET+DRAGON+RIFT_HERALD+OUTER_TURRET-OUTER_TURRET+KILLS+DRAGON+KILLS-KILLS+OUTER_TURRET+KILLS+KILLS-KILLS+BARON_NASHOR+DRAGON+INNER_TURRET+INNER_TURRET+INNER_TURRET+DRAGON-KILLS+KILLS+KILLS+BASE_TURRET+INHIBITOR+NEXUS_TURRET+NEXUS_TURRET-nan-nan-nan-nan-nan-nan-nan-nan...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan134
10016710a48fdd46d-KILLS+KILLS+DRAGON+KILLS-KILLS-OUTER_TURRET+KILLS-DRAGON-KILLS+KILLS+KILLS-KILLS+OUTER_TURRET-OUTER_TURRET-DRAGON-KILLS+KILLS-OUTER_TURRET+KILLS+OUTER_TURRET-KILLS+KILLS+OUTER_TURRET-KILLS+KILLS-KILLS+KILLS-DRAGON+KILLS+BARON_NASHOR+KILLS+INNER_TURRET+INNER_TURRET+INNER_TURRET-KILLS+KILLS+BASE_TURRET-KILLS+INHIBITOR...-nan-nan-nan-KILLS-KILLS-DRAGON-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan140
20016c9df37278448-KILLS-KILLS-KILLS-OUTER_TURRET-DRAGON-KILLS+KILLS-KILLS-RIFT_HERALD+OUTER_TURRET-OUTER_TURRET-DRAGON+KILLS-DRAGON-KILLS-OUTER_TURRET+KILLS+BARON_NASHOR+OUTER_TURRET+DRAGON+KILLS+OUTER_TURRET+INNER_TURRET+INNER_TURRET+BASE_TURRET+INHIBITOR+INNER_TURRET+BASE_TURRET+BASE_TURRET+INHIBITOR+NEXUS_TURRET+NEXUS_TURRET-KILLS+KILLS+INHIBITOR-nan-nan-nan-nan...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan140
30021b45647424cd5-KILLS+KILLS+KILLS+KILLS-KILLS+DRAGON+RIFT_HERALD-KILLS+OUTER_TURRET-KILLS+OUTER_TURRET-KILLS+DRAGON+KILLS+KILLS+OUTER_TURRET-OUTER_TURRET+KILLS+BARON_NASHOR-KILLS+DRAGON+INNER_TURRET+KILLS+INNER_TURRET+BASE_TURRET+INHIBITOR-KILLS+KILLS+NEXUS_TURRET+BARON_NASHOR+INNER_TURRET-KILLS+DRAGON+INHIBITOR+BASE_TURRET+INHIBITOR-KILLS-BARON_NASHOR+KILLS...-nan-KILLS-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan142
400405293fb859241+KILLS-KILLS-KILLS-KILLS-OUTER_TURRET+OUTER_TURRET-DRAGON-KILLS-OUTER_TURRET-KILLS-KILLS-DRAGON-OUTER_TURRET+KILLS-KILLS-DRAGON+OUTER_TURRET+KILLS+KILLS-KILLS+OUTER_TURRET+DRAGON+BARON_NASHOR+KILLS+INNER_TURRET+BASE_TURRET+INHIBITOR+KILLS+INNER_TURRET+KILLS+BASE_TURRET+INHIBITOR-KILLS+KILLS+INNER_TURRET+NEXUS_TURRET+NEXUS_TURRET+KILLS-nan...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan139

In [27]:

# We take on the red event columns now  and re-add the end result of the game for red team (1=win, 0=loss)

RedMatchEvents = matchevents5.iloc[:,0:80]
RedMatchEvents['RedResult'] = matchevents5['rResult']
RedMatchEvents['MatchLength'] = matchevents5['gamelength']
RedMatchEvents.iloc[0:10]

Out[27]:

 idRedEvent1RedEvent2RedEvent3RedEvent4RedEvent5RedEvent6RedEvent7RedEvent8RedEvent9RedEvent10RedEvent11RedEvent12RedEvent13RedEvent14RedEvent15RedEvent16RedEvent17RedEvent18RedEvent19RedEvent20RedEvent21RedEvent22RedEvent23RedEvent24RedEvent25RedEvent26RedEvent27RedEvent28RedEvent29RedEvent30RedEvent31RedEvent32RedEvent33RedEvent34RedEvent35RedEvent36RedEvent37RedEvent38RedEvent39...RedEvent42RedEvent43RedEvent44RedEvent45RedEvent46RedEvent47RedEvent48RedEvent49RedEvent50RedEvent51RedEvent52RedEvent53RedEvent54RedEvent55RedEvent56RedEvent57RedEvent58RedEvent59RedEvent60RedEvent61RedEvent62RedEvent63RedEvent64RedEvent65RedEvent66RedEvent67RedEvent68RedEvent69RedEvent70RedEvent71RedEvent72RedEvent73RedEvent74RedEvent75RedEvent76RedEvent77RedEvent78RedEvent79RedResultMatchLength
00001f4374a03c133+KILLS+KILLS-KILLS+KILLS+OUTER_TURRET-OUTER_TURRET+DRAGON+RIFT_HERALD+OUTER_TURRET-OUTER_TURRET+KILLS+DRAGON+KILLS-KILLS+OUTER_TURRET+KILLS+KILLS-KILLS+BARON_NASHOR+DRAGON+INNER_TURRET+INNER_TURRET+INNER_TURRET+DRAGON-KILLS+KILLS+KILLS+BASE_TURRET+INHIBITOR+NEXUS_TURRET+NEXUS_TURRET-nan-nan-nan-nan-nan-nan-nan-nan...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan134
10016710a48fdd46d-KILLS+KILLS+DRAGON+KILLS-KILLS-OUTER_TURRET+KILLS-DRAGON-KILLS+KILLS+KILLS-KILLS+OUTER_TURRET-OUTER_TURRET-DRAGON-KILLS+KILLS-OUTER_TURRET+KILLS+OUTER_TURRET-KILLS+KILLS+OUTER_TURRET-KILLS+KILLS-KILLS+KILLS-DRAGON+KILLS+BARON_NASHOR+KILLS+INNER_TURRET+INNER_TURRET+INNER_TURRET-KILLS+KILLS+BASE_TURRET-KILLS+INHIBITOR...+KILLS+NEXUS_TURRET+NEXUS_TURRET-KILLS-KILLS-DRAGON+KILLS+INHIBITOR-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan140
20016c9df37278448-KILLS-KILLS-KILLS-OUTER_TURRET-DRAGON-KILLS+KILLS-KILLS-RIFT_HERALD+OUTER_TURRET-OUTER_TURRET-DRAGON+KILLS-DRAGON-KILLS-OUTER_TURRET+KILLS+BARON_NASHOR+OUTER_TURRET+DRAGON+KILLS+OUTER_TURRET+INNER_TURRET+INNER_TURRET+BASE_TURRET+INHIBITOR+INNER_TURRET+BASE_TURRET+BASE_TURRET+INHIBITOR+NEXUS_TURRET+NEXUS_TURRET-KILLS+KILLS+INHIBITOR-nan-nan-nan-nan...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan140
30021b45647424cd5-KILLS+KILLS+KILLS+KILLS-KILLS+DRAGON+RIFT_HERALD-KILLS+OUTER_TURRET-KILLS+OUTER_TURRET-KILLS+DRAGON+KILLS+KILLS+OUTER_TURRET-OUTER_TURRET+KILLS+BARON_NASHOR-KILLS+DRAGON+INNER_TURRET+KILLS+INNER_TURRET+BASE_TURRET+INHIBITOR-KILLS+KILLS+NEXUS_TURRET+BARON_NASHOR+INNER_TURRET-KILLS+DRAGON+INHIBITOR+BASE_TURRET+INHIBITOR-KILLS-BARON_NASHOR+KILLS...+NEXUS_TURRET-KILLS-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan142
400405293fb859241+KILLS-KILLS-KILLS-KILLS-OUTER_TURRET+OUTER_TURRET-DRAGON-KILLS-OUTER_TURRET-KILLS-KILLS-DRAGON-OUTER_TURRET+KILLS-KILLS-DRAGON+OUTER_TURRET+KILLS+KILLS-KILLS+OUTER_TURRET+DRAGON+BARON_NASHOR+KILLS+INNER_TURRET+BASE_TURRET+INHIBITOR+KILLS+INNER_TURRET+KILLS+BASE_TURRET+INHIBITOR-KILLS+KILLS+INNER_TURRET+NEXUS_TURRET+NEXUS_TURRET+KILLS-nan...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan139
500416ed438e29f14-KILLS+KILLS-KILLS-DRAGON-KILLS+OUTER_TURRET-KILLS+KILLS+KILLS+DRAGON-OUTER_TURRET-OUTER_TURRET+OUTER_TURRET+KILLS+KILLS+BARON_NASHOR-DRAGON+OUTER_TURRET-KILLS+INNER_TURRET+INNER_TURRET-DRAGON+BARON_NASHOR+KILLS+BASE_TURRET-KILLS-KILLS-INNER_TURRET+INHIBITOR+KILLS+INNER_TURRET+BASE_TURRET+BASE_TURRET+INHIBITOR-KILLS+KILLS-ELDER_DRAGON+NEXUS_TURRET+NEXUS_TURRET...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan141
6007802c051352561-KILLS+KILLS+DRAGON+KILLS+KILLS+RIFT_HERALD-OUTER_TURRET-OUTER_TURRET+OUTER_TURRET+OUTER_TURRET+INNER_TURRET-INNER_TURRET-KILLS-DRAGON+OUTER_TURRET-KILLS+KILLS+DRAGON+INNER_TURRET+DRAGON-KILLS-OUTER_TURRET+KILLS+BARON_NASHOR-KILLS+KILLS+KILLS+ELDER_DRAGON+INNER_TURRET+BASE_TURRET+INHIBITOR+BARON_NASHOR+BASE_TURRET-KILLS+KILLS+INHIBITOR+KILLS+NEXUS_TURRET+NEXUS_TURRET...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan144
70091705b03924485-KILLS-KILLS-OUTER_TURRET-KILLS-KILLS-OUTER_TURRET+DRAGON-RIFT_HERALD-OUTER_TURRET-KILLS-INNER_TURRET-DRAGON-KILLS-INNER_TURRET-BARON_NASHOR-KILLS-BASE_TURRET-BASE_TURRET+INHIBITOR+INHIBITOR-KILLS-NEXUS_TURRET-NEXUS_TURRET-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan027
800986b51908a63c3-KILLS+KILLS+KILLS+KILLS+KILLS+KILLS-OUTER_TURRET+OUTER_TURRET+DRAGON+OUTER_TURRET-OUTER_TURRET+INNER_TURRET+KILLS+OUTER_TURRET+INNER_TURRET+DRAGON+KILLS-KILLS-OUTER_TURRET+DRAGON-KILLS-BARON_NASHOR+KILLS+INNER_TURRET+BASE_TURRET+INHIBITOR-KILLS-KILLS-ELDER_DRAGON+KILLS+BASE_TURRET-KILLS-KILLS+INHIBITOR-BARON_NASHOR-INNER_TURRET+KILLS+INHIBITOR+BASE_TURRET...+INHIBITOR+ELDER_DRAGON+KILLS-KILLS-INNER_TURRET+KILLS+INHIBITOR+KILLS-KILLS+NEXUS_TURRET-BARON_NASHOR-KILLS-KILLS-ELDER_DRAGON-BASE_TURRET+INHIBITOR+INHIBITOR+NEXUS_TURRET-KILLS-KILLS-NEXUS_TURRET-NEXUS_TURRET-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan069
900b13dbf1bd7aff0-KILLS+KILLS+OUTER_TURRET-OUTER_TURRET+DRAGON-KILLS+DRAGON+OUTER_TURRET-OUTER_TURRET+OUTER_TURRET-KILLS+KILLS-KILLS-DRAGON+KILLS+BARON_NASHOR-KILLS-OUTER_TURRET-INNER_TURRET-BASE_TURRET+DRAGON-KILLS+KILLS+INHIBITOR-NEXUS_TURRET-KILLS-NEXUS_TURRET-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan036

In [28]:

RedMatchEvents[['RedEvent1','id']].groupby('RedEvent1').count()

Out[28]:

 id
RedEvent1 
+DRAGON158
+KILLS1931
+OUTER_TURRET263
+RIFT_HERALD7
-DRAGON108
-KILLS2136
-OUTER_TURRET302
-RIFT_HERALD10

In [29]:

RedMatchEvents[['RedEvent1','MatchLength']].groupby('RedEvent1').mean()

Out[29]:

 MatchLength
RedEvent1 
+DRAGON38.278481
+KILLS36.599689
+OUTER_TURRET37.680608
+RIFT_HERALD39.428571
-DRAGON37.750000
-KILLS36.265918
-OUTER_TURRET38.049669
-RIFT_HERALD41.500000

In [30]:

sns.boxplot(RedMatchEvents['RedEvent1'],RedMatchEvents['MatchLength'],RedMatchEvents['RedResult'],
            boxprops=dict(alpha=.7) )
plt.xticks(rotation=45)
plt.title('Distribution of Match Length by First Event and Match Result (Win = 1, Loss = 0)')
plt.ylim(0,100)
plt.xlabel('Event 1')
plt.plot([1.5, 1.5], [0, 100],'k', linewidth=2,alpha=0.8 )
plt.plot([3.5, 3.5], [0, 100],'k', linewidth=2,alpha=0.8 )
plt.plot([5.5, 5.5], [0, 100],'k', linewidth=2,alpha=0.8 )
plt.show()

In [31]:

# We can now use this to calculate some conditional probabilities as shown

TestData = RedMatchEvents

PwinGivenFirstBloodWon = ( (len(TestData[(TestData['RedEvent1']=="+KILLS")&(TestData['RedResult']==1)])/len(TestData))/
        (len( TestData[TestData['RedEvent1']=="+KILLS"])/len(TestData)) )
    
PwinGivenFirstBloodLost = ( (len(TestData[(TestData['RedEvent1']=="-KILLS")&(TestData['RedResult']==1)])/len(TestData))/
        (len( TestData[TestData['RedEvent1']=="-KILLS"])/len(TestData)) )


PwinGivenFirstTowerWon = ( (len(TestData[(TestData['RedEvent1']=="+OUTER_TURRET")&(TestData['RedResult']==1)])/len(TestData))/
        (len( TestData[TestData['RedEvent1']=="+OUTER_TURRET"])/len(TestData)) )
    
PwinGivenFirstTowerLost = ( (len(TestData[(TestData['RedEvent1']=="-OUTER_TURRET")&(TestData['RedResult']==1)])/len(TestData))/
        (len( TestData[TestData['RedEvent1']=="-OUTER_TURRET"])/len(TestData)) )


PwinGivenFirstDragonWon = ( (len(TestData[(TestData['RedEvent1']=="+DRAGON")&(TestData['RedResult']==1)])/len(TestData))/
        (len( TestData[TestData['RedEvent1']=="+DRAGON"])/len(TestData)) )
    
PwinGivenFirstDragonLost = ( (len(TestData[(TestData['RedEvent1']=="-DRAGON")&(TestData['RedResult']==1)])/len(TestData))/
        (len( TestData[TestData['RedEvent1']=="-DRAGON"])/len(TestData)) )


PwinGivenFirstRiftHeraldWon = ( (len(TestData[(TestData['RedEvent1']=="+RIFT_HERALD")&(TestData['RedResult']==1)])/len(TestData))/
        (len( TestData[TestData['RedEvent1']=="+RIFT_HERALD"])/len(TestData)) )
    
PwinGivenFirstRiftHeraldLost = ( (len(TestData[(TestData['RedEvent1']=="-RIFT_HERALD")&(TestData['RedResult']==1)])/len(TestData))/
        (len( TestData[TestData['RedEvent1']=="-RIFT_HERALD"])/len(TestData)) )





print("-------FIRST BLOOD--------------------------------")
print("P(Won | First Blood Taken):",PwinGivenFirstBloodWon)
print("P(Won | First Blood Lost):",PwinGivenFirstBloodLost)

print("")
print("-------FIRST TURRET-------------------------------")
print("P(Won | First Tower Won):",PwinGivenFirstTowerWon)
print("P(Won | First Tower Lost):",PwinGivenFirstTowerLost)

print("")
print("-------FIRST DRAGON-------------------------------")
print("P(Won | First Dragon Won):",PwinGivenFirstDragonWon)
print("P(Won | First Dragon Lost):",PwinGivenFirstDragonLost)

print("")
print("-------FIRST RIFT HERALD (NOTE: ONLY 17 GAMES)----")
print("P(Won | First Rift Herald Won):",PwinGivenFirstRiftHeraldWon)
print("P(Won | First Rift Herald Lost):",PwinGivenFirstRiftHeraldLost)
-------FIRST BLOOD--------------------------------
P(Won | First Blood Taken): 0.5535991714137752
P(Won | First Blood Lost): 0.3647003745318352

-------FIRST TURRET-------------------------------
P(Won | First Tower Won): 0.4790874524714828
P(Won | First Tower Lost): 0.4470198675496689

-------FIRST DRAGON-------------------------------
P(Won | First Dragon Won): 0.5189873417721519
P(Won | First Dragon Lost): 0.4351851851851851

-------FIRST RIFT HERALD (NOTE: ONLY 17 GAMES)----
P(Won | First Rift Herald Won): 0.5714285714285714
P(Won | First Rift Herald Lost): 0.1

In [32]:

aggs = {'id':'count','MatchLength':'mean'}

RedMatchTWOEvents = (RedMatchEvents[['RedEvent1','RedEvent2','RedResult','id','MatchLength']].groupby(
        ['RedEvent1','RedEvent2','RedResult']).agg(aggs).reset_index())

RedMatchTWOEvents = RedMatchTWOEvents.sort_values(['RedEvent1','RedEvent2','RedResult'])

RedMatchTWOEventsWINS = RedMatchTWOEvents[RedMatchTWOEvents['RedResult']==1]
RedMatchTWOEventsLOSS = RedMatchTWOEvents[RedMatchTWOEvents['RedResult']==0]

In [33]:

# First merge the RedWin and RedLoss data tables
# Then remove events which only resulted in a win then calculate the total number of games that has these two events
# Use this total to calculate the prob of win and loss respectively 

RedMatchTWOEventsMERGED = RedMatchTWOEventsWINS.merge(RedMatchTWOEventsLOSS, how='left',on=['RedEvent1','RedEvent2'])


RedMatchTWOEventsMERGED = RedMatchTWOEventsMERGED[RedMatchTWOEventsMERGED['id_y']>0]
RedMatchTWOEventsMERGED['Total'] = RedMatchTWOEventsMERGED['id_x']+RedMatchTWOEventsMERGED['id_y']

RedMatchTWOEventsMERGED['ProbWIN'] = RedMatchTWOEventsMERGED['id_x']/RedMatchTWOEventsMERGED['Total'].sum()
RedMatchTWOEventsMERGED['ProbLOSS'] = RedMatchTWOEventsMERGED['id_y']/RedMatchTWOEventsMERGED['Total'].sum()

RedMatchTWOEventsMERGED['ProbE1ANDE2'] = RedMatchTWOEventsMERGED['Total']/(RedMatchTWOEventsMERGED['Total'].sum())

RedMatchTWOEventsMERGED['ProbWINgivenE1ANDE2'] = RedMatchTWOEventsMERGED['ProbWIN']/RedMatchTWOEventsMERGED['ProbE1ANDE2']
RedMatchTWOEventsMERGED['ProbLOSSgivenE1ANDE2'] = RedMatchTWOEventsMERGED['ProbLOSS']/RedMatchTWOEventsMERGED['ProbE1ANDE2']

# Create column to single binary digit for whether the first event is positive or negative

RedMatchTWOEventsMERGED['RedEvent1Gain'] = np.where(
                                (RedMatchTWOEventsMERGED['RedEvent1']=="+KILLS") |
                                (RedMatchTWOEventsMERGED['RedEvent1']=="+OUTER_TURRET") |
                                (RedMatchTWOEventsMERGED['RedEvent1']=="+DRAGON") |
                                (RedMatchTWOEventsMERGED['RedEvent1']=="+RIFT_HERALD") ,1,0
                                                   
                                                   
                                                   )
# Repeat for second event

RedMatchTWOEventsMERGED['RedEvent2Gain'] = np.where(
                                (RedMatchTWOEventsMERGED['RedEvent2']=="+KILLS") |
                                (RedMatchTWOEventsMERGED['RedEvent2']=="+OUTER_TURRET") |
                                (RedMatchTWOEventsMERGED['RedEvent2']=="+DRAGON") |
                                (RedMatchTWOEventsMERGED['RedEvent2']=="+RIFT_HERALD") ,1,0
                                                   
                                                   
                                                   )
# Create another column for combination of first and second event outcomes classification
RedMatchTWOEventsMERGED['Event1AND2Outcome'] = np.where(
    (RedMatchTWOEventsMERGED['RedEvent1Gain']==1)&(RedMatchTWOEventsMERGED['RedEvent2Gain']==1),"Both Positive",
                
    np.where(
        (((RedMatchTWOEventsMERGED['RedEvent1Gain']==1)&(RedMatchTWOEventsMERGED['RedEvent2Gain']==0))|
        ((RedMatchTWOEventsMERGED['RedEvent1Gain']==0)&(RedMatchTWOEventsMERGED['RedEvent2Gain']==1))),"One Positive",
    
    np.where(
        (RedMatchTWOEventsMERGED['RedEvent1Gain']==0)&(RedMatchTWOEventsMERGED['RedEvent2Gain']==0),"Neither Positive",
             "MISSING",)))

# Sort by highest probability of win to lowest
RedMatchTWOEventsMERGED = RedMatchTWOEventsMERGED.sort_values('ProbWINgivenE1ANDE2',ascending=False)

# Remove event combination with less than x number of games to remove possible outliers
RedMatchTWOEventsMERGED = RedMatchTWOEventsMERGED[RedMatchTWOEventsMERGED['Total']>=0]


RedMatchTWOEventsMERGED.head(5)

Out[33]:

 RedEvent1RedEvent2RedResult_xid_xMatchLength_xRedResult_yid_yMatchLength_yTotalProbWINProbLOSSProbE1ANDE2ProbWINgivenE1ANDE2ProbLOSSgivenE1ANDE2RedEvent1GainRedEvent2GainEvent1AND2Outcome
16+OUTER_TURRET+KILLS11138.1818180.02.034.5013.00.0022480.0004090.0026560.8461540.15384611Both Positive
9+KILLS+RIFT_HERALD1431.7500000.01.033.005.00.0008170.0002040.0010220.8000000.20000011Both Positive
1+DRAGON+OUTER_TURRET11137.6363640.04.041.7515.00.0022480.0008170.0030650.7333330.26666711Both Positive
21+RIFT_HERALD-DRAGON1236.5000000.01.032.003.00.0004090.0002040.0006130.6666670.33333310One Positive
20+RIFT_HERALD+KILLS1236.0000000.01.033.003.00.0004090.0002040.0006130.6666670.33333311Both Positive

In [34]:

sns.pairplot(data = RedMatchTWOEventsMERGED, x_vars='ProbWINgivenE1ANDE2',y_vars='MatchLength_x',
           hue= 'Event1AND2Outcome', size=8)
plt.title('Probability of Winning Given the First Two Events against Average Game Duration, \n Coloured by Event 1 and 2 Outcomes')
plt.xlabel('Probability of Win GIVEN First Two Events')
plt.ylabel('Average Game Length')
plt.xlim([0,1])
plt.xticks(np.arange(0,1.1,0.1))
#plt.ylim([20,50])

plt.show()

Markov Decision Process (MDP)

We could calculate the conditional probabilities for more events but, as had already become a challenge, the calculation process would be increasingly complicated. Therefore, instead of this, we can model our data as an MDP where we create pairwise probabilities between the events. Each event stage is a state and we calculate the probability of going to the next state given we are in the current one at that event stage.

In [35]:

RedMatchEvents.head()

Out[35]:

 idRedEvent1RedEvent2RedEvent3RedEvent4RedEvent5RedEvent6RedEvent7RedEvent8RedEvent9RedEvent10RedEvent11RedEvent12RedEvent13RedEvent14RedEvent15RedEvent16RedEvent17RedEvent18RedEvent19RedEvent20RedEvent21RedEvent22RedEvent23RedEvent24RedEvent25RedEvent26RedEvent27RedEvent28RedEvent29RedEvent30RedEvent31RedEvent32RedEvent33RedEvent34RedEvent35RedEvent36RedEvent37RedEvent38RedEvent39...RedEvent42RedEvent43RedEvent44RedEvent45RedEvent46RedEvent47RedEvent48RedEvent49RedEvent50RedEvent51RedEvent52RedEvent53RedEvent54RedEvent55RedEvent56RedEvent57RedEvent58RedEvent59RedEvent60RedEvent61RedEvent62RedEvent63RedEvent64RedEvent65RedEvent66RedEvent67RedEvent68RedEvent69RedEvent70RedEvent71RedEvent72RedEvent73RedEvent74RedEvent75RedEvent76RedEvent77RedEvent78RedEvent79RedResultMatchLength
00001f4374a03c133+KILLS+KILLS-KILLS+KILLS+OUTER_TURRET-OUTER_TURRET+DRAGON+RIFT_HERALD+OUTER_TURRET-OUTER_TURRET+KILLS+DRAGON+KILLS-KILLS+OUTER_TURRET+KILLS+KILLS-KILLS+BARON_NASHOR+DRAGON+INNER_TURRET+INNER_TURRET+INNER_TURRET+DRAGON-KILLS+KILLS+KILLS+BASE_TURRET+INHIBITOR+NEXUS_TURRET+NEXUS_TURRET-nan-nan-nan-nan-nan-nan-nan-nan...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan134
10016710a48fdd46d-KILLS+KILLS+DRAGON+KILLS-KILLS-OUTER_TURRET+KILLS-DRAGON-KILLS+KILLS+KILLS-KILLS+OUTER_TURRET-OUTER_TURRET-DRAGON-KILLS+KILLS-OUTER_TURRET+KILLS+OUTER_TURRET-KILLS+KILLS+OUTER_TURRET-KILLS+KILLS-KILLS+KILLS-DRAGON+KILLS+BARON_NASHOR+KILLS+INNER_TURRET+INNER_TURRET+INNER_TURRET-KILLS+KILLS+BASE_TURRET-KILLS+INHIBITOR...+KILLS+NEXUS_TURRET+NEXUS_TURRET-KILLS-KILLS-DRAGON+KILLS+INHIBITOR-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan140
20016c9df37278448-KILLS-KILLS-KILLS-OUTER_TURRET-DRAGON-KILLS+KILLS-KILLS-RIFT_HERALD+OUTER_TURRET-OUTER_TURRET-DRAGON+KILLS-DRAGON-KILLS-OUTER_TURRET+KILLS+BARON_NASHOR+OUTER_TURRET+DRAGON+KILLS+OUTER_TURRET+INNER_TURRET+INNER_TURRET+BASE_TURRET+INHIBITOR+INNER_TURRET+BASE_TURRET+BASE_TURRET+INHIBITOR+NEXUS_TURRET+NEXUS_TURRET-KILLS+KILLS+INHIBITOR-nan-nan-nan-nan...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan140
30021b45647424cd5-KILLS+KILLS+KILLS+KILLS-KILLS+DRAGON+RIFT_HERALD-KILLS+OUTER_TURRET-KILLS+OUTER_TURRET-KILLS+DRAGON+KILLS+KILLS+OUTER_TURRET-OUTER_TURRET+KILLS+BARON_NASHOR-KILLS+DRAGON+INNER_TURRET+KILLS+INNER_TURRET+BASE_TURRET+INHIBITOR-KILLS+KILLS+NEXUS_TURRET+BARON_NASHOR+INNER_TURRET-KILLS+DRAGON+INHIBITOR+BASE_TURRET+INHIBITOR-KILLS-BARON_NASHOR+KILLS...+NEXUS_TURRET-KILLS-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan142
400405293fb859241+KILLS-KILLS-KILLS-KILLS-OUTER_TURRET+OUTER_TURRET-DRAGON-KILLS-OUTER_TURRET-KILLS-KILLS-DRAGON-OUTER_TURRET+KILLS-KILLS-DRAGON+OUTER_TURRET+KILLS+KILLS-KILLS+OUTER_TURRET+DRAGON+BARON_NASHOR+KILLS+INNER_TURRET+BASE_TURRET+INHIBITOR+KILLS+INNER_TURRET+KILLS+BASE_TURRET+INHIBITOR-KILLS+KILLS+INNER_TURRET+NEXUS_TURRET+NEXUS_TURRET+KILLS-nan...-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan-nan139

In [36]:

# WARNING: Takes a while to run
# Replace all N/As with the match outcome so that our final state is either a Win or Loss
for i in range(1,80):
    RedMatchEvents['RedEvent'+str(i)] = RedMatchEvents['RedEvent'+str(i)].replace('-nan',RedMatchEvents['RedResult'].astype(str))
    RedMatchEvents['RedEvent'+str(i)] = RedMatchEvents['RedEvent'+str(i)].replace('+nan',RedMatchEvents['RedResult'].astype(str))
    #Print i for progress tracking
    #print(i)
RedMatchEvents.head()

Out[36]:

 idRedEvent1RedEvent2RedEvent3RedEvent4RedEvent5RedEvent6RedEvent7RedEvent8RedEvent9RedEvent10RedEvent11RedEvent12RedEvent13RedEvent14RedEvent15RedEvent16RedEvent17RedEvent18RedEvent19RedEvent20RedEvent21RedEvent22RedEvent23RedEvent24RedEvent25RedEvent26RedEvent27RedEvent28RedEvent29RedEvent30RedEvent31RedEvent32RedEvent33RedEvent34RedEvent35RedEvent36RedEvent37RedEvent38RedEvent39...RedEvent42RedEvent43RedEvent44RedEvent45RedEvent46RedEvent47RedEvent48RedEvent49RedEvent50RedEvent51RedEvent52RedEvent53RedEvent54RedEvent55RedEvent56RedEvent57RedEvent58RedEvent59RedEvent60RedEvent61RedEvent62RedEvent63RedEvent64RedEvent65RedEvent66RedEvent67RedEvent68RedEvent69RedEvent70RedEvent71RedEvent72RedEvent73RedEvent74RedEvent75RedEvent76RedEvent77RedEvent78RedEvent79RedResultMatchLength
00001f4374a03c133+KILLS+KILLS-KILLS+KILLS+OUTER_TURRET-OUTER_TURRET+DRAGON+RIFT_HERALD+OUTER_TURRET-OUTER_TURRET+KILLS+DRAGON+KILLS-KILLS+OUTER_TURRET+KILLS+KILLS-KILLS+BARON_NASHOR+DRAGON+INNER_TURRET+INNER_TURRET+INNER_TURRET+DRAGON-KILLS+KILLS+KILLS+BASE_TURRET+INHIBITOR+NEXUS_TURRET+NEXUS_TURRET11111111...11111111111111111111111111111111111111134
10016710a48fdd46d-KILLS+KILLS+DRAGON+KILLS-KILLS-OUTER_TURRET+KILLS-DRAGON-KILLS+KILLS+KILLS-KILLS+OUTER_TURRET-OUTER_TURRET-DRAGON-KILLS+KILLS-OUTER_TURRET+KILLS+OUTER_TURRET-KILLS+KILLS+OUTER_TURRET-KILLS+KILLS-KILLS+KILLS-DRAGON+KILLS+BARON_NASHOR+KILLS+INNER_TURRET+INNER_TURRET+INNER_TURRET-KILLS+KILLS+BASE_TURRET-KILLS+INHIBITOR...+KILLS+NEXUS_TURRET+NEXUS_TURRET-KILLS-KILLS-DRAGON+KILLS+INHIBITOR111111111111111111111111111111140
20016c9df37278448-KILLS-KILLS-KILLS-OUTER_TURRET-DRAGON-KILLS+KILLS-KILLS-RIFT_HERALD+OUTER_TURRET-OUTER_TURRET-DRAGON+KILLS-DRAGON-KILLS-OUTER_TURRET+KILLS+BARON_NASHOR+OUTER_TURRET+DRAGON+KILLS+OUTER_TURRET+INNER_TURRET+INNER_TURRET+BASE_TURRET+INHIBITOR+INNER_TURRET+BASE_TURRET+BASE_TURRET+INHIBITOR+NEXUS_TURRET+NEXUS_TURRET-KILLS+KILLS+INHIBITOR1111...11111111111111111111111111111111111111140
30021b45647424cd5-KILLS+KILLS+KILLS+KILLS-KILLS+DRAGON+RIFT_HERALD-KILLS+OUTER_TURRET-KILLS+OUTER_TURRET-KILLS+DRAGON+KILLS+KILLS+OUTER_TURRET-OUTER_TURRET+KILLS+BARON_NASHOR-KILLS+DRAGON+INNER_TURRET+KILLS+INNER_TURRET+BASE_TURRET+INHIBITOR-KILLS+KILLS+NEXUS_TURRET+BARON_NASHOR+INNER_TURRET-KILLS+DRAGON+INHIBITOR+BASE_TURRET+INHIBITOR-KILLS-BARON_NASHOR+KILLS...+NEXUS_TURRET-KILLS111111111111111111111111111111111111142
400405293fb859241+KILLS-KILLS-KILLS-KILLS-OUTER_TURRET+OUTER_TURRET-DRAGON-KILLS-OUTER_TURRET-KILLS-KILLS-DRAGON-OUTER_TURRET+KILLS-KILLS-DRAGON+OUTER_TURRET+KILLS+KILLS-KILLS+OUTER_TURRET+DRAGON+BARON_NASHOR+KILLS+INNER_TURRET+BASE_TURRET+INHIBITOR+KILLS+INNER_TURRET+KILLS+BASE_TURRET+INHIBITOR-KILLS+KILLS+INNER_TURRET+NEXUS_TURRET+NEXUS_TURRET+KILLS1...11111111111111111111111111111111111111139

In [37]:

RedMatchEvents[['RedEvent60','id']].groupby('RedEvent60').count()

Out[37]:

 id
RedEvent60 
+BARON_NASHOR4
+BASE_TURRET2
+ELDER_DRAGON1
+INHIBITOR12
+INNER_TURRET2
+KILLS7
+NEXUS_TURRET5
-BARON_NASHOR1
-BASE_TURRET3
-ELDER_DRAGON2
-KILLS19
-NEXUS_TURRET12
02629
12216

In [38]:

RedMatchEvents2 = RedMatchEvents

In [39]:

# WARNING: Takes a little while to run

EventList = [
    #Positive Events
       '+KILLS', '+OUTER_TURRET', '+DRAGON', '+RIFT_HERALD', '+BARON_NASHOR',
       '+INNER_TURRET', '+BASE_TURRET', '+INHIBITOR', '+NEXUS_TURRET',
       '+ELDER_DRAGON',
    #Negative Events
       '-KILLS', '-OUTER_TURRET', '-DRAGON', '-RIFT_HERALD', '-BARON_NASHOR',
       '-INNER_TURRET', '-BASE_TURRET', '-INHIBITOR', '-NEXUS_TURRET',
       '-ELDER_DRAGON',
    #Game Win or Loss Events        
       '1','0']

RedMatchMDP = pd.DataFrame()

for i in range(1,79):
                              
    Event = i
    for j1 in range(0,len(EventList)):
        Event1 = EventList[j1]
        for j2 in range(0,len(EventList)):
            
            Event2 = EventList[j2]
            
            
            if  len(RedMatchEvents2[(RedMatchEvents2['RedEvent'+str(Event)]==Event1)])==0:
                continue
            #elif len(RedMatchEvents2[(RedMatchEvents2['RedEvent'+str(Event)]==Event1)&
            #                   (RedMatchEvents2['RedEvent'+str(Event+1)]==Event2) ])==0:
                continue
                
            else:
                TransProb = (
                    len(RedMatchEvents2[(RedMatchEvents2['RedEvent'+str(Event)]==Event1)&
                               (RedMatchEvents2['RedEvent'+str(Event+1)]==Event2) ])/

                    len(RedMatchEvents2[(RedMatchEvents2['RedEvent'+str(Event)]==Event1)])
                    )


            RedMatchMDP2 = pd.DataFrame({'StartState':Event,'EndState':Event+1,'Event1':Event1,'Event2':Event2,'Probability':TransProb},
                                  index=[0])
            RedMatchMDP = RedMatchMDP.append(RedMatchMDP2)
   
    #Print i for tracking progress
    #print(i)
    

In [40]:

RedMatchMDP = RedMatchMDP[['StartState','EndState','Event1','Event2','Probability']]
RedMatchMDP[(RedMatchMDP['StartState']==61)&(RedMatchMDP['Event1']=="+INHIBITOR")]

Out[40]:

 StartStateEndStateEvent1Event2Probability
06162+INHIBITOR+KILLS0.076923
06162+INHIBITOR+OUTER_TURRET0.000000
06162+INHIBITOR+DRAGON0.000000
06162+INHIBITOR+RIFT_HERALD0.000000
06162+INHIBITOR+BARON_NASHOR0.153846
06162+INHIBITOR+INNER_TURRET0.076923
06162+INHIBITOR+BASE_TURRET0.000000
06162+INHIBITOR+INHIBITOR0.076923
06162+INHIBITOR+NEXUS_TURRET0.153846
06162+INHIBITOR+ELDER_DRAGON0.000000
06162+INHIBITOR-KILLS0.076923
06162+INHIBITOR-OUTER_TURRET0.000000
06162+INHIBITOR-DRAGON0.000000
06162+INHIBITOR-RIFT_HERALD0.000000
06162+INHIBITOR-BARON_NASHOR0.153846
06162+INHIBITOR-INNER_TURRET0.000000
06162+INHIBITOR-BASE_TURRET0.000000
06162+INHIBITOR-INHIBITOR0.000000
06162+INHIBITOR-NEXUS_TURRET0.230769
06162+INHIBITOR-ELDER_DRAGON0.000000
06162+INHIBITOR10.000000
06162+INHIBITOR00.000000

In [41]:

EndCondition = RedMatchMDP[
    ((RedMatchMDP['Event1']!="1")&(RedMatchMDP['Event2']=="1") )|
    ((RedMatchMDP['Event1']!="0")&(RedMatchMDP['Event2']=="0"))]

EndCondition = EndCondition.sort_values('Probability',ascending=False)

EndConditionGrouped = EndCondition[['StartState','Probability']].groupby('StartState').mean().reset_index()
EndConditionGrouped['CumProb'] = EndConditionGrouped['Probability'].cumsum()

EndConditionGrouped2 = EndCondition[['StartState','Probability']].groupby('StartState').sum().reset_index()
EndConditionGrouped2['CumProb'] = EndConditionGrouped2['Probability'].cumsum()

fig, axes = plt.subplots(nrows=2, ncols=2)

axes[0,0].bar(EndConditionGrouped['StartState'],EndConditionGrouped['Probability'] ,alpha=0.3)
axes[0,0].plot(EndConditionGrouped['StartState'],EndConditionGrouped['Probability'])
axes[0,0].set_title('Mean Probability Dist')
axes[0,0].set_xlabel("State")
axes[0,0].set_ylabel("Probability of Ending")
axes[0,0].set_xticks([],[])
axes[0,0].set_xlabel("")
axes[0,0].set_xlim([0,80])
axes[0,0].grid(False)

axes[0,1].bar(EndConditionGrouped['StartState'],EndConditionGrouped['CumProb'] ,alpha=0.3)
axes[0,1].plot(EndConditionGrouped['StartState'],EndConditionGrouped['CumProb'])
axes[0,1].set_title('Mean Cumulative Probability Dist')
axes[0,1].set_xlabel("State")
axes[0,1].set_ylabel("Cumlative Probability of Ending")
axes[0,1].set_xticks([])
axes[0,1].set_xlabel("")
axes[0,1].set_xlim([0,80])
axes[0,1].grid(False)

axes[1,0].bar(EndConditionGrouped2['StartState'],EndConditionGrouped2['Probability'] ,alpha=0.3)
axes[1,0].plot(EndConditionGrouped2['StartState'],EndConditionGrouped2['Probability'])
axes[1,0].set_title('Sum Probability Dist')
axes[1,0].set_xlabel("State")
axes[1,0].set_ylabel("Probability of Ending")
axes[1,0].set_xlim([0,80])
axes[1,0].grid(False)

axes[1,1].bar(EndConditionGrouped2['StartState'],EndConditionGrouped2['CumProb'] ,alpha=0.3)
axes[1,1].plot(EndConditionGrouped2['StartState'],EndConditionGrouped2['CumProb'])
axes[1,1].set_title('Sum Cumulative Probability Dist')
axes[1,1].set_xlabel("State")
axes[1,1].set_ylabel("Cumlative Probability of Ending")
axes[1,1].set_xlim([0,80])
axes[1,1].grid(False)

fig.suptitle("Probability of Game Ending in Each State Averaged and Summed over Varying Start Events")

fig.set_figheight(15)
fig.set_figwidth(15)
plt.show()

In [42]:

RedMatchMDP['Reward'] = 0

RedMatchMDP.head()

Out[42]:

 StartStateEndStateEvent1Event2ProbabilityReward
012+KILLS+KILLS0.3505960
012+KILLS+OUTER_TURRET0.0585190
012+KILLS+DRAGON0.0761260
012+KILLS+RIFT_HERALD0.0025890
012+KILLS+BARON_NASHOR0.0000000

In [43]:

len(RedMatchMDP)

Out[43]:

25564

In [44]:

RedMatchMDP[(RedMatchMDP['StartState']==15)&(RedMatchMDP['Event1']=="+ELDER_DRAGON")]

Out[44]:

 StartStateEndStateEvent1Event2ProbabilityReward
01516+ELDER_DRAGON+KILLS0.50
01516+ELDER_DRAGON+OUTER_TURRET0.00
01516+ELDER_DRAGON+DRAGON0.00
01516+ELDER_DRAGON+RIFT_HERALD0.00
01516+ELDER_DRAGON+BARON_NASHOR0.00
01516+ELDER_DRAGON+INNER_TURRET0.00
01516+ELDER_DRAGON+BASE_TURRET0.00
01516+ELDER_DRAGON+INHIBITOR0.00
01516+ELDER_DRAGON+NEXUS_TURRET0.00
01516+ELDER_DRAGON+ELDER_DRAGON0.00
01516+ELDER_DRAGON-KILLS0.50
01516+ELDER_DRAGON-OUTER_TURRET0.00
01516+ELDER_DRAGON-DRAGON0.00
01516+ELDER_DRAGON-RIFT_HERALD0.00
01516+ELDER_DRAGON-BARON_NASHOR0.00
01516+ELDER_DRAGON-INNER_TURRET0.00
01516+ELDER_DRAGON-BASE_TURRET0.00
01516+ELDER_DRAGON-INHIBITOR0.00
01516+ELDER_DRAGON-NEXUS_TURRET0.00
01516+ELDER_DRAGON-ELDER_DRAGON0.00
01516+ELDER_DRAGON10.00
01516+ELDER_DRAGON00.00

Reinforcement Learning AI Model

Now that we have our data modelled as an MDP, we can apply Reinforcement Learning. In short, this applied a model that simulates thousands of games and learns how good or bad each decision is towards reaching a win given the team’s current position.

What makes this AI is its ability to learn from its own trial and error experience. It starts with zero knowledge about the game but, as it is rewarded for reaching a win and punished for reaching a loss, it begins to recognise and remember which decisions are better than others. Our first models start with no knowledge but I later demonstrate the impact initial information about decisions can be fed into the model to represent a person’s preferences.

So how is the model learning? In short, we use Monte Carlo learning whereby each episode is a simulation of a game based on our MDP probabilities and depending on the outcome for the team, our return will vary (+1 terminal reward for win and -1 terminal reward for loss). The value of each action taken in this episode is then updated accordingly based on whether the outcome was a win or loss.

In Monte Carlo learning, we have a parameter 'gamma' that discounts the rewards and will give a higher value to immediate steps than later one. In our model, this can be understood by the fact that as we reach later stages of the games, the decisions we make will have a much larger impact on the final outcome than those made in the first few minutes. For example, losing a team fight in minute 50 is much more likely to lead to a loss than losing a team fight in the first 5 minutes.

In [45]:

alpha = 0.1
gamma = 0.9
num_episodes = 100
epsilon = 0.1

reward = RedMatchMDP['Reward']

StartState = 1
StartEvent = '+KILLS'
StartAction = '+OUTER_TURRET'

In [46]:

def MCModelv1(data, alpha, gamma, epsilon, reward, StartState, StartEvent, StartAction, num_episodes):
    
    # Initiatise variables appropiately
    
    data['V'] = 0
 
    
    outcomes = pd.DataFrame()
    episode_return = pd.DataFrame()
    actions_output = pd.DataFrame()
    
    for e in range(0,num_episodes):
        
        action = []

        current_state = StartState
        current_action = StartEvent
        next_action = StartAction 
   
        actions = pd.DataFrame()
 
        for a in range(0,100):
            
            action_table = pd.DataFrame()

            
            if (current_action=="1") | (current_action=="0") | (current_state==79):
                continue
            else:
                
                data_e = data[(data['StartState']==current_state)&(data['Event1']==current_action)]

                data_e = data_e.sort_values('Probability')
                data_e['CumProb'] = data_e['Probability'].cumsum()
                data_e['CumProb'] = np.round(data_e['CumProb'],4)

                
                rng = np.round(np.random.random()*data_e['CumProb'].max(),4)
                action_table = data_e[ data_e['CumProb'] >= rng]
                action_table = action_table[ action_table['CumProb'] == action_table['CumProb'].min()]
                action_table = action_table.reset_index()
                
                action = action_table['Event2'][0]
                
                if action == "1":
                    step_reward = 10*(gamma**a)
                elif action == "0":
                    step_reward = -10*(gamma**a)
                else:
                    step_reward = -0.005*(gamma**a)
                
                action_table['StepReward'] = step_reward
                

                action_table['Episode'] = e
                action_table['Action'] = a
                
                current_action = action
                current_state = current_state+1
                
                
                actions = actions.append(action_table)

        actions_output = actions_output.append(actions)
                
        episode_return = actions['StepReward'].sum()

                
        actions['Return']= episode_return
                
        data = data.merge(actions[['StartState','EndState','Event1','Event2','Return']], how='left',on =['StartState','EndState','Event1','Event2'])
        data['Return'] = data['Return'].fillna(0)    
             
        data['V'] = data['V'] + alpha*(data['Return']-data['V'])
        data = data.drop('Return', 1)
        
        
                
        if current_action=="1":
            outcome = "WIN"
        elif current_action=="0":
            outcome = "LOSS"
        else:
            outcome = "INCOMPLETE"
        outcome = pd.DataFrame({'Epsiode':[e],'Outcome':[outcome]})
        outcomes = outcomes.append(outcome)

        
        

        
   
        
    
        
    optimal_policy_table = data[ ( data['StartState']==StartState) & (data['Event1']==StartEvent)&(data['Event2']==StartAction)]
     
    for i in range(2,79):
        optimal_V = data[data['StartState']==i]['V'].max()
        optimal_policy = data[ ( data['V']==optimal_V) & (data['StartState']==i)]      
        optimal_policy_table = optimal_policy_table.append(optimal_policy)
                
    return(outcomes,actions_output,data,optimal_policy_table)
    

In [47]:

start_time = timeit.default_timer()


Mdl = MCModelv1(data=RedMatchMDP, alpha = alpha, gamma=gamma, epsilon = epsilon, reward = reward,
                StartState=StartState, StartEvent=StartEvent,StartAction=StartAction,
                num_episodes = num_episodes)

elapsed = timeit.default_timer() - start_time

print("Time taken to run model:",np.round(elapsed/60,2),"mins")
Time taken to run model: 0.65 mins

In [48]:

Mdl[3].head()

Out[48]:

 StartStateEndStateEvent1Event2ProbabilityRewardV
112+KILLS+OUTER_TURRET0.0585190-0.000142
28623-KILLS+KILLS0.40529900.055403
40634+KILLS-KILLS0.38006600.027542
74845-KILLS+KILLS0.34598200.062849
86156+KILLS+RIFT_HERALD0.01204800.038553

RL Model V2

This is a good start but our first model requires the first action to be provided. Instead, we now repeat the process but enable it to calculate the optimal first action when none is given.

In [49]:

def MCModelv2(data, alpha, gamma, epsilon, reward, StartState, StartEvent, StartAction, num_episodes):
    
    # Initiatise variables appropiately
    
    data['V'] = 0
 
    
    outcomes = pd.DataFrame()
    episode_return = pd.DataFrame()
    actions_output = pd.DataFrame()
    
    for e in range(0,num_episodes):
        action = []

        current_state = StartState
        current_action = StartEvent
         
        
      
            
            
        actions = pd.DataFrame()
 
        for a in range(0,100):
            
            action_table = pd.DataFrame()

            
            if (current_action=="1") | (current_action=="0") | (current_state==79):
                continue
            else:
                
                data_e = data[(data['StartState']==current_state)&(data['Event1']==current_action)]

                data_e = data_e[data_e['Probability']>0]

                
                if (StartAction is None)&(a==0):
                    random_first_action = data_e.sample()
                    action_table = random_first_action
                    action_table = action_table.reset_index()
                    action = action_table['Event2'][0]
                elif (a==0):
                    action_table = data_e[ data_e['Event2'] ==StartAction]
                    action = StartAction
                else:
                    data_e = data_e.sort_values('Probability')
                    data_e['CumProb'] = data_e['Probability'].cumsum()
                    data_e['CumProb'] = np.round(data_e['CumProb'],4)
                    rng = np.round(np.random.random()*data_e['CumProb'].max(),4)
                    action_table = data_e[ data_e['CumProb'] >= rng]
                    action_table = action_table[ action_table['CumProb'] == action_table['CumProb'].min()]
                    action_table = action_table.reset_index()

                    action = action_table['Event2'][0]
                if action == "1":
                    step_reward = 10*(gamma**a)
                elif action == "0":
                    step_reward = -10*(gamma**a)
                else:
                    step_reward = -0.005*(gamma**a)

                action_table['StepReward'] = step_reward


                action_table['Episode'] = e
                action_table['Action'] = a

                current_action = action
                current_state = current_state+1


                actions = actions.append(action_table)

        actions_output = actions_output.append(actions)
                
        episode_return = actions['StepReward'].sum()

                
        actions['Return']= episode_return
                
        data = data.merge(actions[['StartState','EndState','Event1','Event2','Return']], how='left',on =['StartState','EndState','Event1','Event2'])
        data['Return'] = data['Return'].fillna(0)    
             
        data['V'] = data['V'] + alpha*(data['Return']-data['V'])
        data = data.drop('Return', 1)
        
        
                
        if current_action=="1":
            outcome = "WIN"
        elif current_action=="0":
            outcome = "LOSS"
        else:
            outcome = "INCOMPLETE"
        outcome = pd.DataFrame({'Epsiode':[e],'Outcome':[outcome]})
        outcomes = outcomes.append(outcome)

        
        

        
   
        
    
        if StartAction is None:
            optimal_policy_table = pd.DataFrame()
            for i in range(1,79):
                optimal_V = data[data['StartState']==i]['V'].max()
                optimal_policy = data[ ( data['V']==optimal_V) & (data['StartState']==i)]      
                optimal_policy_table = optimal_policy_table.append(optimal_policy)        
        else:
            optimal_policy_table = data[ ( data['StartState']==StartState) & (data['Event1']==StartEvent)&(data['Event2']==StartAction)]
            for i in range(2,79):
                optimal_V = data[data['StartState']==i]['V'].max()
                optimal_policy = data[ ( data['V']==optimal_V) & (data['StartState']==i)]      
                optimal_policy_table = optimal_policy_table.append(optimal_policy)

    return(outcomes,actions_output,data,optimal_policy_table)
    

In [50]:

alpha = 0.1
gamma = 0.9
num_episodes = 100
epsilon = 0.1

reward = RedMatchMDP['Reward']

StartState = 1
StartEvent = '+KILLS'
StartAction = None


start_time = timeit.default_timer()


Mdl2 = MCModelv2(data=RedMatchMDP, alpha = alpha, gamma=gamma, epsilon = epsilon, reward = reward,
                StartState=StartState, StartEvent=StartEvent,StartAction=None,
                num_episodes = num_episodes)

elapsed = timeit.default_timer() - start_time

print("Time taken to run model:",np.round(elapsed/60,2),"mins")
Time taken to run model: 1.24 mins

In [51]:

Mdl2[3].head(30)

Out[51]:

 StartStateEndStateEvent1Event2ProbabilityRewardV
1112+KILLS-OUTER_TURRET0.03469700.059365
30923-OUTER_TURRET+OUTER_TURRET0.30084700.056334
43034+OUTER_TURRET-DRAGON0.07444700.055783
79345-DRAGON+OUTER_TURRET0.15210400.055781
89156+OUTER_TURRET-OUTER_TURRET0.26194100.055770
128867-OUTER_TURRET-DRAGON0.11009200.055765
160678-DRAGON+KILLS0.26567200.056417
172089+KILLS+BARON_NASHOR0.00585900.055783
2144910+BARON_NASHOR-KILLS0.31250000.055783
23981011+OUTER_TURRET+KILLS0.18598000.059142
27061112+KILLS+KILLS0.11857700.058965
32441213-OUTER_TURRET-KILLS0.22792000.055783
35961314-KILLS-KILLS0.12140900.054589
39751415-KILLS-INNER_TURRET0.13837600.074098
41681516+KILLS-KILLS0.29748100.056211
48771617-DRAGON-INNER_TURRET0.15857600.055783
53561718-INNER_TURRET-KILLS0.23632400.055783
56981819-KILLS+KILLS0.26654600.057560
59401920+KILLS+KILLS0.12820500.058518
64142021+KILLS-DRAGON0.03756900.055783
71282122-DRAGON+KILLS0.23574100.056081
73462223+KILLS10.00230400.056324
77952324+KILLS+INHIBITOR0.04981800.033658
83842425+INHIBITOR+DRAGON0.03703700.035062
87172526+DRAGON+INNER_TURRET0.25477700.035054
91962627+INNER_TURRET+KILLS0.23030300.035203
95342728+KILLS+NEXUS_TURRET0.07112400.035062
101622829+NEXUS_TURRET10.28061200.035062
107212930-BASE_TURRET+INHIBITOR0.57692300.024197
109653031+INHIBITOR+ELDER_DRAGON0.01612900.023902

RL Model V3

It quickly became apparent that our model was not following the structure of the game correctly because we had set no limitations on the number of turrets available to destroy on either team.

Therefore, it was taking more objectives than possible and so we now introduce a rule that removes the turrets, rift heralds and inhibitors after so many are taken by one team.

In [52]:

def MCModelv3(data, alpha, gamma, epsilon, reward, StartState, StartEvent, StartAction, num_episodes):
    
    # Initiatise variables appropiately
    
    data['V'] = 0
    data_output = data
    
    outcomes = pd.DataFrame()
    episode_return = pd.DataFrame()
    actions_output = pd.DataFrame()
    
    for e in range(0,num_episodes):
        action = []

        current_state = StartState
        current_action = StartEvent
        
        
        data_e1 = data
    
    
        actions = pd.DataFrame()

        for a in range(0,100):
            
            action_table = pd.DataFrame()
       
           
            if (current_action=="1") | (current_action=="0") | (current_state==79):
                continue
            else:
                if a==0:
                    data_e1=data_e1
                    
                elif (len(individual_actions_count[individual_actions_count['Event2']=="+RIFT_HERALD"])==1):
                    data_e1_e1 = data_e1[(data_e1['Event2']!='+RIFT_HERALD')|(data_e1['Event2']!='-RIFT_HERALD')]
                    
                elif (len(individual_actions_count[individual_actions_count['Event2']=="-RIFT_HERALD"])==1):
                    data_e1 = data_e1[(data_e1['Event2']!='+RIFT_HERALD')|(data_e1['Event2']!='-RIFT_HERALD')]
                
                elif (len(individual_actions_count[individual_actions_count['Event2']=="+OUTER_TURRET"])==3):
                    data_e1 = data_e1[data_e1['Event2']!='+OUTER_TURRET']
                elif (len(individual_actions_count[individual_actions_count['Event2']=="-OUTER_TURRET"])==3):
                    data_e1 = data_e1[data_e1['Event2']!='-OUTER_TURRET']
                    
                elif (len(individual_actions_count[individual_actions_count['Event2']=="+INNER_TURRET"])==3):
                    data_e1 = data_e1[data_e1['Event2']!='+INNER_TURRET']
                elif (len(individual_actions_count[individual_actions_count['Event2']=="-INNER_TURRET"])==3):
                    data_e1 = data_e1[data_e1['Event2']!='-INNER_TURRET']
                    
                elif (len(individual_actions_count[individual_actions_count['Event2']=="+BASE_TURRET"])==3):
                    data_e1 = data_e1[data_e1['Event2']!='+BASE_TURRET']
                elif (len(individual_actions_count[individual_actions_count['Event2']=="-BASE_TURRET"])==3):
                    data_e1 = data_e1[data_e1['Event2']!='-BASE_TURRET']
                    
                elif (len(individual_actions_count[individual_actions_count['Event2']=="+INHIBITOR"])==3):
                    data_e1 = data_e1[data_e1['Event2']!='+INHIBITOR']
                elif (len(individual_actions_count[individual_actions_count['Event2']=="-INHIBITOR"])==3):
                    data_e1 = data_e1[data_e1['Event2']!='-INHIBITOR']
                    
                elif (len(individual_actions_count[individual_actions_count['Event2']=="+NEXUS_TURRET"])==2):
                    data_e1 = data_e1[data_e1['Event2']!='+NEXUS_TURRET']
                elif (len(individual_actions_count[individual_actions_count['Event2']=="-NEXUS_TURRET"])==2):
                    data_e1 = data_e1[data_e1['Event2']!='-NEXUS_TURRET']
                
                       
                else:
                    data_e1 = data_e1

                
                data_e = data_e1[(data_e1['StartState']==current_state)&(data_e1['Event1']==current_action)]
                
                data_e = data_e[data_e['Probability']>0]
                
                if (StartAction is None)&(a==0):
                    random_first_action = data_e.sample()
                    action_table = random_first_action
                    action_table = action_table.reset_index()
                    action = action_table['Event2'][0]
                elif (a==0):
                    action_table = data_e[ data_e['Event2'] ==StartAction]
                    action = StartAction
                else:
                    data_e = data_e.sort_values('Probability')
                    data_e['CumProb'] = data_e['Probability'].cumsum()
                    data_e['CumProb'] = np.round(data_e['CumProb'],4)
                    

                    rng = np.round(np.random.random()*data_e['CumProb'].max(),4)
                    action_table = data_e[ data_e['CumProb'] >= rng]
                    action_table = action_table[ action_table['CumProb'] == action_table['CumProb'].min()]
                    action_table = action_table.reset_index()

                    action = action_table['Event2'][0]
                if action == "1":
                    step_reward = 10*(gamma**a)
                elif action == "0":
                    step_reward = -10*(gamma**a)
                else:
                    step_reward = -0.005*(gamma**a)

                action_table['StepReward'] = step_reward


                action_table['Episode'] = e
                action_table['Action'] = a

                current_action = action
                current_state = current_state+1

                
                actions = actions.append(action_table)
                
                individual_actions_count = actions
            

        actions_output = actions_output.append(actions)
                
        episode_return = actions['StepReward'].sum()

                
        actions['Return']= episode_return
                
        data_output = data_output.merge(actions[['StartState','EndState','Event1','Event2','Return']], how='left',on =['StartState','EndState','Event1','Event2'])
        data_output['Return'] = data_output['Return'].fillna(0)    
             
        data_output['V'] = data_output['V'] + alpha*(data_output['Return']-data_output['V'])
        data_output = data_output.drop('Return', 1)
        
        
                
        if current_action=="1":
            outcome = "WIN"
        elif current_action=="0":
            outcome = "LOSS"
        else:
            outcome = "INCOMPLETE"
        outcome = pd.DataFrame({'Epsiode':[e],'Outcome':[outcome]})
        outcomes = outcomes.append(outcome)

        
        

        optimal_policy_table = pd.DataFrame()
   
        
        if (StartAction is None):
            
            optimal_policy_table =    data_output[ (data_output['StartState']==StartState)&(data_output['Event1']==StartEvent) &
                (data_output['V']==(data_output[(data_output['StartState']==StartState)&(data_output['Event1']==StartEvent)]['V'].max()))  ]
            for i in range(2,79):
                optimal_V = data_output[(data_output['StartState']==i)]['V'].max()
                optimal_policy = data_output[ ( data_output['V']==optimal_V) & (data_output['StartState']==i)]      
                optimal_policy_table = optimal_policy_table.append(optimal_policy)        
        else:
            optimal_policy_table = data_output[ ( data_output['StartState']==StartState) & (data_output['Event1']==StartEvent)&(data_output['Event2']==StartAction)]
            for i in range(2,79):
                optimal_V = data_output[data_output['StartState']==i]['V'].max()
                optimal_policy = data_output[ ( data_output['V']==optimal_V) & (data_output['StartState']==i)]      
                optimal_policy_table = optimal_policy_table.append(optimal_policy)
                
        if (StartAction is None):
            currentpath_action = StartEvent
            optimal_path = pd.DataFrame()

            for i in range(1,79):
                StartPathState = i
                nextpath_action = data_output [ (data_output['V'] == data_output[ (data_output['StartState']==StartPathState) & (data_output['Event1']==currentpath_action) ]['V'].max()) & 
                                               (data_output['StartState']==StartPathState) & (data_output['Event1']==currentpath_action)  ]
                if (nextpath_action['V'].max()==0):
                    break
                else:
                    nextpath_action = nextpath_action.reset_index(drop=True)
                    currentpath_action = nextpath_action['Event2'][0]
                    optimal_path = optimal_path.append(nextpath_action)
                    
        else:
            currentpath_action = StartEvent
            optimal_path = data_output[(data_output['StartState']==StartPathState) & (data_output['Event1']==currentpath_action) & (data_output['Event2']==StartAction) ]
            for i in range(2,79):
                StartPathState = i
                nextpath_action = data_output [ (data_output['V'] == data_output[ (data_output['StartState']==StartPathState) & (data_output['Event1']==currentpath_action) ]['V'].max()) & 
                                               (data_output['StartState']==StartPathState) & (data_output['Event1']==currentpath_action)  ]
                if (nextpath_action['V'].max()==0):
                    break
                else:

                    nextpath_action = nextpath_action.reset_index(drop=True)
                    currentpath_action = nextpath_action['Event2'][0]
                    optimal_path = optimal_path.append(nextpath_action)


                
                
                
    

        



    return(outcomes,actions_output,data_output,optimal_policy_table,optimal_path)
    

In [53]:

alpha = 0.1
gamma = 0.9
num_episodes = 100
epsilon = 0.1

reward = RedMatchMDP['Reward']

StartState = 1
StartEvent = '+KILLS'
StartAction = None


start_time = timeit.default_timer()


Mdl3 = MCModelv3(data=RedMatchMDP, alpha = alpha, gamma=gamma, epsilon = epsilon, reward = reward,
                StartState=StartState, StartEvent=StartEvent,StartAction=StartAction,
                num_episodes = num_episodes)

elapsed = timeit.default_timer() - start_time

print("Time taken to run model:",np.round(elapsed/60,2),"mins")
print("Avg Time taken per episode:", np.round(elapsed/num_episodes,2),"secs")
Time taken to run model: 1.93 mins
Avg Time taken per episode: 1.16 secs

In [54]:

Mdl3[3].head()

Out[54]:

 StartStateEndStateEvent1Event2ProbabilityRewardV
1212+KILLS-DRAGON0.03159000.094938
34123-DRAGON-OUTER_TURRET0.14285700.094465
52934-OUTER_TURRET+OUTER_TURRET0.36885200.084538
64845+OUTER_TURRET-KILLS0.14684300.094469
97856-KILLS-KILLS0.16074300.087776

In [55]:

Mdl3[4]

Out[55]:

 StartStateEndStateEvent1Event2ProbabilityRewardV
012+KILLS-DRAGON0.03159000.094938
023-DRAGON-OUTER_TURRET0.14285700.094465
034-OUTER_TURRET+OUTER_TURRET0.36885200.084538
045+OUTER_TURRET-KILLS0.14684300.094469
056-KILLS-KILLS0.16074300.087776
067-KILLS-OUTER_TURRET0.27147800.096109
078-OUTER_TURRET+OUTER_TURRET0.18610100.093135
089+OUTER_TURRET+KILLS0.19496000.074443
0910+KILLS+OUTER_TURRET0.21661700.072786
01011+OUTER_TURRET+DRAGON0.12446400.082719
01112+DRAGON-KILLS0.25000000.073039
01213-KILLS+KILLS0.26699500.095967
01314+KILLS+DRAGON0.07269000.091666
01415+DRAGON+KILLS0.31498500.094469
01516+KILLS-KILLS0.29748100.073666
01617-KILLS-INNER_TURRET0.12431900.093982
01718-INNER_TURRET+KILLS0.18161900.093859
01819+KILLS+BASE_TURRET0.02969200.094469
01920+BASE_TURRET+DRAGON0.05555600.094469
02021+DRAGON-KILLS0.19574500.095296
02122-KILLS-KILLS0.10860700.107893
02223-KILLS10.00320900.094469

Part 1 Conclusion

So we have performed some interesting exploratory analysis, modelled the environment as an MDP and even created a RL model, however, there are still many challenges to overcome.

The biggest issue right now is our output doesn't account for cumulative success or failures.

In other words, our model thinks we are just as likely to take good objectives in later stages irrespective of whether we have lost each one before and would likely be behind.

We can see this emphasised by our output getting us to a win from 20+ minutes by simply taking objective after objective even though we had lost the majority of objectives before it. There is no accountability for being in a losing position in our model.

To fix this, we must re-asses our MDP and consider features that would account for the long term success or failure of taking or losing objectives.

This is a good start, in our next part we will redesign our MDP to account for this and fix some other issues.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值