import IPython.core.display as di
# This line will hide code by default when the notebook is exported as HTML
di.display_html('<script>jQuery(function() {if (jQuery("body.notebook_app").length == 0) { jQuery(".input_area").toggle(); jQuery(".prompt").toggle();}});</script>', raw=True)
# This line will add a button to toggle visibility of code blocks, for use with the HTML export version
di.display_html('''<button onclick="jQuery('.input_area').toggle(); jQuery('.prompt').toggle();">Toggle code</button>''', raw=True)
db_file = 'sqlite:///1512249759.sqlite'
%load_ext sql
%sql sqlite:///1512170736.sqlite
None
%%javascript
IPython.OutputArea.auto_scroll_threshold = 9999;
from IPython.core.display import HTML
import pandas
def query(sql, params=None, check=True):
"Run a query and return it as a Pandas dataframe"
df = pandas.read_sql(sql, con=db_file, params=params)
if check and 'Percent' in df:
assert percent_ok(df)
return df
def percent_ok(df):
"Sanity check that percentages sum to 100"
return abs(df['Percent'].sum() - 100.0) < 1
# Style for tables
def mystyle(df):
return (df.style
.format({'Percent': '{:.2f}%'})
.background_gradient(cmap=cmap, subset=['Percent',])
.applymap(lambda x: 'font-weight: bold' if x in keystones else ''))
# Construct a palette that's sorta like coolwarm but not as dark, so black text shows up
import seaborn as sns
cmap = sns.diverging_palette(240, 20, s=50, l=70, as_cmap=True)
# Normalization constant for all picks; handy for later
df = query("select v from totals where k='All'")
count_all = df.iloc[0]['v']
# Construct some lists of constants, useful for display
df = query("select rune from types where slot = 'Keystone'")
keystones = tuple(df['rune'])
df = query("select distinct(path) as p from types")
paths = tuple(df['p'])
all_roles = ('Top', 'Jungle', 'Middle', 'ADC', 'Support')
A study of ~12 million Platinum+ ranked games in the week of Nov 25 - Dec 2 2017. I took a look at how often every rune was picked and then broke down the data by role and by rune path.
In general there's significant diversity in the 2018 rune system. There's also some runes that are clearly much more favored than others.
Keystones
Lesser runes
Raw data comes from Lolalytics. Crunched and processed by Nelson Minar <nelson@monkey.org>. I'm looking for someone to work with to turn this into a nicely formatted article / infographic or else a regular feature on a site. Email me if you are interested.
df = query(f'''
select
types.path as Path,
100*sum(p+s)/? as Percent
from picks left join types on picks.rune = types.rune
group by types.path
order by Percent desc;
''', params=(count_all,))
display(mystyle(df))
q = '''
select
types.path as Path,
sum(p) as P,
sum(s) as S,
sum(p+s) as Total
from
picks
left join types on picks.rune = types.rune
where picks.role = ?
group by types.path
order by types.path
'''
def get_totals_for_role(role):
df = query(q, (role,))
df["%s P" % role] = df['P'] * 100.0 / df['P'].sum()
df["%s S" % role] = df['S'] * 100.0 / df['S'].sum()
df["%s B" % role] = df['Total'] * 100.0 / df['Total'].sum()
del df['Total'], df['P'], df['S']
return df
df = get_totals_for_role(all_roles[0])
for role in all_roles[1:]:
df2 = get_totals_for_role(role)
df["%s B" % role] = df2["%s B" % role]
df["%s P" % role] = df2["%s P" % role]
df["%s S" % role] = df2["%s S" % role]
cols = ["%s B" % r for r in all_roles]
display(HTML('<h3>Primary and Secondary Paths</h3>'))
display(df[['Path',] + cols].fillna(0).style
.format({k: '{:.2f}%' for k in cols})
.background_gradient(cmap=cmap, subset=cols)
)
cols = ["%s P" % r for r in all_roles]
display(HTML('<h3>Primary Path</h3>'))
display(df[['Path',] + cols].fillna(0).style
.format({k: '{:.2f}%' for k in cols})
.background_gradient(cmap=cmap, subset=cols)
)
cols = ["%s S" % r for r in all_roles]
display(HTML('<h3>Secondary Path</h3>'))
display(df[['Path',] + cols].fillna(0).style
.format({k: '{:.2f}%' for k in cols})
.background_gradient(cmap=cmap, subset=cols)
)
df = query('''
select
types.path as Path,
picks.rune as Rune,
100*6*sum(p) / totals.v as Percent
from
picks left join types on picks.rune = types.rune
left join totals on totals.k = 'All'
where types.slot == 'Keystone'
group by picks.rune
order by Percent desc;
''')
display(mystyle(df))
df = query('''
select
types.path as Path,
picks.rune as Rune,
100*6*sum(p+s) / (5*totals.v) as Percent
from
picks left join types on picks.rune = types.rune
left join totals on totals.k = 'All'
where types.slot != 'Keystone'
group by picks.rune
order by Percent desc;
''')
display(mystyle(df))
rune_query = '''
select
champion as Champion,
sum(p+s) as Total
from picks
where rune = ?
group by champion
order by Total desc
'''
df = query(rune_query, ('Minion Dematerializer',))
df['Percent'] = 100 * df['Total'] / df['Total'].sum()
del df['Total']
display(mystyle(df))
This rune is very popular but only useful on champions with certain mobility abilities.
rune_query = '''
select
champion as Champion,
sum(p+s) as Total
from picks
where rune = ?
group by champion
order by Total desc
limit 10
'''
df = query(rune_query, ('Sudden Impact',))
df['Percent'] = 100 * df['Total'] / df['Total'].sum()
del df['Total']
display(mystyle(df))
for path in paths:
display(HTML(f'<h3>{path}</h3>'))
df = query('''
select
picks.rune as Rune,
100*sum(p+s) / totals.v as Percent
from
picks left join types on picks.rune = types.rune
left join totals on totals.k = types.path
where types.path = ?
group by picks.rune
order by Path, Percent desc;
''', (path,))
display(mystyle(df))
all_roles = ('Top', 'Jungle', 'Middle', 'ADC', 'Support')
q = '''
select
picks.rune as Rune,
types.path as Path,
100*sum(p+s) / totals.v as Percent
from
picks left join types on picks.rune = types.rune
left join totals on totals.k = picks.role
where picks.role = ?
group by picks.rune
order by Path, types.slot desc, Rune;
'''
role = all_roles[0]
df = query(q, (role,))
df[role] = df['Percent']
del df['Percent']
for role in all_roles[1:]:
df2 = query(q, (role,))
df[role] = df2['Percent']
display(df.fillna(0).style
.format({k: '{:.2f}%' for k in all_roles})
.background_gradient(cmap=cmap, subset=list(all_roles))
.applymap(lambda x: 'font-weight: bold' if x in keystones else '')
)
all_roles = ('Top', 'Jungle', 'Middle', 'ADC', 'Support')
q = '''
select
picks.rune as Rune,
sum(p+s) as Total
from
picks
left join types on picks.rune = types.rune
where picks.role = ? and types.path = ?
group by picks.rune
order by types.slot desc, Rune;
'''
def get_totals_for_path_and_role(path, role):
df = query(q, (role, path))
all_total = df['Total'].sum()
df[role] = df['Total'] * 100.0 / all_total
del df['Total']
return df
for path in paths:
df = get_totals_for_path_and_role(path, all_roles[0])
for role in all_roles[1:]:
df2 = get_totals_for_path_and_role(path, role)
df[role] = df2[role]
display(HTML(f'<h3>{path}</h3>'))
display(df.fillna(0).style
.format({k: '{:.2f}%' for k in all_roles})
.background_gradient(cmap=cmap, subset=list(all_roles))
.applymap(lambda x: 'font-weight: bold' if x in keystones else '')
)