Due date: Saturday, April 21st, 11:59pm (was April 19th)
In this project, you will first setup tools for doing game analytics on Riot's League of Legends League. Then, you will use the pipeline to extract data from Riot's game data set, analyze game data through charts and tables, and writeup the results in a report for dissemination.
Learn a bit about League of Legends. You are more than welcome to play the game. But, given some of the game's complexities, if you prefer, you can read and/or watch videos instead. Some links that may be useful include:
The Riot API is an Application Programmer Interface (API) for retrieving game data for the popular computer game League of Legends.
Note: There may be up to a 48-hour delay between the time from first making a League of Legends account and getting a Riot API key (the next step). So do this early!
Note 1: You will need to keep track of your personal API key after logging in (e.g., 12345678-abcd-9012-efgh-345678901234
). These are used in the scripts that extract data from the RIOT API. These should not be shared.
Note 2: The API key expires every 24 hours. You can re-generate it by signing in again (step #2) and going to the "Dashboard".
RiotWatcher is a thin wrapper on top of the Riot Games API for League of Legends. See:
To install, open a terminal window / command prompt. Type:
pip install riotwatcher
Put the below script into a file called "hello.py".
#!/usr/bin/python3
#
# Basic "hello, world!" for Riot API.
# v2.0
#
# Bring in imports needed for data processing.
from riotwatcher import RiotWatcher
import json
# Replace below with personal Riot API key.
KEY = 'PERSONAL-RIOT-API-KEY_HERE'
# Using North America region.
REGION = 'na1'
# Getting info on this player.
PLAYER = 'Grig1'
# Get master RiotWatcher object, using Riot developer key.
r_w = RiotWatcher(KEY)
# Get info on summoner.
player = r_w.summoner.by_name(REGION, PLAYER)
print(json.dumps(player, indent=2))
# Get list of Champions.
static_champ_list = r_w.static_data.champions(REGION)
# Print out "Teemo" and "Leona"
print("-----------------");
champ = 'Teemo'
print("Teemo info:");
print(json.dumps(static_champ_list['data'][champ], indent=2))
champ = 'Leona'
print("Leona info:");
print(json.dumps(static_champ_list['data'][champ], indent=2))
Note: You need to replace PERSONAL-RIOT-API-KEY_HERE'
with your own API key.
From a terminal/command prompt, run hello.py
with:
python hello.py
(Linux/Mac users should use python3
, as usual).
If all is working, your output should look similar to:
{ "name": "Grig1", "id": 21542029, "accountId": 34970420, "revisionDate": 1523495535000, "profileIconId": 983, "summonerLevel": 87 } ----------------- Teemo info: { "title": "the Swift Scout", "id": 17, "name": "Teemo", "key": "Teemo" } Leona info: { "title": "the Radiant Dawn", "id": 89, "name": "Leona", "key": "Leona" }
Exploration: For a competitive/professional League of Legends player (summoner) of your choice, analyze: a) the number of League matches played over time; and b) the duration of the matches.
Advanced: c) For the same player, pick an in-depth exploration of either a), or b). This could be many additional games over many years (i.e., over the 100 required), correlation of duration with other in-game data, analysis of summoners Champions played, or a related in-depth analysis of your choosing.
There are many ways to find professional League players, but a pretty easy way is through a Google search. Or, any friends that have played competitive/ranked League matches can be used. You can even use yourself if you've played a significant number of competitive/ranked League matches.
Note 1: You may not use the player Grig1
.
Note 2: You must analyze at least 100 matches, so make sure your player has that many.
#!/usr/bin/python3
#
# get-times.py
# version 1.0
#
########################################
## IMPORTS
from riotwatcher import RiotWatcher
import json
import time
########################################
## SETTINGS
KEY = 'PERSONAL-RIOT-API-KEY_HERE'
REGION = 'na1' # North America as region
PLAYER = 'Grig1' # Player (Summoner) of interest
MATCH_LIMIT = 0 # Number of matches to analyze (0 for no limit)
print("REGION:", REGION)
print("PLAYER:", PLAYER)
print("MATCH_LIMIT:", MATCH_LIMIT)
print("----------------------")
########################################
# SETUP
# Get master RiotWatcher object, using my Riot developer key.
r_w = RiotWatcher(KEY)
# Get player with summoner name.
player = r_w.summoner.by_name(REGION, PLAYER)
id = player['accountId']
########################################
# GET FULL MATCH DATA
# Get player's match list from match data.
match_data = r_w.match.matchlist_by_account(REGION, id)
match_list = match_data['matches']
# Read in full match data for MATCH_LIMIT matches.
full_match_list = []
i = 0
for match in match_list:
print("Reading full match %d..." % i)
m = r_w.match.by_id(REGION, match['gameId'])
full_match_list.append(m)
i += 1
if (i == MATCH_LIMIT):
break
time.sleep(1) # Pause to avoid overwhelming Riot API quota.
print("----------------------")
########################################
# TIMES
# Go through each match in list.
i = 0
for match in full_match_list:
print("Match", i, ":", match['gameDuration'], "seconds")
i += 1
# Done.
exit()
#!/usr/bin/python3
#
# get-durations.py
# version 1.0
#
########################################
## IMPORTS
from riotwatcher import RiotWatcher
import json
import time
########################################
## SETTINGS
KEY = 'PERSONAL-RIOT-API-KEY_HERE'
REGION = 'na1' # North America as region
PLAYER = 'Grig1' # Player (Summoner) of interest
INDEX = 0 # Start of player matches (will get 100 total)
print("REGION:", REGION)
print("PLAYER:", PLAYER)
print("INDEX:", INDEX)
print("----------------------")
########################################
# SETUP
# Get master RiotWatcher object, using my Riot developer key.
r_w = RiotWatcher(KEY)
# Get player with summoner name.
player = r_w.summoner.by_name(REGION, PLAYER)
id = player['accountId']
# Get player's match list from match data.
match_data = r_w.match.matchlist_by_account(REGION, id, begin_index=INDEX)
match_list = match_data['matches']
# Get time of most recent match.
# Note: match times are in milliseconds since 1970.
match = match_list[0]
match_time_newest = match['timestamp']
# Print time of each match since first match.
i = 0
for match in match_list:
print("Match:", INDEX + i)
# Compute how long ago since newest.
match_time = match['timestamp']
milliseconds = match_time_newest - match_time
minutes = milliseconds / (1000 * 60)
hours = minutes / 60
days = hours / 24
years = days / 365
# Print out times.
# Note: %d is for integer, %.2f is for real with 2 digits after decimal
print(" minutes: %d" % minutes)
print(" hours: %.2f" % hours)
print(" days: %.2f" % days)
i += 1
# Done.
exit()
#!/usr/bin/python3
#
# get-champs.py
# version 1.1
#
########################################
## IMPORTS
from riotwatcher import RiotWatcher
import json
import time
########################################
## SETTINGS
KEY = 'PERSONAL-RIOT-API-KEY_HERE'
REGION = 'na1' # North America as region
PLAYER = 'Grig1' # Player (Summoner) of interest
MATCH_LIMIT = 2 # Number of matches to analyze (0 for no limit)
INDEX = 0 # Start of matches (will get 100)
print("REGION:", REGION)
print("PLAYER:", PLAYER)
print("INDEX:", INDEX)
print("MATCH_LIMIT:", MATCH_LIMIT)
print("----------------------")
########################################
# SETUP
# Get master RiotWatcher object, using my Riot developer key.
r_w = RiotWatcher(KEY)
# Get player with summoner name.
player = r_w.summoner.by_name(REGION, PLAYER)
id = player['accountId']
########################################
# GET FULL MATCH DATA
# Get player's match list from match data.
match_data = r_w.match.matchlist_by_account(REGION, id, begin_index=INDEX)
match_list = match_data['matches']
# Read in full match data for MATCH_LIMIT matches.
full_match_list = []
i = 0
for match in match_list:
print("Reading full match %d..." % i)
m = r_w.match.by_id(REGION, match['gameId'])
full_match_list.append(m)
i += 1
if (i == MATCH_LIMIT):
break
time.sleep(1) # Pause to avoid overwhelming Riot API quota.
print("----------------------")
########################################
# CHAMPS
# Read in static champs list from file.
# Note: instead, this could be done infrequently using
# r_w.static_data.champions(REGION)
static_champ_list = json.load(open('static-champs.json'))
# Go through each match in list.
i = 0
for match in full_match_list:
print("Match:", i)
# Go through each player in match.
for player in match['participants']:
id = player['championId']
# Find and print matching champion name from list.
for champ in static_champ_list['data']:
if static_champ_list['data'][champ]['id'] == id:
print(" ", id, champ)
i += 1
# Done.
exit()
Note: The above script uses the file static-champs.json
that you can find in the Hints section.
In League of Legends, Gold is available for making Champions more powerful. Champions earn gold regularly over time and also by killing enemy minions, Champions, and neutral monsters. Players use gold to buy items (e.g., weapons and armor) for his/her Champion. However, gold amounts and distributions can vary widely from match to match. What is the role of gold in League of Legends matches?
Exploration: For your chosen summoner, analyze: a) the total amount of gold obtained by all players in each match, and b) the difference between the total gold earned by the winning team minus the losing team.
Hint: The first 5 participants are on Team 1 and the second 5 participants are on Team 2.
Advanced: c) Pick an additional exploration regarding gold. This could be gold for different roles (i.e., Top Lane versus Jungle), gold versus player summoner rank, gold versus duration, gold versus some other in game statistics (examine single-match.json
below for details) or some similar aspect of your choosing.
Note: You must analyze at least 100 matches, so make sure your player has that many.
#!/usr/bin/python3
#
# get-gold.py
# version 1.1
#
########################################
## IMPORTS
from riotwatcher import RiotWatcher
import json
import time
########################################
## SETTINGS
KEY = 'PERSONAL-RIOT-API-KEY_HERE'
REGION = 'na1' # North America as region
PLAYER = 'Grig1' # Player (Summoner) of interest
MATCH_LIMIT = 2 # Number of matches to analyze (0 for no limit)
INDEX = 0 # Start of matches (will get 100)
print("REGION:", REGION)
print("PLAYER:", PLAYER)
print("INDEX:", INDEX)
print("MATCH_LIMIT:", MATCH_LIMIT)
print("----------------------")
########################################
# SETUP
# Get master RiotWatcher object, using my Riot developer key.
r_w = RiotWatcher(KEY)
# Get player with summoner name.
player = r_w.summoner.by_name(REGION, PLAYER)
id = player['accountId']
########################################
# GET FULL MATCH DATA
# Get player's match list from match data.
match_data = r_w.match.matchlist_by_account(REGION, id, begin_index=INDEX)
match_list = match_data['matches']
# Read in full match data for MATCH_LIMIT matches.
full_match_list = []
i = 0
for match in match_list:
print("Reading full match %d..." % i)
m = r_w.match.by_id(REGION, match['gameId'])
full_match_list.append(m)
i += 1
if (i == MATCH_LIMIT):
break
time.sleep(1) # Pause to avoid overwhelming Riot API quota.
print("----------------------")
########################################
# GOLD
# Go through each match in list.
i = 0
for match in full_match_list:
print("Match:", i)
# Go through each player in match.
p = 0
for player in match['participants']:
gold = player['stats']['goldEarned']
print(" player", p, ":", gold)
p += 1
# Print winner/loser
print("Team 0:", match['teams'][0]['win'])
print("Team 1:", match['teams'][1]['win'])
i += 1
# Done.
exit()
Participate in the Puck Hunt user study.
Visit:
Grab a timeslot and participate in the study.
Data is batched about once per day and posted on the project page. Here are the cumulative data sets:
Download the latest data set.
Prepare to analyze data. The fields are:
Lag
- amount of delay added to all mouse actions (in milliseconds)MinTime
- minimum time between target moves (in milliseconds)Angle
- range for target angle change (in degrees)Rating
- user rating (1 - low to 5 - high)For analysis:
Analyze the Rating
versus Lag
for all the data (this should be the survey-all.txt
file contained in the data set).
Analyze one additional aspect of the data set. Possibilities include:
MinTime
.Angle
.Note: the data is tab separated instead of comma separated. This can be handled by Excel in a couple of ways: a) Copy and paste the data from text editor (e.g., Wordpad) into Excel, or b) in Excel, use "Data" -> "Get Data" -> "From File" -> "Text/CSV" and use a "tab" as the delimiter.
An easy way to write ouptut to a file is to "redirect it" from the command line using the '>' (greater than) operator. For example, running:
python parse.py > myfile.txt
will record all the output that normally would go to the terminal screen into the file myfile.txt
. Note that any previous content in myfile.txt
will be over-written when this is run.
For match data, the Riot APIs return at most 100 matches. If you want to analyze more than 100 matches, you will need to make multiple calls to matchlist_by_account()
, changing the begin_index
each time, until the number of matches returned is less than 100.
For all Riot API queries, there is a limit to how fast requests can be made. Doing so too rapidly will have the query rejected and an error returned. To prevent this, there should be a pause between requests (the example scripts do this). In Python, this can be done by:
import time
...
time.sleep(1) # sleep for 1 second
For reference, here are some json-formatted files may be helpful:
The assignment is to be submitted electronically via the Instruct Assist Website by 11:59pm on the day due.
The submission is a report in PDF, named proj4_lastname.pdf
.
To submit your assignment, log into the Instruct Assist website:
Use your WPI username and password for access. Visit:
Tools --> File Submission
Select "Project 4" from the dropdown and then "Browse" and select the assignment file (i.e., proj4_lastname.pdf
).
Make sure to hit "Upload File" after selecting it!
If successful, there should be a line similar to:
Creator Upload Time File Name Size Status Removal
Claypool 2018-04-17 12:19:17 proj4_claypool.pdf 1203 KB On Time Delete
All accomplishments are shown through the report.
Part 1 (Summoner) - 40% :
10% general
10% duration of matches
10% most played Champions
10% in-depth
Part 2 (Gold) - 40% :
10% general
10% total gold
10% gold for win vs. loss
10% additional
Part 3 (Puck Hunt) - 20% :
10% participating in study
5% rating versus lag
5% additional analysis
100-90. The submission clearly exceeds requirements. All parts of the project have been completed or nearly completed. The report is clearly organized and well-written, charts and tables are clearly labeled and described and messages provided about each part of the analysis.
89-80. The submission meets requirements. The first 3 parts of the project have been completed or nearly completed, but perhaps not completely. The report is organized and well-written, charts and tables are labeled and described and messages provided about most of the analysis.
79-70. The submission barely meets requirements. The first 2 parts of the project have been completed or nearly completed, but perhaps not part 3. The report is semi-organized and semi-well-written, charts and tables are somewhat labeled and described, but parts may be missing. Messages are not always clearly provided for the analysis.
69-60. The project fails to meet requirements in some places. Not all parts of the project are completed. The report is not well-organized nor well-written, charts and tables are not labeled or may be missing. Messages are not always provided for the analysis.
59-0. The project does not meet requirements. No part of the project has been completed. The report is not well-organized nor well-written, charts and tables are not labeled and/or are missing. Messages are not consistently provided for the analysis.
The comments below are in response to graded projects. They are not provided in any particular order.
For some, the "Guidelines for Good Charts" in the Presenting Data slide deck should be re-visited. Make sure to consider charts basics of font sizes, colors, "ink", symbols and so on.
When displaying time units (or any units for that matter), consider what is easiest to understand for the reader. For this project, the League of Legends match duration data is in seconds, but total seconds is not the easiest for the reader. For example, a reader cannot easily understand how long a 1320 second game is, while 22 minutes is much more understandable.
Again, before providing analysis on a dataset, say something about it. In this case, something about the player (e.g., what level/rank?). In the case of ephemeral data that changes, such as the League of Legends API, when the data was gathered (exact date) is important. The most recent 100 matches played changes daily for many players!
For gold analysis, the difference in gold reported is of interest, but particularly the difference relative to the total gold. Is it that a relatively massive difference in gold matters or does even a slight (e.g., 1%) difference matter?
In most cases, the match number itself is mostly arbitrary and should not be used as an independent axis. The only exception for this if a trend over time is considered, but even in this case, time would be a more natural axis label.
While in-depth analysis could be attempted for 1 game, statistically speaking, there is little value in this. You cannot generalize about, say, the effect on of gold on matches from a single case.
For a couple of well-done project examples, see:
Return to the IMGD 2905 home page
Questions: imgd2905 question-answer forum