Introduction
The traditional game of League of Legends takes place on a map known as Summoner’s Rift, where there are three major lanes as well as a jungle comprised of monsters. There are two teams, blue team and red team, who’s bases exist on opposite sides of the non-symmetrical map. Minions run through each lane from both sides and the eventual goal of the game is to take down most of the towers of the enemy team and destroy the nest of their base, the “nexus.” However, there is a much simpler gamemode in league that many casual players really enjoy, known as ARAM; it instead takes place on a map that consists of only one lane, basically the mid lane of summoner’s rift. Both teams consisting of five random players are given random champions, and this is why the gamemode is called ARAM (all random, and all mid). Within this project, I will try and predict which side has a greater chance of winning a game of ARAM based on team composition. I’ll be training a model that will take into account not only thousands of past ARAM games, but also the current ARAM win rates of champions in patch 11.
To get access to the Riot Games API and replicate or add on to this tutorial, you have to first navigate to https://developer.riotgames.com/ and sign in with a Riot Games account. You will then have to fill out an application and should automatically get access to a development API key shortly afterwards.
Initially, I signed up for the Riot Games API as a college developer, and got my own unique API key to utilize. Thus, I was able to use the adapted cassiopeia python library of riot's league API to extract data.
import pandas as pd
import numpy as np
import random
import cassiopeia
import warnings
from sortedcontainers import SortedList
from cassiopeia.core import Match
from cassiopeia import Patch
warnings.filterwarnings('ignore')
# We have a list of champion names with respect to their key values, which signify a weight corresponding to their current winrate in patch 11.24
champions = pd.read_json("champInfo.json")
champions["key"] = champions["data"].apply(lambda x: x["key"])
champions["name"] = champions["data"].apply(lambda x: x["name"])
champions[["key","name"]]
key | name | |
---|---|---|
Aatrox | 266 | Aatrox |
Ahri | 103 | Ahri |
Akali | 84 | Akali |
Alistar | 12 | Alistar |
Amumu | 32 | Amumu |
... | ... | ... |
Zed | 238 | Zed |
Ziggs | 115 | Ziggs |
Zilean | 26 | Zilean |
Zoe | 142 | Zoe |
Zyra | 143 | Zyra |
145 rows × 2 columns
We then utilize a CountVectorizer to convert a list of champion names in champion select into a sparse vector where each element indicates whether the corresponding champion is present in the game. A positive value signifies that the champion is on blue team and a negative value that the champion is on red team.
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer(token_pattern = r"(?u)\b\w+\b") # This is to prevent removing one character key values
cv.fit(champions.key.values)
CountVectorizer(token_pattern='(?u)\\b\\w+\\b')
# Export the CV as a pickle file for later use
import pickle
pickle.dump(cv, open("cv.p", "wb"))
We utilize get requests to retrieve the match history of a certain starter summoner by name, and for each ARAM match within this match history, we run another get request using the riot API to gather more specifics about this match through its match ID. We then repeat this process with extended summoners/players (as we have access to 9 other players within each match), until we eventually hit the number of matches we desire in our dataset for training (5000 in our case). Please take a look at https://github.com/meraki-analytics/cassiopeia/tree/master/examples and https://cassiopeia.readthedocs.io/en/latest/ to understand more about how the Riot Games API functions; it's quite neat!
cassiopeia.set_riot_api_key('RGAPI-7bfd94d1-757c-46da-a1ee-5f94298c0d8f') # This unique API key expires on 12/21/2021. Please generate another API key on the riot games dev website if you wish to re-run the code for your own analysis.
firstSummoner = cassiopeia.get_summoner(name='mattgu16', region='NA')
currPatch = Patch.from_str('11.24', region='NA')
# create a sorted list for player ID's (we start with the initial summoner name, my friend Matt Gu who really enjoys playing ARAM)
unprocessedSummoners = SortedList(['mattgu16']) # Essentially, we have a huge tree of connected players starting with one player. From that one players game, we have access to 9 other players, and from each of those summoners' games, we have access to games of 9 other summoners. Thus, we essentially have thousands and thousands of branching matches.
processedSummoners = SortedList()
unprocessedMatches = SortedList()
processedMatches = SortedList()
# number of random matches pulled for our training data
num_matches = 5000
# Finally crawl through collect match id's
while unprocessedSummoners and len(processedMatches) < num_matches:
# Get a random summoner from our list of unpulled summoners and pull their match history
newSummonerName = random.choice(unprocessedSummoners)
newSummoner = cassiopeia.get_summoner(name=newSummonerName, region='NA')
matches = newSummoner.match_history
for match in matches:
if (not isinstance(match.id, int)):
unprocessedMatches.update([match.id])
# After we pull the match id's from the summoner we
unprocessedSummoners.remove(newSummonerName)
processedSummoners.add(newSummonerName)
while unprocessedMatches and len(processedMatches) < num_matches:
# Get a random match from our list of matches
newMatchID = random.choice(unprocessedMatches)
newMatch = Match(id=newMatchID, region='NA')
for player in newMatch.participants:
if player.summoner.name not in processedSummoners and player.summoner.name not in unprocessedSummoners:
unprocessedSummoners.add(player.summoner.name)
# The above lines will trigger the match to load its data by iterating over all the participants.
# If you have a database in your datapipeline, the match will automatically be stored in it.
unprocessedMatches.remove(newMatchID)
processedMatches.add(newMatchID)
Making call: https://cdn.merakianalytics.com/riot/lol/resources/patches.json Making call: https://na1.api.riotgames.com/lol/summoner/v4/summoners/by-name/mattgu16 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/RQ42LuhfbXQt8Ar-HgZG5NhjB-U3J5-9cZEcZYQVJ9sGMjgJ1Tg-0oGD6dJFgebtZzJNbT1eLkRxYw/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4138758589 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144717968 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144551764 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145372830 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139062747 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4138967297 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142827408 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4138988652 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4138972721 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4140747363 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144690108 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4138734952 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144497001 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144470775 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145376165 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144503207 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4140718487 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142796080 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145349793 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4138708812 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/KaMsMXv4DFoo3kgp-ZbhgE09Xgys7YgFVf3XrLmUb3LAAX8tOUbZQ1ZW8US8YqFjMecKsr2akKmtBg/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145708738 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141247467 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143302502 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143356566 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144812186 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143042926 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144479495 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142294386 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142142571 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142259892 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144891585 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143177408 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139882758 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144817001 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145757674 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141179556 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142313126 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145813581 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141171707 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146652617 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/LbU_R5UCPbIIZ8dD_p8Y4GYvsiGQS8Qg2vb59rQV86U-OiWCDN_bVglSvBsEpG2VBOfg0910DDhNIg/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144112665 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139389772 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139603453 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139511475 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4137325882 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145063993 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4138990823 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4138585328 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144182361 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144110408 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4137287358 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144167851 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139553981 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139629890 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139664221 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139079694 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145051335 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144524198 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/R_FcDyyenZk5m_MeQpA77kBAOoU9TunvNbsPnDcaeccaYge_tJLYrdslikwUkC12XTHfSaaFwmxIdw/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145775873 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145682035 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145722976 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144425202 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145547528 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145697024 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144437196 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144593183 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145606273 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144685982 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144517341 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144580878 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144498578 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144451173 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144282796 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144616956 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144493731 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144619453 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146548896 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/62qXiGBJhhLnUHWjdpVudaWE-7biiLfF0vWhJV6R9M_XiY3k-S03yHnfIPz1quMekhTIF1mrAeV9rA/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143653847 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143041953 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143020728 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142405548 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143690480 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144541483 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143076170 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144779416 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145611203 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142327251 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143744137 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142482372 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145653209 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143717069 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142997325 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144471137 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145659227 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142957428 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144475994 INFO: Unexpected service rate limit, backing off for 73 seconds (from headers). Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144475994 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/DuFHWKUC529n4nN-uN7Ar8QDPpsVbLV0LAknCqyDPedl1YKqz59ZP4n77djn4204ABgJvLNyG_U6Vw/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139561642 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142975399 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4134726652 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141950920 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4140899572 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143053123 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139486311 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4134731918 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144880263 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4134658229 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144780388 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139527128 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139479149 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144895352 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144826644 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144940469 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144805610 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4134780562 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143029813 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/7QR5kkvZR4zEb5AjcgsP1kK9PtR3v9C7H4QBhVqBroQU1KTyh-OUcqNseABfTGhjIsQkLKD2z0EkAw/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144517838 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142953927 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144393893 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143087775 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145237348 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144795550 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146754357 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142987546 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143009963 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143014417 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144450402 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145244093 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144486364 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144716480 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142931197 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146688868 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146694942 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142004581 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144551123 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/rHBrGkZ6o7QKfXT7AUApnSF_cq-F-HiaMpbpeIC9XMD1pyd7XOUtjpOGywEJknA29p4ZmSX9upgmbA/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142218937 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142703616 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142999849 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143897612 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144956622 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145022086 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142323744 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144664757 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144628576 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144631301 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144604093 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144370722 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141569917 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146401949 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142158188 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144969176 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144961151 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141505534 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143045532 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144887676 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/XGUAc8WuDS5pQHiSNh4YAAhUS995Wb7nOzJ_ZGkCBsT9209Z86eHzRCd-T6KpCLCEEkc5JkpWcVWnw/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139707561 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143357423 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4138863365 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141505400 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144432387 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4137674918 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144486524 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146121846 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144446589 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4130533816 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143633658 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4128080373 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142405223 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4137617402 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4137571683 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143529231 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4137504126 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4129285322 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4129292159 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/XL_CptWF4hEb7h-pJE2_BmqXZontduITvaUssa-NGXSw0k9H2QP9vJ1sQph6bPcwi-jP7bxktVGEaw/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144566291 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144288942 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141340882 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141285712 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144529272 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144374820 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143089332 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4140792052 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141296793 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144274351 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142278405 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143037713 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144541954 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142300532 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144587011 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142890889 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146519262 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142809005 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145589814 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143019727 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/-dX1tcmY2owsbLsIyC6VSLx6R6AbC0zuOS8nQ8M58LIY26KUk1CCk3Fuco7cMc8ARGm4oENLkz-gFg/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145346995 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146498522 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144454720 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144286624 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144303083 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145397192 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145334041 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144487314 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145617275 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144329318 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146691743 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146656127 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144441573 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145612571 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145301711 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144339585 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144470792 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145198043 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4146658892 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144239692 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/r3D6mwpp2ksmo9AaWevGoOppKKTHFPLyKdUsb4K7ZNo1npTDhDZgaO2jWodHdhWO23mLQspyfWHbaA/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4139865841 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141196032 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145844019 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145875017 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142341148 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144356785 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4141250103 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145759474 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4140871284 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142205973 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142261697 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144420658 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144394523 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4142306161 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4140855223 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4140857415 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144308180 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4140893824 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4145838450 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144342314 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/WmbXYktXfiy0SaBJrh6MSD8w5UEMv-giACinuk-5zLPMyb91bvQWEvfSTVJNwjIrQ-u2oV7tui58uw/ids?beginIndex=0&endIndex=100 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144757256 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4137594142 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4126707398 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4134853859 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4138975009 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144771537 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4128757629 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144631082 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144434627 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4129020929 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4128215744 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4137515288 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4135265582 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4143367639 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144705999 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4144380257 Making call: https://americas.api.riotgames.com/lol/match/v5/matches/NA1_4128999145
# Save into pickle
import pickle
pickle.dump(processedMatches, open("matchID.p", "wb" ))
# A list of all the match ID's we pulled to later analyze
Now that we have a list of all the match ID's we wanted, we start our training process. We iterate through all the matches, figure out what champions were on blue and red side during that match, and pass in the resultant target variable that shows which team won. We create a pandas dataframe with all this information and store it as a pickle file for later use.
# open pulled match id's
match_ids = pickle.load(open("matchID.p", "rb"))
# create new dataframe
df = pd.DataFrame(columns=["MatchID","BlueWin"] + [(champions.index)[champions.key == str(i)][0] for i in cv.get_feature_names()])
for ids in match_ids:
currMatch = Match(id=ids, region="NA") # get match object
participants = currMatch.participants # get champions in the match
result = int(currMatch.blue_team.win) # get result of the match
blue_team = []
red_team = []
for player in participants:
if player.team.side.name == 'blue':
blue_team.append(str(player.champion.id))
else:
red_team.append(str(player.champion.id))
# to get +1 for blue team and -1 for red team, we minus the two vectors
X = (cv.transform([",".join(blue_team)]) - cv.transform([",".join(red_team)])).toarray()
# Join ID, result, and previous match vector (as a single row)
row = np.append(np.array([ids,result]),X)
# Add to the dataframe
df.loc[len(df)] = row
# Reset Index to the matches
pickle.dump(df ,open("df.p", "wb"))
Making call: https://ddragon.leagueoflegends.com/api/versions.json
After training the model on thousands of ARAM games in addition to the provided winrates of champions (the individual weights of each champion will be tuned from the initial winrate as we analyze games, as winrate is definitely not everything and can be very misleading), we will then test the accuracy of this model by running it back on the provided dataset of sample ARAM games. The beauty of league of legends is that each game feels pretty random; although team composition (champions chosen in the pre-game lobby) do make a big difference in the end result, there's so much that could potentially occur that even if one comp is much stronger than the other, most games are close to being coinflips. Thus, I don't expect this algorithm to be very accurate but any accuracy above 55% is honestly still impressive.
Furthermore, after the model was fully trained, I analyzed mirror matchups on both sides with the algorithm and found that blue side typically had a higher expected chance of winning compared to red side. This does make sense as the map is not symmetrical, meaning that vision and controls happen to be slightly more beneficial to blue side. Thus, I modified the algorithm by adding a slight fixed boost to blue side's chance of winning.
As for the algorithms I utilized to train this model, I chose logistic regression, ridge regression, a SVM, and a decision tree. Since the dependent variable is binary (either blue team won or not), I decided that it was best to use logistic regression for the further analysis, but I still checked the accuracy of the other models. If you wish to learn more about how logistic regression works, please take a look at this link: https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html
import pickle
import numpy as np
import pandas as pd
import os
import sys
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import LinearSVC
from sklearn.metrics import accuracy_score
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import train_test_split
from sklearn.linear_model import RidgeClassifierCV
from sklearn.linear_model import LogisticRegressionCV
# I have pulled the initial win rates of champions in patch 11.24 in ARAM, but I will be fine-tuning these through the thousands of games analyzed. I will then convert the
# raw rating of each champion to a number between 0-100 representing the champion's "strength" in ARAM games (not necessarily winrate) through the following logistic function.
def score(x,a=1):
return (1/(1+np.exp(-a*(x))))*100
# Get dataframe
df = pickle.load(open(os.path.join(sys.path[0], "df.p"), "rb"))
df.set_index('MatchID',inplace=True)
# Split into features vs target
X = df.drop("BlueWin",axis=1)
y = df.BlueWin.astype('int')
# Get train and test sets
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=10)
# Set random seed
np.random.seed()
# Logistic Regression CV
lr = LogisticRegressionCV(cv=5, random_state=0).fit(X_train, y_train)
# Ridge Regression CV
rc = RidgeClassifierCV(cv=5).fit(X_train, y_train)
# Parameters for Max Depth for SVM
parameters_svm = {'C':np.logspace(-3,3,5)}
svm = GridSearchCV(LinearSVC(), parameters_svm, n_jobs=-1).fit(X_train,y_train).best_estimator_
# Parameters for Max Depth for Decision Tree
parameters_dt = {'max_depth':range(20,100,5)}
dt = GridSearchCV(DecisionTreeClassifier(), parameters_dt, n_jobs=-1).fit(X_train,y_train).best_estimator_
classifiers = [lr,rc,svm,dt]
algos = ["Logistic Regression","Ridge Regression","SVM","Decision Tree"]
for i in range(4):
print("{}: train accuracy was: {:.4f} and test accuracy was {:.4f}".format(algos[i],accuracy_score(y_train,classifiers[i].predict(X_train)),accuracy_score(y_test,classifiers[i].predict(X_test))))
# pickle fitted logistic regression object
pickle.dump(lr, open("/home/cs/cmsc320/arman-b.github.io/logistic_regression.p", "wb"))
# Create champion ranking list from the coefficients
rankings = pd.DataFrame({'Champion':X_train.columns,'Score':score(lr.coef_.flatten(),7)}).sort_values('Score',ascending=False).reset_index(drop=True)
rankings.Score = rankings.Score.round(5)
rankings.reset_index(inplace=True)
rankings.columns = ["Rank","Champion","Score"]
rankings.Rank += 1 # Rank is 1-indexed
pickle.dump(rankings ,open( "/home/cs/cmsc320/arman-b.github.io/scores.p", "wb" ))
rankings
Logistic Regression: train accuracy was: 0.8083 and test accuracy was 0.3000 Ridge Regression: train accuracy was: 0.9000 and test accuracy was 0.3000 SVM: train accuracy was: 0.8083 and test accuracy was 0.4000 Decision Tree: train accuracy was: 1.0000 and test accuracy was 0.6000
Rank | Champion | Score | |
---|---|---|---|
0 | 1 | Morgana | 63.54035 |
1 | 2 | Sona | 59.08748 |
2 | 3 | Quinn | 56.64222 |
3 | 4 | Singed | 56.63528 |
4 | 5 | Kennen | 56.50624 |
... | ... | ... | ... |
140 | 141 | Zoe | 42.40336 |
141 | 142 | Corki | 42.12776 |
142 | 143 | Darius | 41.99661 |
143 | 144 | Jinx | 38.37918 |
144 | 145 | Zed | 37.99553 |
145 rows × 3 columns
As we can see above, due to the really random nature of League games, our accuracy was pretty low for all models. Furthermore, we only predicted based on champion select, so there are so many features we didn't account for, such as player rank, runes, and builds. As for champion rankings, we see that the adjusted score seems to be generally pretty stable across the board, although there are definitely some champions with greater chances of winning than others.
import matplotlib.pyplot as plt
import seaborn as sns
champions = pd.read_json("/home/cs/cmsc320/arman-b.github.io/champInfo.json")
countvectorizer = pickle.load(open(os.path.join(sys.path[0], "cv.p"), "rb" ))
logisticregressor = pickle.load(open(os.path.join(sys.path[0], "logistic_regression.p"), "rb"))
scores = pickle.load(open(os.path.join(sys.path[0], "scores.p"), "rb"))
blueComp = ["Draven", "Ahri", "Ryze", "Teemo", "Blitzcrank"]
redComp = ["Trundle", "Irelia", "Katarina", "Vayne", "Ashe"] #Modify these two arrays to see the chance of winning your own game based on team composition!
result = logisticregressor.predict_proba((countvectorizer.transform([",".join(blueComp)]) - countvectorizer.transform([",".join(redComp)])).toarray())[0]
ax = sns.barplot(x=['Blue', 'Red'], y=[result[0], result[1]], alpha=0.5, palette=["Blue", "Red"])
plt.xlabel('Side', fontsize=17, fontweight='bold')
plt.ylabel('Win Chance', fontsize=17, fontweight='bold')
plt.title('Chance of winning game based on inputted champions on both sides')
for p in ax.patches:
height = p.get_height()
ax.text(p.get_x()+p.get_width()/2.,
height +.007,
'{:1.5f}%'.format(100*height),
ha="center")
plt.show()
totalBlue = 0
totalRed = 0
for i in range(5000):
teamComp = champions.sample(5)
result = logisticregressor.predict_proba((countvectorizer.transform([",".join(teamComp)]) - countvectorizer.transform([",".join(teamComp)])).toarray())[0]
totalBlue += result[0] + 0.05
totalRed += result[1] - 0.05
# Plot the results and label the graph
ax = sns.barplot(x=['Blue', 'Red'], y=[totalBlue/5000, totalRed/5000], alpha=0.5, palette=["Blue", "Red"])
plt.xlabel('Side', fontsize=17, fontweight='bold')
plt.ylabel('Win Chance', fontsize=17, fontweight='bold')
plt.title('Win chance of Blue and Red team predicted by the model after 5000 mirrored matchups')
for p in ax.patches:
height = p.get_height()
ax.text(p.get_x()+p.get_width()/2.,
height +.007,
'{:1.5f}%'.format(100*height),
ha="center")
plt.show()
As we can see, when the champions on both sides are mirrored, the blue team seems to have a slightly higher chance of winning, which leads us to believe that there is an inherent bias towards blue side.
As for future improvements to this project, I could potentially make a win predictor for Summoner’s Rift one day. It would be a lot more complicated and less accurate as there are not only many more factors (such as objectives, dragons, and ganking) but also a lot of skewed lane matchups, meaning we have to consider team synergy as well as counter champions. It would also be cool to analyze everyone’s ranks on both teams to make the prediction even more accurate. I’m currently a diamond adc main (draven one trick) and really enjoy playing ranked summoner’s rift, so I might look into doing so in the future.
Thank you so much for taking the time to read this and I hope you found it interesting!