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]:

  League Year Season Type blueTeamTag bResult rResult redTeamTag gamelength blueTop blueTopChamp blueJungle blueJungleChamp blueMiddle blueMiddleChamp blueADC blueADCChamp blueSupport blueSupportChamp redTop redTopChamp redJungle redJungleChamp redMiddle redMiddleChamp redADC redADCChamp redSupport redSupportChamp Address
0 NALCS 2015 Spring Season TSM 1 0 C9 40 Dyrus Irelia Santorin RekSai Bjergsen Ahri WildTurtle Jinx Lustboy Janna Balls Gnar Meteos Elise Hai Fizz Sneaky Sivir LemonNation Thresh http://matchhistory.na.leagueoflegends.com/en/...
1 NALCS 2015 Spring Season CST 0 1 DIG 38 Cris Gnar Impaler Rengar Jesiz Ahri Mash Caitlyn Sheep Leona Gamsu Irelia Crumbzz JarvanIV Shiphtur Azir CoreJJ Corki KiWiKiD Annie http://matchhistory.na.leagueoflegends.com/en/...
2 NALCS 2015 Spring Season WFX 1 0 GV 40 Flaresz Renekton ShorterACE Rengar Pobelter Fizz Altec Sivir Gleeb Annie Hauntzer Sion Saintvicious LeeSin Keane Azir Cop Corki BunnyFuFuu Janna http://matchhistory.na.leagueoflegends.com/en/...
3 NALCS 2015 Spring Season TIP 0 1 TL 41 Rhux Irelia Rush JarvanIV XiaoWeiXiao Leblanc Apollo Sivir Adrian Thresh Quas Gnar IWDominate Nunu Fenix Lulu KEITH KogMaw Xpecial Janna http://matchhistory.na.leagueoflegends.com/en/...
4 NALCS 2015 Spring Season CLG 1 0 T8 35 Benny Gnar Xmithie JarvanIV Link Lissandra Doublelift Tristana aphromoo Janna CaliTrlolz8 Sion Porpoise8 RekSai Slooshi8 Lulu Maplestreet8 Corki Dodo8 Annie http://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]:

  League Year Season Type blueTeamTag bResult rResult redTeamTag gamelength blueTop blueTopChamp blueJungle blueJungleChamp blueMiddle blueMiddleChamp blueADC blueADCChamp blueSupport blueSupportChamp redTop redTopChamp redJungle redJungleChamp redMiddle redMiddleChamp redADC redADCChamp redSupport redSupportChamp Address id
0 NALCS 2015 Spring Season TSM 1 0 C9 40 Dyrus Irelia Santorin RekSai Bjergsen Ahri WildTurtle Jinx Lustboy Janna Balls Gnar Meteos Elise Hai Fizz Sneaky Sivir LemonNation Thresh http://matchhistory.na.leagueoflegends.com/en/... fbb300951ad8327c
1 NALCS 2015 Spring Season CST 0 1 DIG 38 Cris Gnar Impaler Rengar Jesiz Ahri Mash Caitlyn Sheep Leona Gamsu Irelia Crumbzz JarvanIV Shiphtur Azir CoreJJ Corki KiWiKiD Annie http://matchhistory.na.leagueoflegends.com/en/... 055b17da8456fdc8
2 NALCS 2015 Spring Season WFX 1 0 GV 40 Flaresz Renekton ShorterACE Rengar Pobelter Fizz Altec Sivir Gleeb Annie Hauntzer Sion Saintvicious LeeSin Keane Azir Cop Corki BunnyFuFuu Janna http://matchhistory.na.leagueoflegends.com/en/... 8e8a9b58df366e2d
3 NALCS 2015 Spring Season TIP 0 1 TL 41 Rhux Irelia Rush JarvanIV XiaoWeiXiao Leblanc Apollo Sivir Adrian Thresh Quas Gnar IWDominate Nunu Fenix Lulu KEITH KogMaw Xpecial Janna http://matchhistory.na.leagueoflegends.com/en/... 0ed1cd0e0e57329c
4 NALCS 2015 Spring Season CLG 1 0 T8 35 Benny Gnar Xmithie JarvanIV Link Lissandra Doublelift Tristana aphromoo Janna CaliTrlolz8 Sion Porpoise8 RekSai Slooshi8 Lulu Maplestreet8 Corki Dodo8 Annie http://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]:

  Address Team Time Victim Killer Assist_1 Assist_2 Assist_3 Assist_4 x_pos y_pos id Minute
4462 http://matchhistory.na.leagueoflegends.com/en/... Blue 6.032 CLG Huhi TSM Svenskeren TSM Bjergsen NaN NaN NaN 7825 8666 55109b5a7a91ae87 6
4463 http://matchhistory.na.leagueoflegends.com/en/... Blue 9.428 CLG Huhi TSM Biofrost TSM Bjergsen TSM Doublelift NaN NaN 8728 8751 55109b5a7a91ae87 9
4464 http://matchhistory.na.leagueoflegends.com/en/... Blue 9.780 CLG Xmithie TSM Bjergsen TSM Hauntzer TSM Svenskeren NaN NaN 8655 1172 55109b5a7a91ae87 9
4465 http://matchhistory.na.leagueoflegends.com/en/... Blue 10.252 CLG Stixxay TSM Doublelift TSM Biofrost NaN NaN NaN 3621 11607 55109b5a7a91ae87 10
4466 http://matchhistory.na.leagueoflegends.com/en/... Blue 12.993 CLG Darshan TSM Doublelift TSM Biofrost NaN NaN NaN 5674 12903 55109b5a7a91ae87 12

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]:

  id Team Minute Time Avg Count
4 0001f4374a03c133 Red 4 4.6350 1
5 0001f4374a03c133 Red 6 6.0640 1
0 0001f4374a03c133 Blue 8 8.1940 1
6 0001f4374a03c133 Red 10 10.4780 1
7 0001f4374a03c133 Red 17 17.3870 2
1 0001f4374a03c133 Blue 18 18.0980 1
8 0001f4374a03c133 Red 18 18.0670 2
9 0001f4374a03c133 Red 21 21.7900 1
2 0001f4374a03c133 Blue 22 22.5125 2
10 0001f4374a03c133 Red 22 22.4578 5
3 0001f4374a03c133 Blue 31 31.1685 2
11 0001f4374a03c133 Red 31 31.3880 1
12 0001f4374a03c133 Red 32 32.7634 5

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]:

  Address Team Time Lane Type id Minute
6740 http://matchhistory.na.leagueoflegends.com/en/... Blue 11.182 TOP_LANE OUTER_TURRET 0001f4374a03c133 11
57600 http://matchhistory.na.leagueoflegends.com/en/... Red 11.006 BOT_LANE OUTER_TURRET 0001f4374a03c133 11
6741 http://matchhistory.na.leagueoflegends.com/en/... Blue 16.556 BOT_LANE OUTER_TURRET 0001f4374a03c133 16
57601 http://matchhistory.na.leagueoflegends.com/en/... Red 16.145 TOP_LANE OUTER_TURRET 0001f4374a03c133 16
57598 http://matchhistory.na.leagueoflegends.com/en/... Red 18.378 MID_LANE OUTER_TURRET 0001f4374a03c133 18
57602 http://matchhistory.na.leagueoflegends.com/en/... Red 24.943 MID_LANE INNER_TURRET 0001f4374a03c133 24
57599 http://matchhistory.na.leagueoflegends.com/en/... Red 25.463 TOP_LANE INNER_TURRET 0001f4374a03c133 25
57597 http://matchhistory.na.leagueoflegends.com/en/... Red 26.330 BOT_LANE INNER_TURRET 0001f4374a03c133 26
57594 http://matchhistory.na.leagueoflegends.com/en/... Red 33.153 MID_LANE BASE_TURRET 0001f4374a03c133 33
57595 http://matchhistory.na.leagueoflegends.com/en/... Red 33.326 MID_LANE NEXUS_TURRET 0001f4374a03c133 33
57596 http://matchhistory.na.leagueoflegends.com/en/... Red 33.408 MID_LANE NEXUS_TURRET 0001f4374a03c133 33
111295 http://matchhistory.na.leagueoflegends.com/en/... Red 33.222 MID_LANE INHIBITOR 0001f4374a03c133 33
35900 http://matchhistory.oce.leagueoflegends.com/en... Blue 12.603 BOT_LANE OUTER_TURRET 0016710a48fdd46d 12

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]:

  id Team Minute Time Avg Count Time Lane Type
0 0001f4374a03c133 Red 4 4.6350 1.0 NaN NaN NaN
1 0001f4374a03c133 Red 6 6.0640 1.0 NaN NaN NaN
2 0001f4374a03c133 Blue 8 8.1940 1.0 NaN NaN NaN
3 0001f4374a03c133 Red 10 10.4780 1.0 NaN NaN NaN
4 0001f4374a03c133 Red 17 17.3870 2.0 NaN NaN NaN
5 0001f4374a03c133 Blue 18 18.0980 1.0 NaN NaN NaN
6 0001f4374a03c133 Red 18 18.0670 2.0 18.378 MID_LANE OUTER_TURRET
7 0001f4374a03c133 Red 21 21.7900 1.0 NaN NaN NaN
8 0001f4374a03c133 Blue 22 22.5125 2.0 NaN NaN NaN
9 0001f4374a03c133 Red 22 22.4578 5.0 NaN NaN NaN
10 0001f4374a03c133 Blue 31 31.1685 2.0 NaN NaN NaN
11 0001f4374a03c133 Red 31 31.3880 1.0 NaN NaN NaN
12 0001f4374a03c133 Red 32 32.7634 5.0 NaN NaN NaN
13 0016710a48fdd46d Blue 5 5.9710 1.0 NaN NaN NaN
14 0016710a48fdd46d Red 8 8.3750 1.0 NaN NaN NaN
15 0016710a48fdd46d Blue 11 11.9720 1.0 NaN NaN NaN
16 0016710a48fdd46d Red 11 11.9130 1.0 NaN NaN NaN
17 0016710a48fdd46d Red 13 13.1030 1.0 NaN NaN NaN
18 0016710a48fdd46d Blue 17 17.2070 1.0 NaN NaN NaN
19 0016710a48fdd46d Red 17 17.9140 1.0 NaN NaN NaN

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]:

  Address Team Time Type id Type2 Minute
696 http://matchhistory.na.leagueoflegends.com/en/... Blue 23.444 EARTH_DRAGON 55109b5a7a91ae87 DRAGON 23
697 http://matchhistory.na.leagueoflegends.com/en/... Blue 31.069 WATER_DRAGON 55109b5a7a91ae87 DRAGON 31
698 http://matchhistory.na.leagueoflegends.com/en/... Blue 16.419 AIR_DRAGON 55109b5a7a91ae87 DRAGON 16
699 http://matchhistory.na.leagueoflegends.com/en/... Blue 32.022 EARTH_DRAGON e147296c928da5b4 DRAGON 32
700 http://matchhistory.na.leagueoflegends.com/en/... Blue 25.304 WATER_DRAGON e147296c928da5b4 DRAGON 25

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]:

  id Team_x Minute Time Avg Count Time_x Lane Type Team_y Time_y Type2
0 0001f4374a03c133 Red 4 4.635 1.0 NaN NaN NaN NaN NaN NaN
1 0001f4374a03c133 Red 6 6.064 1.0 NaN NaN NaN NaN NaN NaN
2 0001f4374a03c133 Blue 8 8.194 1.0 NaN NaN NaN NaN NaN NaN
3 0001f4374a03c133 Red 10 10.478 1.0 NaN NaN NaN NaN NaN NaN
94502 0001f4374a03c133 Blue 11 NaN NaN 11.182 TOP_LANE OUTER_TURRET Red 11.261 DRAGON

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]:

  id EventNum Team Minute Time KillCount StructType StructLane Monster
4 0001f4374a03c133 1 Red 4 4.635 1.0 NaN NaN NaN
5 0001f4374a03c133 2 Red 6 6.064 1.0 NaN NaN NaN
0 0001f4374a03c133 3 Blue 8 8.194 1.0 NaN NaN NaN
6 0001f4374a03c133 4 Red 10 10.478 1.0 NaN NaN NaN
57600 0001f4374a03c133 5 Red 11 11.006 NaN OUTER_TURRET BOT_LANE NaN

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]:

  id EventNum Team Minute Time KillCount StructType StructLane Monster Event
4 0001f4374a03c133 1 Red 4 4.635 1.0 NaN NaN NaN KILLS
5 0001f4374a03c133 2 Red 6 6.064 1.0 NaN NaN NaN KILLS
0 0001f4374a03c133 3 Blue 8 8.194 1.0 NaN NaN NaN KILLS
6 0001f4374a03c133 4 Red 10 10.478 1.0 NaN NaN NaN KILLS
57600 0001f4374a03c133 5 Red 11 11.006 NaN OUTER_TURRET BOT_LANE NaN OUTER_TURRET
6740 0001f4374a03c133 6 Blue 11 11.182 NaN OUTER_TURRET TOP_LANE NaN OUTER_TURRET
24027 0001f4374a03c133 7 Red 11 11.261 NaN NaN NaN DRAGON DRAGON
42565 0001f4374a03c133 8 Red 15 15.777 NaN NaN NaN RIFT_HERALD RIFT_HERALD
57601 0001f4374a03c133 9 Red 16 16.145 NaN OUTER_TURRET TOP_LANE NaN OUTER_TURRET
6741 0001f4374a03c133 10 Blue 16 16.556 NaN OUTER_TURRET BOT_LANE NaN OUTER_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]:

  EventNum id
0 14 1
1 16 3
2 17 4
3 18 7
4 19 12

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
0 0001f4374a03c133
1 0016710a48fdd46d
2 0016c9df37278448
3 0021b45647424cd5
4 00405293fb859241

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]:

  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 id 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
0 KILLS KILLS NaN KILLS OUTER_TURRET NaN DRAGON RIFT_HERALD OUTER_TURRET NaN KILLS DRAGON KILLS NaN OUTER_TURRET KILLS KILLS NaN BARON_NASHOR DRAGON INNER_TURRET INNER_TURRET INNER_TURRET DRAGON NaN KILLS KILLS BASE_TURRET INHIBITOR NEXUS_TURRET NEXUS_TURRET 0001f4374a03c133 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN KILLS DRAGON KILLS NaN NaN KILLS NaN NaN KILLS KILLS NaN OUTER_TURRET NaN NaN NaN KILLS NaN KILLS OUTER_TURRET NaN KILLS OUTER_TURRET NaN KILLS NaN KILLS NaN KILLS BARON_NASHOR KILLS 0016710a48fdd46d INNER_TURRET INNER_TURRET INNER_TURRET NaN KILLS BASE_TURRET NaN INHIBITOR KILLS NaN KILLS NEXUS_TURRET NEXUS_TURRET NaN NaN NaN 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
2 NaN NaN NaN NaN NaN NaN KILLS NaN NaN OUTER_TURRET NaN NaN KILLS NaN NaN NaN 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 0016c9df37278448 NEXUS_TURRET NaN 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 NaN NaN NaN
3 NaN KILLS KILLS KILLS NaN DRAGON RIFT_HERALD NaN OUTER_TURRET NaN OUTER_TURRET NaN DRAGON KILLS KILLS OUTER_TURRET NaN KILLS BARON_NASHOR NaN DRAGON INNER_TURRET KILLS INNER_TURRET BASE_TURRET INHIBITOR NaN KILLS NEXUS_TURRET BARON_NASHOR INNER_TURRET 0021b45647424cd5 NaN DRAGON INHIBITOR BASE_TURRET INHIBITOR NaN NaN KILLS NaN 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
4 KILLS NaN NaN NaN NaN OUTER_TURRET NaN NaN NaN NaN NaN NaN NaN KILLS NaN NaN OUTER_TURRET KILLS KILLS NaN OUTER_TURRET DRAGON BARON_NASHOR KILLS INNER_TURRET BASE_TURRET INHIBITOR KILLS INNER_TURRET KILLS BASE_TURRET 00405293fb859241 INHIBITOR NaN 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 NaN NaN NaN

In [24]:

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

Out[24]:

  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 id 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
0 NaN NaN KILLS NaN NaN OUTER_TURRET NaN NaN NaN OUTER_TURRET NaN NaN NaN KILLS NaN NaN NaN KILLS NaN NaN NaN NaN NaN NaN KILLS NaN NaN NaN NaN NaN NaN 0001f4374a03c133 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 KILLS NaN NaN NaN KILLS OUTER_TURRET NaN DRAGON KILLS NaN NaN KILLS NaN OUTER_TURRET DRAGON KILLS NaN OUTER_TURRET NaN NaN KILLS NaN NaN KILLS NaN KILLS NaN DRAGON NaN NaN NaN 0016710a48fdd46d NaN NaN NaN KILLS NaN NaN KILLS NaN NaN KILLS 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 NaN
2 KILLS KILLS KILLS OUTER_TURRET DRAGON KILLS NaN KILLS RIFT_HERALD NaN OUTER_TURRET DRAGON NaN DRAGON KILLS OUTER_TURRET NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 0016c9df37278448 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 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 KILLS NaN NaN NaN KILLS NaN NaN KILLS NaN KILLS NaN KILLS NaN NaN NaN NaN OUTER_TURRET NaN NaN KILLS NaN NaN NaN NaN NaN NaN KILLS NaN NaN NaN NaN 0021b45647424cd5 KILLS NaN NaN NaN NaN KILLS BARON_NASHOR NaN KILLS NaN 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 NaN
4 NaN KILLS KILLS KILLS OUTER_TURRET NaN DRAGON KILLS OUTER_TURRET KILLS KILLS DRAGON OUTER_TURRET NaN KILLS DRAGON NaN NaN NaN KILLS NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 00405293fb859241 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 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

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]:

  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 ... 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
0 0001f4374a03c133 KILLS KILLS NaN KILLS OUTER_TURRET NaN DRAGON RIFT_HERALD OUTER_TURRET NaN KILLS DRAGON KILLS NaN OUTER_TURRET KILLS KILLS NaN BARON_NASHOR DRAGON INNER_TURRET INNER_TURRET INNER_TURRET DRAGON NaN 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 NaN 1 34
1 0016710a48fdd46d NaN KILLS DRAGON KILLS NaN NaN KILLS NaN NaN KILLS KILLS NaN OUTER_TURRET NaN NaN NaN KILLS NaN KILLS OUTER_TURRET NaN KILLS OUTER_TURRET NaN KILLS NaN KILLS NaN KILLS BARON_NASHOR KILLS INNER_TURRET INNER_TURRET INNER_TURRET NaN KILLS BASE_TURRET NaN 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 NaN 1 40
2 0016c9df37278448 NaN NaN NaN NaN NaN NaN KILLS NaN NaN OUTER_TURRET NaN NaN KILLS NaN NaN NaN 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 NaN 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 NaN 1 40
3 0021b45647424cd5 NaN KILLS KILLS KILLS NaN DRAGON RIFT_HERALD NaN OUTER_TURRET NaN OUTER_TURRET NaN DRAGON KILLS KILLS OUTER_TURRET NaN KILLS BARON_NASHOR NaN DRAGON INNER_TURRET KILLS INNER_TURRET BASE_TURRET INHIBITOR NaN KILLS NEXUS_TURRET BARON_NASHOR INNER_TURRET NaN DRAGON INHIBITOR BASE_TURRET INHIBITOR NaN NaN 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 NaN 1 42
4 00405293fb859241 KILLS NaN NaN NaN NaN OUTER_TURRET NaN NaN NaN NaN NaN NaN NaN KILLS NaN NaN OUTER_TURRET KILLS KILLS NaN OUTER_TURRET DRAGON BARON_NASHOR KILLS INNER_TURRET BASE_TURRET INHIBITOR KILLS INNER_TURRET KILLS BASE_TURRET INHIBITOR NaN 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 NaN 1 39
5 00416ed438e29f14 NaN KILLS NaN NaN NaN OUTER_TURRET NaN KILLS KILLS DRAGON NaN NaN OUTER_TURRET KILLS KILLS BARON_NASHOR NaN OUTER_TURRET NaN INNER_TURRET INNER_TURRET NaN BARON_NASHOR KILLS BASE_TURRET NaN NaN NaN INHIBITOR KILLS INNER_TURRET BASE_TURRET BASE_TURRET INHIBITOR NaN KILLS NaN 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 1 41
6 007802c051352561 NaN KILLS DRAGON KILLS KILLS RIFT_HERALD NaN NaN OUTER_TURRET OUTER_TURRET INNER_TURRET NaN NaN NaN OUTER_TURRET NaN KILLS DRAGON INNER_TURRET DRAGON NaN NaN KILLS BARON_NASHOR NaN KILLS KILLS ELDER_DRAGON INNER_TURRET BASE_TURRET INHIBITOR BARON_NASHOR BASE_TURRET NaN 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 NaN 1 44
7 0091705b03924485 NaN NaN NaN NaN NaN NaN DRAGON NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN INHIBITOR 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 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 0 27
8 00986b51908a63c3 NaN KILLS KILLS KILLS KILLS KILLS NaN OUTER_TURRET DRAGON OUTER_TURRET NaN INNER_TURRET KILLS OUTER_TURRET INNER_TURRET DRAGON KILLS NaN NaN DRAGON NaN NaN KILLS INNER_TURRET BASE_TURRET INHIBITOR NaN NaN NaN KILLS BASE_TURRET NaN NaN INHIBITOR NaN NaN KILLS INHIBITOR BASE_TURRET ... NaN NaN NaN KILLS INNER_TURRET NaN NaN NaN KILLS NaN BARON_NASHOR KILLS KILLS ELDER_DRAGON BASE_TURRET NaN NaN NaN KILLS KILLS NEXUS_TURRET NEXUS_TURRET NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 0 69
9 00b13dbf1bd7aff0 NaN KILLS OUTER_TURRET NaN DRAGON NaN DRAGON OUTER_TURRET NaN OUTER_TURRET NaN KILLS NaN NaN KILLS BARON_NASHOR NaN NaN NaN NaN DRAGON NaN 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 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 0 36
10 00b2492ce66406e9 KILLS KILLS KILLS NaN NaN NaN NaN KILLS KILLS KILLS KILLS KILLS OUTER_TURRET OUTER_TURRET DRAGON RIFT_HERALD OUTER_TURRET BARON_NASHOR INNER_TURRET NaN INNER_TURRET KILLS INNER_TURRET DRAGON KILLS BASE_TURRET BASE_TURRET BASE_TURRET INHIBITOR KILLS NEXUS_TURRET INHIBITOR NEXUS_TURRET INHIBITOR 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 NaN NaN NaN NaN NaN NaN 1 29
11 00df84280ed2625b KILLS NaN NaN DRAGON NaN KILLS NaN KILLS KILLS OUTER_TURRET OUTER_TURRET KILLS NaN BARON_NASHOR NaN KILLS OUTER_TURRET DRAGON INNER_TURRET NaN KILLS KILLS KILLS BARON_NASHOR DRAGON INNER_TURRET KILLS NaN BASE_TURRET INHIBITOR NEXUS_TURRET KILLS INNER_TURRET BASE_TURRET KILLS NEXUS_TURRET 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 NaN NaN NaN NaN 1 39
12 00e4eb2c47c14065 NaN OUTER_TURRET NaN OUTER_TURRET NaN DRAGON RIFT_HERALD NaN NaN NaN KILLS OUTER_TURRET NaN NaN BARON_NASHOR INNER_TURRET NaN NaN NaN NaN NaN NaN NaN NaN NaN 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 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 0 31
13 00ecc69a0f024ada NaN NaN NaN NaN NaN NaN OUTER_TURRET NaN NaN OUTER_TURRET NaN NaN NaN NaN NaN NaN NaN NaN INHIBITOR NaN NaN NaN INHIBITOR NaN NaN NaN 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 NaN NaN NaN NaN NaN NaN NaN NaN NaN 0 34
14 00fc8c3957193d35 KILLS KILLS OUTER_TURRET NaN INNER_TURRET NaN DRAGON NaN KILLS NaN OUTER_TURRET OUTER_TURRET DRAGON NaN KILLS NaN NaN NaN DRAGON KILLS BARON_NASHOR NaN KILLS INNER_TURRET NaN KILLS BASE_TURRET INHIBITOR KILLS INNER_TURRET KILLS NaN DRAGON NaN KILLS NaN 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 1 32
15 0122e715a37611f0 NaN NaN KILLS NaN NaN NaN NaN DRAGON KILLS KILLS OUTER_TURRET KILLS NaN NaN NaN NaN OUTER_TURRET NaN NaN NaN KILLS NaN NaN KILLS NaN KILLS NaN NaN NaN NaN NaN NaN NaN INHIBITOR NaN INHIBITOR NaN INHIBITOR NaN ... 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 0 36
16 01321351be01672f OUTER_TURRET NaN NaN OUTER_TURRET NaN DRAGON NaN NaN NaN OUTER_TURRET NaN KILLS KILLS NaN NaN NaN NaN NaN NaN INHIBITOR NaN KILLS NaN NaN KILLS NaN INHIBITOR NaN KILLS NaN INHIBITOR NaN NaN 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 NaN NaN NaN NaN NaN NaN NaN 0 34
17 01360c9cdc07e173 NaN KILLS KILLS NaN NaN KILLS NaN DRAGON NaN NaN NaN OUTER_TURRET NaN KILLS NaN BARON_NASHOR NaN DRAGON OUTER_TURRET OUTER_TURRET NaN INNER_TURRET KILLS NaN BASE_TURRET INNER_TURRET NaN INHIBITOR KILLS NaN DRAGON NaN NEXUS_TURRET NaN INNER_TURRET NaN BASE_TURRET DRAGON 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 1 44
18 01368b5ce8fcef87 NaN KILLS NaN KILLS NaN NaN NaN NaN NaN KILLS DRAGON KILLS NaN NaN NaN NaN NaN NaN NaN NaN NaN INHIBITOR NaN INHIBITOR NaN NaN NaN INHIBITOR NaN KILLS NaN NaN NaN KILLS INHIBITOR 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 NaN NaN NaN 0 34
19 01417eebd0d9ccb3 NaN NaN KILLS NaN KILLS NaN KILLS NaN NaN KILLS NaN NaN NaN NaN NaN NaN KILLS NaN NaN NaN NaN INHIBITOR NaN NaN 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 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 0 31

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]:

  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 ... 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
0 0001f4374a03c133 +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 -nan 1 34
1 0016710a48fdd46d -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 -nan 1 40
2 0016c9df37278448 -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 -nan 1 40
3 0021b45647424cd5 -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 -nan 1 42
4 00405293fb859241 +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 -nan 1 39

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]:

  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 ... 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 RedResult MatchLength
0 0001f4374a03c133 +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 -nan 1 34
1 0016710a48fdd46d -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 -nan 1 40
2 0016c9df37278448 -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 -nan 1 40
3 0021b45647424cd5 -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 -nan 1 42
4 00405293fb859241 +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 -nan 1 39
5 00416ed438e29f14 -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 -nan 1 41
6 007802c051352561 -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 -nan 1 44
7 0091705b03924485 -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 -nan 0 27
8 00986b51908a63c3 -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 -nan 0 69
9 00b13dbf1bd7aff0 -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 -nan 0 36

In [28]:

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

Out[28]:

  id
RedEvent1  
+DRAGON 158
+KILLS 1931
+OUTER_TURRET 263
+RIFT_HERALD 7
-DRAGON 108
-KILLS 2136
-OUTER_TURRET 302
-RIFT_HERALD 10

In [29]:

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

Out[29]:

  MatchLength
RedEvent1  
+DRAGON 38.278481
+KILLS 36.599689
+OUTER_TURRET 37.680608
+RIFT_HERALD 39.428571
-DRAGON 37.750000
-KILLS 36.265918
-OUTER_TURRET 38.049669
-RIFT_HERALD 41.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]:

  RedEvent1 RedEvent2 RedResult_x id_x MatchLength_x RedResult_y id_y MatchLength_y Total ProbWIN ProbLOSS ProbE1ANDE2 ProbWINgivenE1ANDE2 ProbLOSSgivenE1ANDE2 RedEvent1Gain RedEvent2Gain Event1AND2Outcome
16 +OUTER_TURRET +KILLS 1 11 38.181818 0.0 2.0 34.50 13.0 0.002248 0.000409 0.002656 0.846154 0.153846 1 1 Both Positive
9 +KILLS +RIFT_HERALD 1 4 31.750000 0.0 1.0 33.00 5.0 0.000817 0.000204 0.001022 0.800000 0.200000 1 1 Both Positive
1 +DRAGON +OUTER_TURRET 1 11 37.636364 0.0 4.0 41.75 15.0 0.002248 0.000817 0.003065 0.733333 0.266667 1 1 Both Positive
21 +RIFT_HERALD -DRAGON 1 2 36.500000 0.0 1.0 32.00 3.0 0.000409 0.000204 0.000613 0.666667 0.333333 1 0 One Positive
20 +RIFT_HERALD +KILLS 1 2 36.000000 0.0 1.0 33.00 3.0 0.000409 0.000204 0.000613 0.666667 0.333333 1 1 Both 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]:

  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 ... 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 RedResult MatchLength
0 0001f4374a03c133 +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 -nan 1 34
1 0016710a48fdd46d -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 -nan 1 40
2 0016c9df37278448 -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 -nan 1 40
3 0021b45647424cd5 -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 -nan 1 42
4 00405293fb859241 +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 -nan 1 39

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]:

  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 ... 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 RedResult MatchLength
0 0001f4374a03c133 +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 1 1 1 1 1 1 1 1 ... 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 34
1 0016710a48fdd46d -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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 40
2 0016c9df37278448 -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 1 1 1 1 ... 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 40
3 0021b45647424cd5 -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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 42
4 00405293fb859241 +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 1 ... 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 39

In [37]:

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

Out[37]:

  id
RedEvent60  
+BARON_NASHOR 4
+BASE_TURRET 2
+ELDER_DRAGON 1
+INHIBITOR 12
+INNER_TURRET 2
+KILLS 7
+NEXUS_TURRET 5
-BARON_NASHOR 1
-BASE_TURRET 3
-ELDER_DRAGON 2
-KILLS 19
-NEXUS_TURRET 12
0 2629
1 2216

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]:

  StartState EndState Event1 Event2 Probability
0 61 62 +INHIBITOR +KILLS 0.076923
0 61 62 +INHIBITOR +OUTER_TURRET 0.000000
0 61 62 +INHIBITOR +DRAGON 0.000000
0 61 62 +INHIBITOR +RIFT_HERALD 0.000000
0 61 62 +INHIBITOR +BARON_NASHOR 0.153846
0 61 62 +INHIBITOR +INNER_TURRET 0.076923
0 61 62 +INHIBITOR +BASE_TURRET 0.000000
0 61 62 +INHIBITOR +INHIBITOR 0.076923
0 61 62 +INHIBITOR +NEXUS_TURRET 0.153846
0 61 62 +INHIBITOR +ELDER_DRAGON 0.000000
0 61 62 +INHIBITOR -KILLS 0.076923
0 61 62 +INHIBITOR -OUTER_TURRET 0.000000
0 61 62 +INHIBITOR -DRAGON 0.000000
0 61 62 +INHIBITOR -RIFT_HERALD 0.000000
0 61 62 +INHIBITOR -BARON_NASHOR 0.153846
0 61 62 +INHIBITOR -INNER_TURRET 0.000000
0 61 62 +INHIBITOR -BASE_TURRET 0.000000
0 61 62 +INHIBITOR -INHIBITOR 0.000000
0 61 62 +INHIBITOR -NEXUS_TURRET 0.230769
0 61 62 +INHIBITOR -ELDER_DRAGON 0.000000
0 61 62 +INHIBITOR 1 0.000000
0 61 62 +INHIBITOR 0 0.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]:

  StartState EndState Event1 Event2 Probability Reward
0 1 2 +KILLS +KILLS 0.350596 0
0 1 2 +KILLS +OUTER_TURRET 0.058519 0
0 1 2 +KILLS +DRAGON 0.076126 0
0 1 2 +KILLS +RIFT_HERALD 0.002589 0
0 1 2 +KILLS +BARON_NASHOR 0.000000 0

In [43]:

len(RedMatchMDP)

Out[43]:

25564

In [44]:

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

Out[44]:

  StartState EndState Event1 Event2 Probability Reward
0 15 16 +ELDER_DRAGON +KILLS 0.5 0
0 15 16 +ELDER_DRAGON +OUTER_TURRET 0.0 0
0 15 16 +ELDER_DRAGON +DRAGON 0.0 0
0 15 16 +ELDER_DRAGON +RIFT_HERALD 0.0 0
0 15 16 +ELDER_DRAGON +BARON_NASHOR 0.0 0
0 15 16 +ELDER_DRAGON +INNER_TURRET 0.0 0
0 15 16 +ELDER_DRAGON +BASE_TURRET 0.0 0
0 15 16 +ELDER_DRAGON +INHIBITOR 0.0 0
0 15 16 +ELDER_DRAGON +NEXUS_TURRET 0.0 0
0 15 16 +ELDER_DRAGON +ELDER_DRAGON 0.0 0
0 15 16 +ELDER_DRAGON -KILLS 0.5 0
0 15 16 +ELDER_DRAGON -OUTER_TURRET 0.0 0
0 15 16 +ELDER_DRAGON -DRAGON 0.0 0
0 15 16 +ELDER_DRAGON -RIFT_HERALD 0.0 0
0 15 16 +ELDER_DRAGON -BARON_NASHOR 0.0 0
0 15 16 +ELDER_DRAGON -INNER_TURRET 0.0 0
0 15 16 +ELDER_DRAGON -BASE_TURRET 0.0 0
0 15 16 +ELDER_DRAGON -INHIBITOR 0.0 0
0 15 16 +ELDER_DRAGON -NEXUS_TURRET 0.0 0
0 15 16 +ELDER_DRAGON -ELDER_DRAGON 0.0 0
0 15 16 +ELDER_DRAGON 1 0.0 0
0 15 16 +ELDER_DRAGON 0 0.0 0

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]:

  StartState EndState Event1 Event2 Probability Reward V
1 1 2 +KILLS +OUTER_TURRET 0.058519 0 -0.000142
286 2 3 -KILLS +KILLS 0.405299 0 0.055403
406 3 4 +KILLS -KILLS 0.380066 0 0.027542
748 4 5 -KILLS +KILLS 0.345982 0 0.062849
861 5 6 +KILLS +RIFT_HERALD 0.012048 0 0.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]:

  StartState EndState Event1 Event2 Probability Reward V
11 1 2 +KILLS -OUTER_TURRET 0.034697 0 0.059365
309 2 3 -OUTER_TURRET +OUTER_TURRET 0.300847 0 0.056334
430 3 4 +OUTER_TURRET -DRAGON 0.074447 0 0.055783
793 4 5 -DRAGON +OUTER_TURRET 0.152104 0 0.055781
891 5 6 +OUTER_TURRET -OUTER_TURRET 0.261941 0 0.055770
1288 6 7 -OUTER_TURRET -DRAGON 0.110092 0 0.055765
1606 7 8 -DRAGON +KILLS 0.265672 0 0.056417
1720 8 9 +KILLS +BARON_NASHOR 0.005859 0 0.055783
2144 9 10 +BARON_NASHOR -KILLS 0.312500 0 0.055783
2398 10 11 +OUTER_TURRET +KILLS 0.185980 0 0.059142
2706 11 12 +KILLS +KILLS 0.118577 0 0.058965
3244 12 13 -OUTER_TURRET -KILLS 0.227920 0 0.055783
3596 13 14 -KILLS -KILLS 0.121409 0 0.054589
3975 14 15 -KILLS -INNER_TURRET 0.138376 0 0.074098
4168 15 16 +KILLS -KILLS 0.297481 0 0.056211
4877 16 17 -DRAGON -INNER_TURRET 0.158576 0 0.055783
5356 17 18 -INNER_TURRET -KILLS 0.236324 0 0.055783
5698 18 19 -KILLS +KILLS 0.266546 0 0.057560
5940 19 20 +KILLS +KILLS 0.128205 0 0.058518
6414 20 21 +KILLS -DRAGON 0.037569 0 0.055783
7128 21 22 -DRAGON +KILLS 0.235741 0 0.056081
7346 22 23 +KILLS 1 0.002304 0 0.056324
7795 23 24 +KILLS +INHIBITOR 0.049818 0 0.033658
8384 24 25 +INHIBITOR +DRAGON 0.037037 0 0.035062
8717 25 26 +DRAGON +INNER_TURRET 0.254777 0 0.035054
9196 26 27 +INNER_TURRET +KILLS 0.230303 0 0.035203
9534 27 28 +KILLS +NEXUS_TURRET 0.071124 0 0.035062
10162 28 29 +NEXUS_TURRET 1 0.280612 0 0.035062
10721 29 30 -BASE_TURRET +INHIBITOR 0.576923 0 0.024197
10965 30 31 +INHIBITOR +ELDER_DRAGON 0.016129 0 0.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]:

  StartState EndState Event1 Event2 Probability Reward V
12 1 2 +KILLS -DRAGON 0.031590 0 0.094938
341 2 3 -DRAGON -OUTER_TURRET 0.142857 0 0.094465
529 3 4 -OUTER_TURRET +OUTER_TURRET 0.368852 0 0.084538
648 4 5 +OUTER_TURRET -KILLS 0.146843 0 0.094469
978 5 6 -KILLS -KILLS 0.160743 0 0.087776

In [55]:

Mdl3[4]

Out[55]:

  StartState EndState Event1 Event2 Probability Reward V
0 1 2 +KILLS -DRAGON 0.031590 0 0.094938
0 2 3 -DRAGON -OUTER_TURRET 0.142857 0 0.094465
0 3 4 -OUTER_TURRET +OUTER_TURRET 0.368852 0 0.084538
0 4 5 +OUTER_TURRET -KILLS 0.146843 0 0.094469
0 5 6 -KILLS -KILLS 0.160743 0 0.087776
0 6 7 -KILLS -OUTER_TURRET 0.271478 0 0.096109
0 7 8 -OUTER_TURRET +OUTER_TURRET 0.186101 0 0.093135
0 8 9 +OUTER_TURRET +KILLS 0.194960 0 0.074443
0 9 10 +KILLS +OUTER_TURRET 0.216617 0 0.072786
0 10 11 +OUTER_TURRET +DRAGON 0.124464 0 0.082719
0 11 12 +DRAGON -KILLS 0.250000 0 0.073039
0 12 13 -KILLS +KILLS 0.266995 0 0.095967
0 13 14 +KILLS +DRAGON 0.072690 0 0.091666
0 14 15 +DRAGON +KILLS 0.314985 0 0.094469
0 15 16 +KILLS -KILLS 0.297481 0 0.073666
0 16 17 -KILLS -INNER_TURRET 0.124319 0 0.093982
0 17 18 -INNER_TURRET +KILLS 0.181619 0 0.093859
0 18 19 +KILLS +BASE_TURRET 0.029692 0 0.094469
0 19 20 +BASE_TURRET +DRAGON 0.055556 0 0.094469
0 20 21 +DRAGON -KILLS 0.195745 0 0.095296
0 21 22 -KILLS -KILLS 0.108607 0 0.107893
0 22 23 -KILLS 1 0.003209 0 0.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.

猜你喜欢

转载自blog.csdn.net/XYYxyy55/article/details/81166332
今日推荐