Engaging plots, made easy.

Easily turn your data into engaging visualizations. Powerful API for coders. Powerful app for everyone.

main.py
notebook.ipynb
main.rs
from plotapi import Chord

Chord(matrix, names).show()

Visualizations Showcase

League of Legends World Championship 2019

League of Legends World Championship 2019


Preamble

import numpy as np                   # for multi-dimensional containers 
import pandas as pd                  # for DataFrames
import itertools
from plotapi import Chord

Introduction

In previous sections, we visualised co-occurrences of Pokémon type. Whilst it was interesting to look at, the dataset only contained Pokémon from the first six geerations. In this section, we're going to use the Pokemon with stats Generation 8 dataset to visualise the co-occurrence of Pokémon types from generations one to eight.

The Dataset

The dataset documentation states that we can expect 13 variables per each of the 1017 Pokémon of the first eight generations.

Let's download the mirrored dataset and have a look for ourselves.

data_url = 'https://datacrayon.com/datasets/lol/wc_matches.csv'
data = pd.read_csv(data_url)
data['date'] =  pd.to_datetime(data['date'])
data.head()
Unnamed: 0 team1 team2 winner date pbp_caster color_caster mvp blue red
0 0 Fnatic SK Telecom T1 SK Telecom T1 2019-10-12 12:00:00 Atlus Froskurinn, Kobe Faker Fnatic SK Telecom T1
1 1 Royal Never Give Up Clutch Gaming Royal Never Give Up 2019-10-12 13:00:00 Atlus Froskurinn, Kobe Langx Royal Never Give Up Clutch Gaming
2 2 Invictus Gaming ahq eSports Club Invictus Gaming 2019-10-12 14:00:00 Atlus Froskurinn, Kobe Rookie Invictus Gaming ahq eSports Club
3 3 DAMWON Gaming Team Liquid Team Liquid 2019-10-12 15:00:00 Phreak Azael, Spawn Impact DAMWON Gaming Team Liquid
4 4 J Team FunPlus Phoenix J Team 2019-10-12 16:00:00 Phreak Azael, Spawn FoFo J Team FunPlus Phoenix

It looks good so far, but let's confirm the 13 variables against 1017 samples from the documentation.

data.shape
(81, 10)

Perfect, that's exactly what we were expecting.

Data Wrangling

We need to do a bit of data wrangling before we can visualise our data. We can see from the columns names that the Pokémon types are split between the columns Type 1 and Type 2.

pd.DataFrame(data.columns.values.tolist())
0
0 Unnamed: 0
1 team1
2 team2
3 winner
4 date
5 pbp_caster
6 color_caster
7 mvp
8 blue
9 red

So let's select just these two columns and work with a list containing only them as we move forward.

types = pd.DataFrame(data[['team1', 'team2']].values)
types
0 1
0 Fnatic SK Telecom T1
1 Royal Never Give Up Clutch Gaming
2 Invictus Gaming ahq eSports Club
3 DAMWON Gaming Team Liquid
4 J Team FunPlus Phoenix
... ... ...
76 Flamengo eSports Royal Youth
77 DAMWON Gaming Lowkey Esports
78 Clutch Gaming Royal Youth
79 Hong Kong Attitude Isurus
80 Splyce Unicorns Of Love

81 rows × 2 columns

Without further investigation, we can see that we have at least a few NaN values in the table above. We are only interested in co-occurrence of types, so we can remove all samples which contain a NaN value.

types = types.dropna()

We can also see an instance where the type Fighting at index 1014 is followed by \n. We'll strip all these out before continuing.

types = types.replace('\n','', regex=True)
types
0 1
0 Fnatic SK Telecom T1
1 Royal Never Give Up Clutch Gaming
2 Invictus Gaming ahq eSports Club
3 DAMWON Gaming Team Liquid
4 J Team FunPlus Phoenix
... ... ...
76 Flamengo eSports Royal Youth
77 DAMWON Gaming Lowkey Esports
78 Clutch Gaming Royal Youth
79 Hong Kong Attitude Isurus
80 Splyce Unicorns Of Love

81 rows × 2 columns

Our chord diagram will need two inputs: the co-occurrence matrix, and a list of names to label the segments.

First we'll populate our list of type names by looking for the unique ones.

names = np.unique(types).tolist()
pd.DataFrame(names)
0
0 Cloud9
1 Clutch Gaming
2 DAMWON Gaming
3 DetonatioN FocusMe
4 Flamengo eSports
5 Fnatic
6 FunPlus Phoenix
7 G2 Esports
8 GAM Esports
9 Griffin
10 Hong Kong Attitude
11 Invictus Gaming
12 Isurus
13 J Team
14 Lowkey Esports
15 MAMMOTH
16 MEGA
17 Royal Never Give Up
18 Royal Youth
19 SK Telecom T1
20 Splyce
21 Team Liquid
22 Unicorns Of Love
23 ahq eSports Club

Now we can create our empty co-occurrence matrix using these type names for the row and column indeces.


We can populate a co-occurrence matrix with the following approach. We'll start by creating a list with every type pairing in its original and reversed form.

Which we can now use to create the matrix.

matrix = pd.DataFrame(0, index=names, columns=names)
details = pd.DataFrame([], index=names, columns=names).astype('object')
for index, x in details.iterrows():
    for k in details.columns.values:
        x[k] = []

for index, x in data.iterrows():
    if(x['winner'] == x['team1']):
        matrix.at[x['team1'], x['team2']] += 1
        temp = details.at[x['team1'], x['team2']]
        if(x['team1'] == x['blue']):
            color = '#00bbf9'
        else:
            color = '#fe5f55' 
        temp.append(f"{x['date'].strftime('%b-%d %H:%M')} <span style='color:{color}!important; font-style: normal;'>⬤</span> {x['team1']} 🥇 {x['mvp']}")
        details.at[x['team1'], x['team2']] = temp
    else:
        matrix.at[x['team2'], x['team1']] += 1
        temp = details.at[x['team2'], x['team1']]
        if(x['team2'] == x['blue']):
            color = '#00bbf9'
        else:
            color = '#fe5f55' 
        temp.append(f"{x['date'].strftime('%b-%d %H:%M')} <span style='color:{color}!important; font-style: normal;'>⬤</span> {x['team2']} 🥇 {x['mvp']}")
    
matrix = matrix.values.tolist()
details = pd.DataFrame(details)

We can list DataFrame for better presentation.

x['blue']
'Unicorns Of Love'

Chord Diagram

Time to visualise the co-occurrence of types using a chord diagram. We are going to use a list of custom colours that represent the types.

colors = ["#A6B91A", "#705746", "#6F35FC", "#F7D02C", "#D685AD",
          "#C22E28", "#EE8130", "#A98FF3", "#735797", "#7AC74C",
          "#E2BF65", "#96D9D6", "#A8A77A", "#A33EA1", "#F95587",
          "#B6A136", "#B7B7CE", "#6390F0"];

Finally, we can put it all together.

Chord(
    matrix,
    names,
    symmetric=False,
    popup_width=700,
    verb="battled together in",
    noun="matches",
    details=details.values.tolist(),
    details_separator="<br>",
    colors=colors,
    margin=75,
    font_size_large="12px",
    font_size="12px",
    credit=True,
    padding=0.04,
).show()
PlotAPI - Chord Diagram
import json

data = {"matrix": matrix,
        "names": names,
        "colors": colors,
        "details": details.values.tolist(),
       }

with open("lol-championship.json", "w") as fp:
    json.dump(data, fp)
    
    
    
Previous
Showcase