In League of Legends, an online battle arena game in which players choose a character (a champion) with unique abilities and fight together in matches against an opposing team. It is very popular, and has a competitive player base.
The mechanics of the game aren't important for understanding this demo, but one mechanic is of interest: before each match, each team can choose to ban up to three champion types from the game. Teams may do this because they perceive particular champions as broken (unfair to use), or to give themselves an advantage in the match. However, it's generally not established what, if any, advantage banning a character gives to a team. We seek to ask questions on this topic here.
Alyssa Adams has pulled data for each match from Riot Games' public API. For each match, we have a directory named with the match's unique ID, and this contains a couple things. For example:
%ls matches/2353211982
match.csv Treant.c7633606-6432-4cb8-a673-1faa37f3a56b.json
This match contains a CSV file giving some of the most interesting match data to Alyssa, which we can parse using pandas
:
import pandas as pd
pd.read_csv('matches/2353211982/match.csv', index_col=0)
Player Name | Champion | Win | Side | Kills | Deaths | Assists | KDA | CS | SSpell 1 | ... | Item1 | Item2 | Item3 | Item4 | Item5 | Item6 | Magic Dmg | Physical Dmg | Lane | Role | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Doublelift | Draven | False | Side.blue | 1 | 5 | 1 | 0.400000 | 141 | Flash | ... | Doran's Blade | Caulfield's Warhammer | Cull | Infinity Edge | Boots of Speed | Dagger | 916 | 52136 | Lane.bot_lane | Role.carry |
1 | Stixxay | Vi | False | Side.blue | 2 | 3 | 2 | 1.333333 | 105 | Smite | ... | Chain Vest | Refillable Potion | Ninja Tabi | Enchantment: Warrior | Giant's Belt | NaN | 612 | 78017 | Lane.jungle | Role.none |
2 | Pocket Rhino | Karma | False | Side.blue | 1 | 3 | 4 | 1.666667 | 106 | Flash | ... | Haunting Guise | Athene's Unholy Grail | Ionian Boots of Lucidity | Doran's Ring | NaN | NaN | 46740 | 7879 | Lane.mid_lane | Role.solo |
3 | Old Man Lohpally | Nami | False | Side.blue | 1 | 4 | 1 | 0.500000 | 11 | Exhaust | ... | Total Biscuit of Rejuvenation | Eye of the Watchers | Control Ward | Boots of Mobility | Tear of the Goddess | NaN | 5274 | 3058 | Lane.bot_lane | Role.support |
4 | Licorice | Jarvan IV | False | Side.blue | 1 | 4 | 3 | 1.000000 | 97 | Flash | ... | Tiamat | Corrupting Potion | Doran's Blade | Chain Vest | Ninja Tabi | Bami's Cinder | 2563 | 42171 | Lane.top_lane | Role.solo |
5 | Hi Im A Sheep | Janna | True | Side.red | 0 | 0 | 14 | 14.000000 | 6 | Flash | ... | Eye of the Watchers | Control Ward | NaN | Forbidden Idol | Crystalline Bracer | Ionian Boots of Lucidity | 4298 | 3424 | Lane.bot_lane | Role.support |
6 | Pyrites | Vel'Koz | True | Side.red | 5 | 3 | 5 | 3.333333 | 133 | Flash | ... | Doran's Ring | Aether Wisp | Total Biscuit of Rejuvenation | Morellonomicon | Ionian Boots of Lucidity | Doran's Ring | 58409 | 5572 | Lane.mid_lane | Role.solo |
7 | Révenge | Yasuo | True | Side.red | 8 | 3 | 3 | 3.666667 | 154 | Teleport | ... | Doran's Blade | Doran's Blade | Trinity Force | Phantom Dancer | Ninja Tabi | B. F. Sword | 27726 | 76857 | Lane.top_lane | Role.solo |
8 | windking66 | Ezreal | True | Side.red | 1 | 0 | 6 | 7.000000 | 154 | Heal | ... | Manamune | Doran's Blade | Ionian Boots of Lucidity | Cull | Iceborn Gauntlet | Vampiric Scepter | 11608 | 55667 | Lane.bot_lane | Role.carry |
9 | kms myself | Zac | True | Side.red | 5 | 1 | 6 | 11.000000 | 136 | Smite | ... | Enchantment: Cinderhulk | Spirit Visage | Ionian Boots of Lucidity | Giant's Belt | Warden's Mail | NaN | 91990 | 15130 | Lane.jungle | Role.none |
10 rows × 25 columns
Each row corresponds to a given player in the match, and gives information such as what champion they used, which side they were on, how many kills, deaths, etc., and whether their team won.
In addition to this file, we also have a funny looking Treant.*.json
file. This is a datreant
state file, and it's pure JSON:
%cat matches/2353211982/Treant.c7633606-6432-4cb8-a673-1faa37f3a56b.json
{"categories": {"Map": "Map.summoners_rift", "Red Ban 3": "Lee Sin", "Hour": 0, "Season": "Season.preseason_7", "Region": "Region.north_america", "MatchID": 2353211982, "Month": 11, "Queue": "Queue.ranked_solo_queue", "Platform": "Platform.north_america", "VersionPatch": "6.22.165.9605", "Blue Ban 1": "Graves", "Mode": "GameMode.classic", "Blue Ban 3": "Ivern", "Year": 2016, "Duration": "0:21:12", "Red Ban 2": "LeBlanc", "Red Ban 1": "Ekko", "Type": "GameType.matched", "Day": 22, "Blue Ban 2": "Caitlyn"}, "tags": []}
We've used this to store information about the match as a whole, not player-specific data. This contains several categories
(key-value pairs), giving things like the map, the game type, the duration, and, importantly for this study, which bans were chosen by each team. We could use any old JSON parser to use this file, such as the built-in json.load
function:
import json
with open('matches/2353211982/Treant.c7633606-6432-4cb8-a673-1faa37f3a56b.json', 'r') as f:
match = json.load(f)
match
{u'categories': {u'Blue Ban 1': u'Graves', u'Blue Ban 2': u'Caitlyn', u'Blue Ban 3': u'Ivern', u'Day': 22, u'Duration': u'0:21:12', u'Hour': 0, u'Map': u'Map.summoners_rift', u'MatchID': 2353211982, u'Mode': u'GameMode.classic', u'Month': 11, u'Platform': u'Platform.north_america', u'Queue': u'Queue.ranked_solo_queue', u'Red Ban 1': u'Ekko', u'Red Ban 2': u'LeBlanc', u'Red Ban 3': u'Lee Sin', u'Region': u'Region.north_america', u'Season': u'Season.preseason_7', u'Type': u'GameType.matched', u'VersionPatch': u'6.22.165.9605', u'Year': 2016}, u'tags': []}
But instead we'll use this in a more refined way, using datreant
:
import datreant.core as dtr
match = dtr.Treant('matches/2353211982')
match
<Treant: '2353211982'>
This file is a datreant
state file, which stores the state for a Treant
. This is a library that makes it easy to treat directories in the filesystem as logical units that can be introspected easily and tagged with metadata:
match.draw()
2353211982/ +-- match.csv +-- Treant.c7633606-6432-4cb8-a673-1faa37f3a56b.json
match['match.csv'].read()
",Player Name,Champion,Win,Side,Kills,Deaths,Assists,KDA,CS,SSpell 1,SSpell 2,End Level,First Blood?,Gold Earned,Gold Spent,Item1,Item2,Item3,Item4,Item5,Item6,Magic Dmg,Physical Dmg,Lane,Role\n0,Doublelift,Draven,False,Side.blue,1,5,1,0.4,141,Flash,Heal,9,False,6616,6250,Doran's Blade,Caulfield's Warhammer,Cull,Infinity Edge,Boots of Speed,Dagger,916,52136,Lane.bot_lane,Role.carry\n1,Stixxay,Vi,False,Side.blue,2,3,2,1.3333333333333333,105,Smite,Flash,11,False,6616,5900,Chain Vest,Refillable Potion,Ninja Tabi,Enchantment: Warrior,Giant's Belt,,612,78017,Lane.jungle,Role.none\n2,Pocket Rhino,Karma,False,Side.blue,1,3,4,1.6666666666666667,106,Flash,Ghost,11,False,5760,5175,Haunting Guise,Athene's Unholy Grail,Ionian Boots of Lucidity,Doran's Ring,,,46740,7879,Lane.mid_lane,Role.solo\n3,Old Man Lohpally,Nami,False,Side.blue,1,4,1,0.5,11,Exhaust,Flash,9,False,4977,4275,Total Biscuit of Rejuvenation,Eye of the Watchers,Control Ward,Boots of Mobility,Tear of the Goddess,,5274,3058,Lane.bot_lane,Role.support\n4,Licorice,Jarvan IV,False,Side.blue,1,4,3,1.0,97,Flash,Teleport,11,False,5810,5300,Tiamat,Corrupting Potion,Doran's Blade,Chain Vest,Ninja Tabi,Bami's Cinder,2563,42171,Lane.top_lane,Role.solo\n5,Hi Im A Sheep,Janna,True,Side.red,0,0,14,14.0,6,Flash,Exhaust,10,False,6099,5225,Eye of the Watchers,Control Ward,,Forbidden Idol,Crystalline Bracer,Ionian Boots of Lucidity,4298,3424,Lane.bot_lane,Role.support\n6,Pyrites,Vel'Koz,True,Side.red,5,3,5,3.3333333333333335,133,Flash,Ghost,11,False,8203,6100,Doran's Ring,Aether Wisp,Total Biscuit of Rejuvenation,Morellonomicon,Ionian Boots of Lucidity,Doran's Ring,58409,5572,Lane.mid_lane,Role.solo\n7,R\xc3\xa9venge,Yasuo,True,Side.red,8,3,3,3.6666666666666665,154,Teleport,Flash,13,True,10656,10038,Doran's Blade,Doran's Blade,Trinity Force,Phantom Dancer,Ninja Tabi,B. F. Sword,27726,76857,Lane.top_lane,Role.solo\n8,windking66,Ezreal,True,Side.red,1,0,6,7.0,154,Heal,Flash,11,False,8027,7925,Manamune,Doran's Blade,Ionian Boots of Lucidity,Cull,Iceborn Gauntlet,Vampiric Scepter,11608,55667,Lane.bot_lane,Role.carry\n9,kms myself,Zac,True,Side.red,5,1,6,11.0,136,Smite,Flash,13,False,8969,8550,Enchantment: Cinderhulk,Spirit Visage,Ionian Boots of Lucidity,Giant's Belt,Warden's Mail,,91990,15130,Lane.jungle,Role.none\n"
match.categories
<Categories({u'Map': u'Map.summoners_rift', u'Red Ban 3': u'Lee Sin', u'Hour': 0, u'Season': u'Season.preseason_7', u'Region': u'Region.north_america', u'MatchID': 2353211982, u'Month': 11, u'Queue': u'Queue.ranked_solo_queue', u'Platform': u'Platform.north_america', u'VersionPatch': u'6.22.165.9605', u'Blue Ban 1': u'Graves', u'Mode': u'GameMode.classic', u'Blue Ban 3': u'Ivern', u'Year': 2016, u'Duration': u'0:21:12', u'Red Ban 2': u'LeBlanc', u'Red Ban 1': u'Ekko', u'Type': u'GameType.matched', u'Day': 22, u'Blue Ban 2': u'Caitlyn'})>
match.categories['Map']
u'Map.summoners_rift'
Having our match data in this form lets us ask lots of questions with very little friction. If you're familiar with relational databases, in a way it lets one work with directories as if they were part of a relational database. And the files inside these directories can be anything, not just tables like we have here.
Treant
s allow introspection into their own trees to any level of detail required, but we can do powerful things with them in aggregate. Let's gather all our matches up into a Bundle
:
matches = dtr.discover('matches/')
len(matches)
5244
This Bundle
has a few thousand Treant
s. It's important to remember that a Treant
is just a directory in the filesystem with a state file, which serves as:
Treant
, andThe Treant
object (that is, the Python object in memory) is itself very light. It only really amounts to a reference to the state file, which gets parsed on access of things like tags
and categories
.
A Bundle
can be used to work with a collection of Treant
s as if it was a single logical unit:
matches[:5].draw()
2359450630/ +-- Treant.1bf087dc-24c8-4982-9c18-24f8b8a9809d.json +-- match.csv 2363071768/ +-- Treant.cbd4a102-e9bd-4db8-a723-9c1f8804e34b.json +-- match.csv 2353972809/ +-- match.csv +-- Treant.db872e16-43f1-4a69-ad2b-fbabbce7d35e.json 2361686875/ +-- Treant.e5d550c0-9995-4f5f-afa0-1a19f31c2d1e.json +-- match.csv 2361675597/ +-- Treant.42fd8b90-725e-4a83-9829-7249459e2727.json +-- match.csv
We can, for example, get back any category keys present among these Treant
s:
matches.categories.any.keys()
[u'Map', u'Red Ban 3', u'Hour', u'Year', u'Season', u'fluffy', u'MatchID', u'Month', u'Queue', u'Platform', u'VersionPatch', u'Blue Ban 1', u'Mode', u'Blue Ban 3', u'Blue Ban 2', u'Duration', u'Red Ban 2', u'Red Ban 1', u'Region', u'Type', u'Day']
And perhaps get back the values for one:
matches[:10].categories['Mode']
[u'GameMode.classic', u'GameMode.classic', u'GameMode.classic', u'GameMode.classic', u'GameMode.classic', u'GameMode.classic', u'GameMode.classic', u'GameMode.classic', u'GameMode.classic', u'GameMode.classic']
For starters, we'll ask a simple question. If the champion Teemo is banned, does this make his team more likely to win? Does it make any difference? First, we need to extract matches in which Teemo is banned. Let's keep blue and red bans separate for now, since this might make answering the question easier later.
First, we'll get all the matches in which the blue team banned Teemo:
blueban = dtr.Bundle([matches.categories.groupby(ban)['Teemo'] for ban in ('Blue Ban 1', 'Blue Ban 2', 'Blue Ban 3')])
blueban
<Bundle([<Treant: '2358429974'>, <Treant: '2359613323'>, <Treant: '2356208063'>, <Treant: '2357946733'>, <Treant: '2365070298'>, <Treant: '2363656370'>, <Treant: '2362300366'>, <Treant: '2353277148'>, <Treant: '2356102823'>, <Treant: '2361668484'>, <Treant: '2359826812'>, <Treant: '2362658805'>, <Treant: '2363940703'>])>
And we'll do the same for the red team:
redban = dtr.Bundle([matches.categories.groupby(ban)['Teemo'] for ban in ('Red Ban 1', 'Red Ban 2', 'Red Ban 3')])
redban
<Bundle([<Treant: '2364722045'>, <Treant: '2358810869'>, <Treant: '2359142368'>, <Treant: '2364861102'>, <Treant: '2355510307'>, <Treant: '2353779432'>, <Treant: '2356203166'>, <Treant: '2353343511'>, <Treant: '2353591503'>, <Treant: '2363881294'>, <Treant: '2358936032'>, <Treant: '2358759278'>, <Treant: '2362831230'>, <Treant: '2364853411'>, <Treant: '2356318495'>, <Treant: '2361485395'>, <Treant: '2355511251'>, <Treant: '2360581895'>, <Treant: '2362170628'>, <Treant: '2357290762'>, <Treant: '2361910541'>, <Treant: '2354772707'>, <Treant: '2363076685'>, <Treant: '2361483300'>, <Treant: '2358621768'>])>
Because Bundle
s behave as ordered sets, if we wanted all the matches where Teemo was banned by at least one team, then we could get it with:
redban + blueban
<Bundle([<Treant: '2364722045'>, <Treant: '2358810869'>, <Treant: '2359142368'>, <Treant: '2364861102'>, <Treant: '2355510307'>, <Treant: '2353779432'>, <Treant: '2356203166'>, <Treant: '2353343511'>, <Treant: '2353591503'>, <Treant: '2363881294'>, <Treant: '2358936032'>, <Treant: '2358759278'>, <Treant: '2362831230'>, <Treant: '2364853411'>, <Treant: '2356318495'>, <Treant: '2361485395'>, <Treant: '2355511251'>, <Treant: '2360581895'>, <Treant: '2362170628'>, <Treant: '2357290762'>, <Treant: '2361910541'>, <Treant: '2354772707'>, <Treant: '2363076685'>, <Treant: '2361483300'>, <Treant: '2358621768'>, <Treant: '2358429974'>, <Treant: '2359613323'>, <Treant: '2356208063'>, <Treant: '2357946733'>, <Treant: '2365070298'>, <Treant: '2363656370'>, <Treant: '2362300366'>, <Treant: '2353277148'>, <Treant: '2356102823'>, <Treant: '2361668484'>, <Treant: '2359826812'>, <Treant: '2362658805'>, <Treant: '2363940703'>])>
And if we wanted all matches where Teemo was banned by both teams, we could do an intersection of the two:
redban & blueban
<Bundle([])>
This is expected, because both teams can't ban the same champion. This is a nice check. :)
For red team bans, how many of those matches did the red team lose? Looking at what a given match CSV gives us:
df = pd.read_csv(redban[0]['match.csv'].abspath, index_col=0)
df
Player Name | Champion | Win | Side | Kills | Deaths | Assists | KDA | CS | SSpell 1 | ... | Item1 | Item2 | Item3 | Item4 | Item5 | Item6 | Magic Dmg | Physical Dmg | Lane | Role | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Koyori | Nami | True | Side.blue | 4 | 2 | 15 | 9.500000 | 4 | Exhaust | ... | Frost Queen's Claim | Redemption | NaN | Sightstone | Boots of Mobility | NaN | 7571 | 4104 | Lane.bot_lane | Role.support |
1 | TNL Astatine | Jhin | True | Side.blue | 9 | 6 | 8 | 2.833333 | 128 | Flash | ... | Zeal | Essence Reaver | Kircheis Shard | Doran's Blade | NaN | Boots of Swiftness | 1592 | 89816 | Lane.bot_lane | Role.carry |
2 | Xelverak | Nasus | True | Side.blue | 2 | 3 | 2 | 1.333333 | 155 | Teleport | ... | Doran's Ring | Corrupting Potion | Zz'Rot Portal | Doran's Ring | Boots of Speed | Sheen | 60591 | 36817 | Lane.top_lane | Role.solo |
3 | LL Foil | Elise | True | Side.blue | 9 | 4 | 8 | 4.250000 | 87 | Flash | ... | Ionian Boots of Lucidity | Hunter's Potion | Control Ward | Enchantment: Runic Echoes | Rylai's Crystal Scepter | Hextech Protobelt-01 | 64562 | 24447 | Lane.jungle | Role.none |
4 | Butler Epsilon | Ryze | True | Side.blue | 4 | 5 | 6 | 2.000000 | 168 | Flash | ... | Archangel's Staff | NaN | The Dark Seal | NaN | Rod of Ages | Sorcerer's Shoes | 105988 | 9350 | Lane.mid_lane | Role.solo |
5 | holygoose | Ashe | False | Side.red | 3 | 10 | 5 | 0.800000 | 19 | Ignite | ... | Sightstone | Pickaxe | Ionian Boots of Lucidity | Rapid Firecannon | B. F. Sword | NaN | 3156 | 22902 | Lane.bot_lane | Role.support |
6 | Krypt | Jinx | False | Side.red | 3 | 6 | 5 | 1.333333 | 142 | Flash | ... | Brawler's Gloves | Infinity Edge | Runaan's Hurricane | Dagger | Doran's Blade | Berserker's Greaves | 833 | 78174 | Lane.bot_lane | Role.carry |
7 | Azhi Dahaki | Shyvana | False | Side.red | 3 | 6 | 3 | 1.000000 | 97 | Smite | ... | Enchantment: Bloodrazor | Refillable Potion | Titanic Hydra | Mercury's Treads | NaN | NaN | 32708 | 47144 | Lane.jungle | Role.none |
8 | Happy Birfdizzay | Twitch | False | Side.red | 7 | 3 | 1 | 2.666667 | 178 | Flash | ... | Long Sword | Runaan's Hurricane | B. F. Sword | Lord Dominik's Regards | Berserker's Greaves | Doran's Blade | 0 | 99027 | Lane.mid_lane | Role.solo |
9 | Lord Gomenator | Maokai | False | Side.red | 4 | 3 | 5 | 3.000000 | 146 | Flash | ... | Doran's Ring | Spirit Visage | Chain Vest | Doran's Ring | Mercury's Treads | Bami's Cinder | 81784 | 9724 | Lane.top_lane | Role.solo |
10 rows × 25 columns
We could get this by checking if the word 'blue'
is in the 'Win'
column for rows where that column gives True
:
def blue_win(match):
df = pd.read_csv(match['match.csv'].abspath, index_col=0)
return 'blue' in df[df['Win'] == True]['Side'].iloc[0]
So we could then get back a list of bools telling us which matches blue won among those where the red team banned Teemo:
redban_blue_wins = redban.map(blue_win)
redban_blue_wins
[True, True, False, False, True, False, False, True, False, True, True, False, False, False, True, False, True, False, False, False, True, False, False, False, True]
Did red win more often than they lost? Here are the matches red won:
len(redban - redban[redban_blue_wins])
15
Here are the number of matches red lost:
len(redban[redban_blue_wins])
10
We used this list of bools as a boolean index for our redban
Bundle
, here.
We'll do the same thing for matches in which the blue team banned Teemo:
blueban_blue_wins = blueban.map(blue_win)
blueban_blue_wins
[False, False, True, False, False, True, False, False, True, True, True, False, True]
Did blue win more often than they lost? Here are the matches blue won:
len(blueban[blueban_blue_wins])
6
Here are the number of matches blue lost:
len(blueban - blueban[blueban_blue_wins])
7
Hmmm...maybe not a signal here, but the number of samples is pretty small anyhow to say anything.
We want to take to task some conventional wisdom that may be floating around the community about which characters you should ban if you want to increase your chances to win. Let's see if this holds water.
First we need to find the most banned character. Probably lots of ways to get at this, but one thing we can do is a groupby
on each ban slot as before, for each building a dictionary giving banned champions as keys and the number of matches they were banned in as values:
counts = pd.DataFrame({ban: {key: len(value) for key, value in matches.categories.groupby(ban).items()} for ban in ('Blue Ban 1', 'Blue Ban 2', 'Blue Ban 3', 'Red Ban 1', 'Red Ban 2', 'Red Ban 3')})
counts
Blue Ban 1 | Blue Ban 2 | Blue Ban 3 | Red Ban 1 | Red Ban 2 | Red Ban 3 | |
---|---|---|---|---|---|---|
Aatrox | 77 | 68 | 95 | 93 | 93 | 67 |
Ahri | 5 | 11 | 6 | 7 | 5 | 11 |
Akali | 4 | 7 | 3 | 3 | 5 | 4 |
Alistar | 14 | 21 | 18 | 22 | 21 | 24 |
Amumu | 7 | 14 | 9 | 9 | 10 | 9 |
Anivia | 2 | 2 | 1 | 3 | 8 | 5 |
Annie | 11 | 12 | 10 | 12 | 12 | 3 |
Ashe | 1 | NaN | NaN | 1 | NaN | 1 |
Aurelion Sol | 17 | 14 | 19 | 9 | 14 | 19 |
Azir | 1 | NaN | 4 | 4 | NaN | NaN |
Bard | 1 | 2 | 2 | NaN | 4 | 1 |
Blitzcrank | 12 | 9 | 9 | 8 | 11 | 12 |
Brand | 8 | 10 | 10 | 8 | 4 | 10 |
Braum | 2 | 2 | 3 | 1 | 2 | 2 |
Caitlyn | 24 | 27 | 32 | 27 | 33 | 45 |
Cassiopeia | 21 | 27 | 20 | 19 | 29 | 33 |
Cho'Gath | 1 | 1 | 1 | 1 | NaN | 2 |
Corki | 1 | 1 | 2 | NaN | 2 | 1 |
Darius | 17 | 24 | 22 | 16 | 27 | 26 |
Diana | 2 | 2 | 4 | 1 | 1 | 1 |
Dr. Mundo | 1 | NaN | 1 | NaN | 1 | NaN |
Draven | 1 | 5 | 1 | 3 | 2 | 4 |
Ekko | 6 | 5 | 5 | 5 | 7 | 9 |
Elise | 10 | 6 | 9 | 13 | 6 | 16 |
Evelynn | 53 | 73 | 70 | 70 | 65 | 74 |
Ezreal | 2 | 2 | 2 | 3 | 3 | 3 |
Fiddlesticks | 1 | 1 | NaN | NaN | 3 | 2 |
Fiora | 8 | 11 | 16 | 14 | 6 | 9 |
Fizz | 35 | 40 | 49 | 36 | 35 | 57 |
Galio | 4 | 4 | 9 | 3 | 1 | 2 |
... | ... | ... | ... | ... | ... | ... |
Talon | 43 | 66 | 74 | 51 | 51 | 58 |
Taric | NaN | NaN | NaN | NaN | 1 | NaN |
Teemo | 7 | 3 | 3 | 6 | 10 | 9 |
Thresh | 2 | 4 | 2 | 2 | NaN | 2 |
Tristana | NaN | NaN | NaN | NaN | 1 | NaN |
Trundle | 1 | 1 | 1 | 1 | 1 | 2 |
Tryndamere | 3 | 3 | 1 | 4 | 6 | 2 |
Twisted Fate | 5 | 6 | 11 | 6 | 3 | 7 |
Twitch | 54 | 50 | 57 | 39 | 56 | 64 |
Udyr | NaN | NaN | 1 | 2 | NaN | 1 |
Urgot | 1 | 4 | 2 | 1 | 1 | 4 |
Varus | NaN | 1 | NaN | 1 | NaN | NaN |
Vayne | 253 | 302 | 284 | 275 | 285 | 297 |
Veigar | 1 | 1 | 1 | 1 | 3 | 5 |
Vel'Koz | 1 | NaN | 1 | 1 | 3 | 1 |
Vi | 43 | 39 | 58 | 45 | 44 | 49 |
Viktor | 6 | 11 | 20 | 13 | 12 | 13 |
Vladimir | 2 | 2 | 1 | 2 | 1 | 1 |
Volibear | 1 | NaN | NaN | NaN | 3 | 1 |
Warwick | 2 | NaN | 1 | 1 | 3 | 2 |
Wukong | 2 | 1 | NaN | 1 | NaN | NaN |
Xerath | 6 | 7 | 4 | 6 | 5 | 3 |
Xin Zhao | 1 | 3 | 1 | 2 | NaN | NaN |
Yasuo | 109 | 104 | 113 | 124 | 118 | 109 |
Yorick | NaN | 2 | 1 | 2 | 2 | 6 |
Zac | 193 | 243 | 228 | 182 | 215 | 182 |
Zed | 12 | 12 | 29 | 8 | 13 | 18 |
Ziggs | 1 | 1 | 1 | 1 | 1 | 1 |
Zilean | 1 | 3 | 2 | 1 | 4 | 1 |
Zyra | 23 | 30 | 28 | 14 | 18 | 20 |
133 rows × 6 columns
We can then sum across columns and sort to get a ranking of banned champions across all matches:
counts.sum(axis=1).sort_values(ascending=False)
Rengar 3514 Hecarim 2779 LeBlanc 2646 Ivern 2549 Syndra 2143 Vayne 1696 Kha'Zix 1510 Ryze 1459 Zac 1243 Katarina 1240 Jayce 1031 Lee Sin 849 Graves 776 Yasuo 677 Aatrox 493 Riven 467 Olaf 415 Evelynn 405 Poppy 385 Talon 343 Janna 329 Twitch 320 Kennen 313 Vi 278 Fizz 252 Shyvana 191 Caitlyn 188 Shaco 176 Cassiopeia 149 Zyra 133 ... Cho'Gath 6 Ziggs 6 Skarner 5 Sejuani 5 Swain 5 Leona 5 Kindred 5 Volibear 5 Kassadin 5 Lulu 5 Udyr 4 Kalista 4 Kog'Maw 4 Lissandra 4 Taliyah 4 Jarvan IV 4 Miss Fortune 4 Wukong 4 Jinx 3 Dr. Mundo 3 Ashe 3 Sona 3 Karthus 3 Lucian 2 Nami 2 Sion 2 Sivir 2 Varus 2 Tristana 1 Taric 1 dtype: float64
So we have our answer. The most banned is Rengar.
Out of curiosity, what's the distribution of bans look like across champions?
%matplotlib inline
/home/alter/.virtualenvs/datreant/lib/python2.7/site-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment. warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')
counts.sum(axis=1).sort_values(ascending=False).plot()
<matplotlib.axes._subplots.AxesSubplot at 0x7f61c77de210>
We'll do the same analysis we did for Teemo, but now for Rengar.
blueban = dtr.Bundle([matches.categories.groupby(ban)['Rengar'] for ban in ('Blue Ban 1', 'Blue Ban 2', 'Blue Ban 3')])
len(blueban)
1855
redban = dtr.Bundle([matches.categories.groupby(ban)['Rengar'] for ban in ('Red Ban 1', 'Red Ban 2', 'Red Ban 3')])
len(redban)
1659
For red team bans, how many of those matches did the red team lose?
Did red win more often than they lost? Here are the matches red won:
len(redban - redban[redban.map(blue_win)])
820
Here are the number of matches red lost:
len(redban[redban.map(blue_win)])
839
Did blue win more often than they lost? Here are the matches blue won:
len(blueban[blueban.map(blue_win)])
934
Here are the number of matches blue lost:
len(blueban - blueban[blueban.map(blue_win)])
921
Hmmm...maybe not a signal here...might not be much point overall to banning Rengar. However, it may be the case that if you ban Rengar and perhaps another character this improves your chances? That's an interesting question for another time. Could it also be the case that banning Rengar levels the playing field?
We already know that banning Rengar doesn't seem to make your team more likely to win, but does not banning him make a team with Rengar more likely to win? Is he even picked by a player?
First we need matches where Rengar was not banned. We'll iterate through all ban slots, grab out the matches where the banned champion was Rengar, then combine these Bundle
s into a single one. We can then subtract this Bundle
from all the matches to get only those where Rengar was not banned:
rengar_not_banned = matches - dtr.Bundle([matches.categories.groupby(ban)['Rengar'] for ban in ('Blue Ban 1', 'Blue Ban 2', 'Blue Ban 3', 'Red Ban 1', 'Red Ban 2', 'Red Ban 3')])
len(rengar_not_banned)
1730
So now, which matches was Rengar present in? We can get this like we did before, by checking each match CSV. this time, we just want to know if 'Rengar'
shows up in 'Champion'
column:
def has_rengar(match):
df = pd.read_csv(match['match.csv'].abspath, index_col=0)
return len(df[df['Champion'] == 'Rengar']) == 1
matches_has_rengar = rengar_not_banned[rengar_not_banned.map(has_rengar)]
len(matches_has_rengar)
372
So, if a team has a Rengar, did they also tend to win? We can check the CSV files again to see if the player using Rengar was also on the winning team:
def rengar_won(match):
df = pd.read_csv(match['match.csv'].abspath, index_col=0)
return bool(df[df['Champion'] == 'Rengar']['Win'].iloc[0])
Number of wins:
len(matches_has_rengar[matches_has_rengar.map(rengar_won, processes=4)])
192
Number of losses:
len(matches_has_rengar - matches_has_rengar[matches_has_rengar.map(rengar_won, processes=4)])
180
Doesn't really look like he confers too much of an advantage, but perhaps a slight one?
There are tons of questions we can ask, and even complex ones are surprisingly easy to do so given the way we've structured our data, and how datreant
allows us to work with it. Here's a smattering:
We can do a similar analysis as we did for Rengar on all champions, and if it's the case that having a particular champion on your team results in more wins on average, we can begin drilling down into more questions to establish why. First let's see what we get.
We'll gather all dataframes into a single, big dataframe with an extra index giving the uuid of the Treant
for unique identification. This will make writing code that gets the number of wins/losses for a particular champion faster, since we can operate on a single DataFrame
instead of ~5000:
match_dfs = [pd.read_csv(match['match.csv'].abspath, index_col=0) for match in matches]
bigdf = pd.concat(match_dfs, keys=matches.uuids, names=['uuids'])
bigdf
Player Name | Champion | Win | Side | Kills | Deaths | Assists | KDA | CS | SSpell 1 | ... | Item1 | Item2 | Item3 | Item4 | Item5 | Item6 | Magic Dmg | Physical Dmg | Lane | Role | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
uuids | ||||||||||||||||||||||
1bf087dc-24c8-4982-9c18-24f8b8a9809d | 0 | Anonymity Missay | Twisted Fate | False | Side.blue | 2 | 11 | 9 | 1.000000 | 135 | Flash | ... | Doran's Ring | Abyssal Scepter | Refillable Potion | Ionian Boots of Lucidity | Lich Bane | Seeker's Armguard | 89851 | 13855 | Lane.mid_lane | Role.solo |
1 | Queen Cersei | Janna | False | Side.blue | 1 | 9 | 14 | 1.666667 | 9 | Exhaust | ... | Boots of Speed | Sightstone | Nomad's Medallion | Total Biscuit of Rejuvenation | Locket of the Iron Solari | Redemption | 5938 | 4799 | Lane.bot_lane | Role.support | |
2 | Tyrion with xBow | Vayne | False | Side.blue | 8 | 12 | 4 | 1.000000 | 131 | Heal | ... | Doran's Blade | Berserker's Greaves | Infinity Edge | Statikk Shiv | Hexdrinker | Dagger | 14328 | 67261 | Lane.bot_lane | Role.carry | |
3 | KOKOA | Gragas | False | Side.blue | 2 | 8 | 8 | 1.250000 | 97 | Smite | ... | Giant's Belt | Refillable Potion | The Dark Seal | Spirit Visage | Enchantment: Cinderhulk | Ninja Tabi | 70099 | 18811 | Lane.jungle | Role.none | |
4 | UseIess Riven | Riven | False | Side.blue | 10 | 10 | 5 | 1.500000 | 212 | Teleport | ... | Edge of Night | Ravenous Hydra | B. F. Sword | Ionian Boots of Lucidity | The Black Cleaver | Long Sword | 3687 | 146519 | Lane.top_lane | Role.solo | |
5 | 9point5mechanics | LeBlanc | True | Side.red | 19 | 5 | 10 | 5.800000 | 192 | Teleport | ... | Rabadon's Deathcap | Void Staff | Rylai's Crystal Scepter | Doran's Ring | Morellonomicon | Sorcerer's Shoes | 122594 | 13799 | Lane.mid_lane | Role.solo | |
6 | We Win Late Game | Tahm Kench | True | Side.red | 7 | 6 | 25 | 5.333333 | 27 | Flash | ... | Locket of the Iron Solari | Redemption | Knight's Vow | Boots of Swiftness | Eye of the Equinox | NaN | 12918 | 7586 | Lane.bot_lane | Role.support | |
7 | keine Hosen | Jhin | True | Side.red | 8 | 4 | 20 | 7.000000 | 167 | Flash | ... | Doran's Blade | Infinity Edge | Boots of Swiftness | Essence Reaver | Rapid Firecannon | Pickaxe | 13014 | 122676 | Lane.bot_lane | Role.carry | |
8 | LADYBABY | Malphite | True | Side.red | 7 | 6 | 25 | 5.333333 | 170 | Teleport | ... | Fiendish Codex | Iceborn Gauntlet | Dead Man's Plate | Sunfire Cape | Mejai's Soulstealer | Ninja Tabi | 84411 | 50471 | Lane.top_lane | Role.solo | |
9 | Zorki | Lee Sin | True | Side.red | 9 | 2 | 24 | 16.500000 | 91 | Smite | ... | Titanic Hydra | Randuin's Omen | Mercury's Treads | Enchantment: Warrior | B. F. Sword | NaN | 17171 | 89788 | Lane.jungle | Role.none | |
cbd4a102-e9bd-4db8-a723-9c1f8804e34b | 0 | CUTE TOXIC MID | Sion | True | Side.blue | 3 | 7 | 20 | 3.285714 | 49 | Flash | ... | Redemption | Locket of the Iron Solari | Face of the Mountain | Ruby Sightstone | Mercury's Treads | Crystalline Bracer | 22127 | 22709 | Lane.bot_lane | Role.support |
1 | Savâgekit | Anivia | True | Side.blue | 5 | 2 | 9 | 7.000000 | 314 | Flash | ... | Morellonomicon | Seraph's Embrace | Void Staff | Sorcerer's Shoes | Liandry's Torment | Control Ward | 227309 | 11646 | Lane.mid_lane | Role.solo | |
2 | edbickham | Jayce | True | Side.blue | 8 | 3 | 10 | 6.000000 | 253 | Teleport | ... | Youmuu's Ghostblade | The Black Cleaver | Maw of Malmortius | Doran's Blade | Frozen Mallet | Ninja Tabi | 21876 | 197258 | Lane.top_lane | Role.solo | |
3 | Nickymosh | Vi | True | Side.blue | 10 | 5 | 7 | 3.400000 | 206 | Flash | ... | Enchantment: Warrior | Dead Man's Plate | Sterak's Gage | Mercury's Treads | Trinity Force | Chain Vest | 1351 | 198343 | Lane.jungle | Role.none | |
4 | Kiqko | Ezreal | True | Side.blue | 8 | 6 | 13 | 3.500000 | 245 | Heal | ... | Lord Dominik's Regards | Blade of the Ruined King | Iceborn Gauntlet | Muramana | Ionian Boots of Lucidity | Negatron Cloak | 30518 | 162987 | Lane.bot_lane | Role.carry | |
5 | Fledge | Viktor | False | Side.red | 5 | 7 | 6 | 1.571429 | 224 | Ghost | ... | Perfect Hex Core | Fiendish Codex | Morellonomicon | Rylai's Crystal Scepter | Seeker's Armguard | Ionian Boots of Lucidity | 145783 | 11676 | Lane.mid_lane | Role.solo | |
6 | Sud83 | Kha'Zix | False | Side.red | 13 | 4 | 5 | 4.500000 | 254 | Smite | ... | Maw of Malmortius | Youmuu's Ghostblade | Ravenous Hydra | Mercury's Treads | Enchantment: Warrior | Lord Dominik's Regards | 8245 | 256930 | Lane.jungle | Role.none | |
7 | Welshy JR | Blitzcrank | False | Side.red | 0 | 9 | 12 | 1.333333 | 58 | Ignite | ... | Eye of the Oasis | Glacial Shroud | Zz'Rot Portal | Boots of Mobility | Spirit Visage | NaN | 16637 | 6311 | Lane.bot_lane | Role.support | |
8 | TrustInMyCards | Yorick | False | Side.red | 2 | 8 | 7 | 1.125000 | 284 | Teleport | ... | Randuin's Omen | Guardian Angel | Mercury's Treads | Dead Man's Plate | Trinity Force | NaN | 26858 | 161079 | Lane.top_lane | Role.solo | |
9 | JoeJacko | Caitlyn | False | Side.red | 3 | 7 | 9 | 1.714286 | 253 | Heal | ... | Doran's Blade | Runaan's Hurricane | Infinity Edge | Berserker's Greaves | Rapid Firecannon | Lord Dominik's Regards | 2557 | 187692 | Lane.bot_lane | Role.carry | |
db872e16-43f1-4a69-ad2b-fbabbce7d35e | 0 | Emilia Is Love | Ezreal | False | Side.blue | 2 | 7 | 1 | 0.428571 | 133 | Flash | ... | Boots of Speed | Control Ward | Total Biscuit of Rejuvenation | Trinity Force | Doran's Blade | Tear of the Goddess | 6056 | 57131 | Lane.bot_lane | Role.carry |
1 | retarded virgin | Graves | False | Side.blue | 2 | 9 | 2 | 0.444444 | 68 | Flash | ... | Enchantment: Warrior | Refillable Potion | Berserker's Greaves | Caulfield's Warhammer | Long Sword | NaN | 2375 | 59815 | Lane.jungle | Role.none | |
2 | Koby | Nasus | False | Side.blue | 1 | 1 | 3 | 4.000000 | 169 | Flash | ... | Doran's Ring | Corrupting Potion | Zz'Rot Portal | Doran's Ring | Ninja Tabi | NaN | 54649 | 15413 | Lane.top_lane | Role.solo | |
3 | Shrew | Karma | False | Side.blue | 0 | 6 | 3 | 0.500000 | 23 | Flash | ... | Eye of the Watchers | Forbidden Idol | Ionian Boots of Lucidity | NaN | NaN | NaN | 14041 | 3751 | Lane.bot_lane | Role.support | |
4 | Rip Cig | Vel'Koz | False | Side.blue | 3 | 5 | 0 | 0.600000 | 137 | Flash | ... | Doran's Ring | Morellonomicon | Ionian Boots of Lucidity | Haunting Guise | NaN | NaN | 53490 | 6114 | Lane.mid_lane | Role.solo | |
5 | Feitan | Lee Sin | True | Side.red | 7 | 1 | 7 | 14.000000 | 95 | Smite | ... | Mercury's Treads | Refillable Potion | Youmuu's Ghostblade | Enchantment: Warrior | Giant's Belt | Chain Vest | 13556 | 84407 | Lane.jungle | Role.none | |
6 | FIoopie | Caitlyn | True | Side.red | 10 | 1 | 5 | 15.000000 | 188 | Flash | ... | Doran's Blade | NaN | Infinity Edge | Rapid Firecannon | Berserker's Greaves | Runaan's Hurricane | 304 | 104937 | Lane.bot_lane | Role.carry | |
7 | TRAVYPOOO | Xin Zhao | True | Side.red | 2 | 3 | 1 | 1.000000 | 187 | Flash | ... | Corrupting Potion | Trinity Force | Mercury's Treads | Vampiric Scepter | NaN | Tiamat | 3761 | 87805 | Lane.top_lane | Role.solo | |
8 | MongolWarrior | Corki | True | Side.red | 7 | 1 | 7 | 14.000000 | 152 | Exhaust | ... | Doran's Blade | Hextech Gunblade | Trinity Force | Sorcerer's Shoes | NaN | NaN | 59926 | 21894 | Lane.mid_lane | Role.solo | |
9 | zz cicero | Poppy | True | Side.red | 2 | 2 | 13 | 7.500000 | 41 | Ignite | ... | Dead Man's Plate | Eye of the Equinox | Cloth Armor | Boots of Mobility | NaN | NaN | 1352 | 20405 | Lane.bot_lane | Role.support | |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
5fa307d3-30b8-40e5-bcd6-80013952e244 | 0 | C9 Waygzh | Nunu | True | Side.blue | 1 | 4 | 4 | 1.250000 | 96 | Flash | ... | Mercury's Treads | Refillable Potion | Enchantment: Cinderhulk | Crystalline Bracer | Crystalline Bracer | Cloth Armor | 19823 | 28903 | Lane.jungle | Role.none |
1 | Dexima | Poppy | True | Side.blue | 1 | 2 | 3 | 2.000000 | 142 | Flash | ... | Spirit Visage | Corrupting Potion | Control Ward | Bami's Cinder | Mercury's Treads | Chain Vest | 3206 | 55190 | Lane.top_lane | Role.solo | |
2 | Cat Person | Janna | True | Side.blue | 1 | 3 | 11 | 4.000000 | 8 | Flash | ... | Control Ward | Sightstone | Locket of the Iron Solari | Spellthief's Edge | Crystalline Bracer | Mercury's Treads | 1489 | 3691 | Lane.bot_lane | Role.support | |
3 | Falco Mechanic | Vayne | True | Side.blue | 13 | 5 | 1 | 2.800000 | 167 | Exhaust | ... | Doran's Blade | Infinity Edge | Berserker's Greaves | Phantom Dancer | NaN | NaN | 0 | 106491 | Lane.bot_lane | Role.carry | |
4 | MindCalm | Cassiopeia | True | Side.blue | 2 | 2 | 1 | 1.500000 | 178 | Flash | ... | Refillable Potion | Archangel's Staff | Rylai's Crystal Scepter | NaN | Doran's Ring | NaN | 63034 | 12017 | Lane.mid_lane | Role.solo | |
5 | TaeYeonT | Ezreal | False | Side.red | 1 | 4 | 5 | 1.500000 | 133 | Heal | ... | Doran's Blade | Boots of Speed | Manamune | Iceborn Gauntlet | NaN | Dagger | 9289 | 56555 | Lane.bot_lane | Role.carry | |
6 | HanzoSanada | Maokai | False | Side.red | 2 | 1 | 2 | 4.000000 | 145 | Teleport | ... | Doran's Ring | Doran's Ring | Sunfire Cape | Mercury's Treads | Rejuvenation Bead | Giant's Belt | 63955 | 11029 | Lane.top_lane | Role.solo | |
7 | IIlIllIIlllIII | Morgana | False | Side.red | 1 | 3 | 6 | 2.333333 | 20 | Flash | ... | Frost Queen's Claim | Sightstone | Control Ward | Boots of Mobility | Crystalline Bracer | Faerie Charm | 5013 | 5044 | Lane.bot_lane | Role.support | |
8 | always4u | Ahri | False | Side.red | 7 | 4 | 4 | 2.750000 | 160 | Flash | ... | Needlessly Large Rod | Doran's Ring | Sorcerer's Shoes | Morellonomicon | Rylai's Crystal Scepter | Amplifying Tome | 50741 | 16247 | Lane.mid_lane | Role.solo | |
9 | HvK LiquidDiego | Lee Sin | False | Side.red | 5 | 6 | 6 | 1.833333 | 67 | Flash | ... | Control Ward | Youmuu's Ghostblade | Enchantment: Warrior | Ruby Crystal | Ninja Tabi | Refillable Potion | 11068 | 64680 | Lane.jungle | Role.none | |
61bdd566-3049-48ad-be06-287f9adba3ca | 0 | BestFriendDavis | Lux | False | Side.blue | 3 | 7 | 2 | 0.714286 | 213 | Flash | ... | Needlessly Large Rod | Fiendish Codex | Doran's Ring | Luden's Echo | Morellonomicon | Sorcerer's Shoes | 137496 | 19803 | Lane.mid_lane | Role.solo |
1 | King slayerdan | Sejuani | False | Side.blue | 4 | 7 | 2 | 0.857143 | 127 | Flash | ... | Enchantment: Cinderhulk | Ninja Tabi | Dead Man's Plate | Banshee's Veil | Cloth Armor | Cloth Armor | 84738 | 26744 | Lane.jungle | Role.none | |
2 | LIG Forgiven | Twitch | False | Side.blue | 3 | 6 | 2 | 0.833333 | 253 | Flash | ... | Doran's Blade | Runaan's Hurricane | Berserker's Greaves | Infinity Edge | Phantom Dancer | Long Sword | 0 | 164752 | Lane.bot_lane | Role.carry | |
3 | zBalrog | Bard | False | Side.blue | 0 | 5 | 3 | 0.600000 | 8 | Exhaust | ... | Frost Queen's Claim | Locket of the Iron Solari | Sightstone | Boots of Swiftness | Ruby Crystal | NaN | 20486 | 4803 | Lane.bot_lane | Role.support | |
4 | WatchThePower | Riven | False | Side.blue | 3 | 4 | 1 | 1.000000 | 291 | Flash | ... | The Black Cleaver | Ravenous Hydra | Mercurial Scimitar | Ionian Boots of Lucidity | Last Whisper | NaN | 0 | 190191 | Lane.top_lane | Role.solo | |
5 | c0uchwarrior | Graves | True | Side.red | 4 | 4 | 16 | 5.000000 | 183 | Smite | ... | Enchantment: Warrior | The Black Cleaver | Control Ward | Phantom Dancer | Berserker's Greaves | Maw of Malmortius | 4783 | 201658 | Lane.jungle | Role.none | |
6 | Alan Senpai | Taric | True | Side.red | 2 | 1 | 22 | 24.000000 | 58 | Flash | ... | Sightstone | Face of the Mountain | Locket of the Iron Solari | Knight's Vow | Boots of Swiftness | Glacial Shroud | 17307 | 16165 | Lane.bot_lane | Role.support | |
7 | NVe Enzo | Zilean | True | Side.red | 7 | 2 | 13 | 10.000000 | 184 | Ghost | ... | Morellonomicon | Zhonya's Hourglass | Blasting Wand | Sorcerer's Shoes | Rod of Ages | Needlessly Large Rod | 129032 | 14495 | Lane.jungle | Role.none | |
8 | Jhin To Nguyen | Gangplank | True | Side.red | 4 | 6 | 11 | 2.500000 | 301 | Flash | ... | Phantom Dancer | Trinity Force | Last Whisper | Infinity Edge | Ionian Boots of Lucidity | Statikk Shiv | 18208 | 250786 | Lane.top_lane | Role.solo | |
9 | Jordanian Sniper | Jhin | True | Side.red | 12 | 1 | 8 | 20.000000 | 220 | Flash | ... | Youmuu's Ghostblade | Duskblade of Draktharr | Rapid Firecannon | Infinity Edge | Boots of Swiftness | Doran's Blade | 13579 | 153958 | Lane.bot_lane | Role.carry | |
fd8bfa34-541d-47ba-beaf-013fc79bf5d5 | 0 | Yuán G | Vayne | True | Side.blue | 13 | 5 | 7 | 4.000000 | 380 | Heal | ... | The Bloodthirster | Infinity Edge | Statikk Shiv | Phantom Dancer | Guardian Angel | Berserker's Greaves | 97158 | 233302 | Lane.bot_lane | Role.carry |
1 | Cytosine | Maokai | True | Side.blue | 12 | 6 | 16 | 4.666667 | 247 | Teleport | ... | Righteous Glory | Spirit Visage | Thornmail | Zz'Rot Portal | Ninja Tabi | Sunfire Cape | 175045 | 26142 | Lane.top_lane | Role.solo | |
2 | skcepsxmB | Olaf | True | Side.blue | 11 | 9 | 13 | 2.666667 | 200 | Ghost | ... | Enchantment: Cinderhulk | Guardian Angel | Randuin's Omen | Ionian Boots of Lucidity | Spectre's Cowl | Dead Man's Plate | 34512 | 152288 | Lane.jungle | Role.none | |
3 | Hakuho | Lulu | True | Side.blue | 4 | 6 | 28 | 5.333333 | 22 | Ignite | ... | Eye of the Watchers | Redemption | Needlessly Large Rod | Ionian Boots of Lucidity | Ardent Censer | Blasting Wand | 25459 | 6846 | Lane.bot_lane | Role.support | |
4 | Swifte | Karma | True | Side.blue | 4 | 1 | 23 | 27.000000 | 206 | Flash | ... | Rabadon's Deathcap | Ardent Censer | Void Staff | Athene's Unholy Grail | Crystalline Bracer | Ionian Boots of Lucidity | 147642 | 23745 | Lane.mid_lane | Role.solo | |
5 | Budded | Vel'Koz | False | Side.red | 6 | 7 | 6 | 1.714286 | 284 | Ghost | ... | Morellonomicon | Rabadon's Deathcap | Blasting Wand | Liandry's Torment | Sorcerer's Shoes | Luden's Echo | 172647 | 11723 | Lane.mid_lane | Role.solo | |
6 | Pamelä | Graves | False | Side.red | 10 | 11 | 8 | 1.636364 | 211 | Flash | ... | Berserker's Greaves | Enchantment: Warrior | The Black Cleaver | Phantom Dancer | Mortal Reminder | The Bloodthirster | 8370 | 238854 | Lane.jungle | Role.none | |
7 | AsianSGpøtatø | Caitlyn | False | Side.red | 5 | 8 | 10 | 1.875000 | 370 | Flash | ... | Berserker's Greaves | Mercurial Scimitar | Infinity Edge | Rapid Firecannon | Runaan's Hurricane | Mortal Reminder | 4166 | 333944 | Lane.bot_lane | Role.carry | |
8 | Bwas | Jayce | False | Side.red | 4 | 15 | 7 | 0.733333 | 280 | Flash | ... | Muramana | B. F. Sword | Mercury's Treads | Enchantment: Warrior | The Black Cleaver | Lord Dominik's Regards | 29569 | 181049 | Lane.top_lane | Role.solo | |
9 | Aecinira | Janna | False | Side.red | 2 | 3 | 17 | 6.333333 | 11 | Flash | ... | Talisman of Ascension | Locket of the Iron Solari | Redemption | Ruby Sightstone | Chalice of Harmony | Ionian Boots of Lucidity | 5285 | 4083 | Lane.bot_lane | Role.support |
52288 rows × 25 columns
We'll then write two quick-and-dirty functions (probably better to combine them, but whatever for now), that tell us how many wins a champion saw or how many losses:
def match_result_champion(champion, win=True):
df = bigdf[bigdf['Win'] == win]
df = df[df['Champion'] == champion]
return len(df)
For example:
match_result_champion('Rengar', win=True)
192
Let's get a full list of the available champions from our dataframe:
champions = list(bigdf['Champion'].unique())
len(champions)
133
Now we can get the number of wins for every champion:
champion_won = {champion: match_result_champion(champion, win=True) for champion in champions}
...and the number of losses:
champion_lost = {champion: match_result_champion(champion, win=False) for champion in champions}
So now we can build pandas
Series objects directly from these, for easy statistics work:
num_wins = pd.Series(champion_won)
num_lost = pd.Series(champion_lost)
And perhaps we can see which characters correlate with more wins (or losses).
percent_win = ((num_wins - num_lost)*100.0 / (num_wins + num_lost)).sort_values(ascending=False)
percent_win
Urgot 23.404255 Rumble 17.482517 Xerath 15.476190 Taric 14.285714 LeBlanc 14.247670 Jax 13.114754 Kindred 12.676056 Udyr 9.677419 Hecarim 9.427609 Ivern 9.417040 Mordekaiser 9.090909 Vel'Koz 8.270677 Warwick 8.108108 Twitch 7.915994 Morgana 7.171315 Rammus 6.944444 Kha'Zix 6.666667 Quinn 6.422018 Swain 6.410256 Rek'Sai 6.013363 Anivia 5.952381 Kayle 5.759162 Shaco 5.752212 Brand 5.474453 Riven 5.106778 Kennen 5.099778 Volibear 4.918033 Darius 4.891304 Vi 4.656863 Vayne 4.295943 ... Fiora -6.989247 Diana -7.006369 Fizz -7.122507 Irelia -7.167235 Trundle -7.766990 Wukong -8.000000 Ekko -8.733624 Aatrox -9.090909 Varus -9.565217 Tristana -9.722222 Katarina -10.018215 Nocturne -10.067114 Kalista -10.909091 Singed -10.917031 Dr. Mundo -11.111111 Talon -11.242604 Karthus -12.820513 Leona -13.004484 Lucian -14.767932 Miss Fortune -16.964286 Azir -17.241379 Kled -18.095238 Veigar -18.232044 Shen -19.565217 Sejuani -22.916667 Nunu -25.000000 Garen -25.925926 Cho'Gath -27.659574 Fiddlesticks -29.032258 Heimerdinger -34.883721 dtype: float64
ax = percent_win.plot.bar(figsize=(40, 8))
for item in ax.get_xticklabels():
item.set_rotation(60)
From here we can start to ask why some champions appear advantageous from these match data.