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()

Video Game Publishers and Genres with Split Chord

Video Game Titles - Publishers and Genres


Preamble

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

Introduction

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 = pd.read_csv('gummy.csv',index_col=0)
data.head()
data.dtypes
Haribo                 int64
Sweet Smiles           int64
Lolli&Pops             int64
Tooty Fruity           int64
Herbert's Best         int64
HEB                    int64
Lily's Sweet Fruity    int64
Albanese               int64
Organic Tiny           int64
Kroger                 int64
Albanese Tropical      int64
Black Forest           int64
dtype: object

capitalise the name, personality, and species of each villager.

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

data = 13-data

data.sum()
Haribo                 128
Sweet Smiles           162
Lolli&Pops              86
Tooty Fruity           104
Herbert's Best          64
HEB                    174
Lily's Sweet Fruity     59
Albanese               172
Organic Tiny           112
Kroger                 150
Albanese Tropical       98
Black Forest            96
dtype: int64

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.

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

Now for the names of our types.

Which we can now use to create the matrix.

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.

#right = data.columns.to_list()
right =  data.sum(numeric_only=True, axis=0).sort_values(ascending=False).index.to_list()
left = data.index.to_list()
nodes = []

for node in left:
    nodes.append({"name":node, "group":"left"})
    
for node in right:
    nodes.append({"name":node, "group":"right"}) 
    
nodes
[{'name': 'Ben', 'group': 'left'},
 {'name': 'Holly', 'group': 'left'},
 {'name': 'Andy', 'group': 'left'},
 {'name': 'Gretchen', 'group': 'left'},
 {'name': 'Matt', 'group': 'left'},
 {'name': 'Gary', 'group': 'left'},
 {'name': 'Becca', 'group': 'left'},
 {'name': 'Ashley', 'group': 'left'},
 {'name': 'John', 'group': 'left'},
 {'name': 'Gina', 'group': 'left'},
 {'name': 'Aaron', 'group': 'left'},
 {'name': 'Natalie', 'group': 'left'},
 {'name': 'Justin', 'group': 'left'},
 {'name': 'Liv', 'group': 'left'},
 {'name': 'Dave', 'group': 'left'},
 {'name': 'Allyson', 'group': 'left'},
 {'name': 'Ricky', 'group': 'left'},
 {'name': 'Luis', 'group': 'left'},
 {'name': 'HEB ', 'group': 'right'},
 {'name': 'Albanese', 'group': 'right'},
 {'name': 'Sweet Smiles', 'group': 'right'},
 {'name': 'Kroger', 'group': 'right'},
 {'name': 'Haribo', 'group': 'right'},
 {'name': 'Organic Tiny', 'group': 'right'},
 {'name': 'Tooty Fruity', 'group': 'right'},
 {'name': 'Albanese Tropical', 'group': 'right'},
 {'name': 'Black Forest', 'group': 'right'},
 {'name': 'Lolli&Pops', 'group': 'right'},
 {'name': "Herbert's Best", 'group': 'right'},
 {'name': "Lily's Sweet Fruity", 'group': 'right'}]
columns = data.columns.tolist()
links = []

for n, i in data.iterrows():

    for c in columns:
        links.append({"source": n,
                  "target": c,
                 "value": int(i[c])})
    
links
[{'source': 'Ben', 'target': 'Haribo', 'value': 12},
 {'source': 'Ben', 'target': 'Sweet Smiles', 'value': 8},
 {'source': 'Ben', 'target': 'Lolli&Pops', 'value': 2},
 {'source': 'Ben', 'target': 'Tooty Fruity', 'value': 6},
 {'source': 'Ben', 'target': "Herbert's Best", 'value': 1},
 {'source': 'Ben', 'target': 'HEB ', 'value': 9},
 {'source': 'Ben', 'target': "Lily's Sweet Fruity", 'value': 3},
 {'source': 'Ben', 'target': 'Albanese', 'value': 10},
 {'source': 'Ben', 'target': 'Organic Tiny', 'value': 4},
 {'source': 'Ben', 'target': 'Kroger', 'value': 10},
 {'source': 'Ben', 'target': 'Albanese Tropical', 'value': 5},
 {'source': 'Ben', 'target': 'Black Forest', 'value': 7},
 {'source': 'Holly', 'target': 'Haribo', 'value': 7},
 {'source': 'Holly', 'target': 'Sweet Smiles', 'value': 10},
 {'source': 'Holly', 'target': 'Lolli&Pops', 'value': 4},
 {'source': 'Holly', 'target': 'Tooty Fruity', 'value': 6},
 {'source': 'Holly', 'target': "Herbert's Best", 'value': 3},
 {'source': 'Holly', 'target': 'HEB ', 'value': 9},
 {'source': 'Holly', 'target': "Lily's Sweet Fruity", 'value': 1},
 {'source': 'Holly', 'target': 'Albanese', 'value': 12},
 {'source': 'Holly', 'target': 'Organic Tiny', 'value': 11},
 {'source': 'Holly', 'target': 'Kroger', 'value': 8},
 {'source': 'Holly', 'target': 'Albanese Tropical', 'value': 2},
 {'source': 'Holly', 'target': 'Black Forest', 'value': 5},
 {'source': 'Andy', 'target': 'Haribo', 'value': 12},
 {'source': 'Andy', 'target': 'Sweet Smiles', 'value': 10},
 {'source': 'Andy', 'target': 'Lolli&Pops', 'value': 1},
 {'source': 'Andy', 'target': 'Tooty Fruity', 'value': 4},
 {'source': 'Andy', 'target': "Herbert's Best", 'value': 5},
 {'source': 'Andy', 'target': 'HEB ', 'value': 6},
 {'source': 'Andy', 'target': "Lily's Sweet Fruity", 'value': 2},
 {'source': 'Andy', 'target': 'Albanese', 'value': 11},
 {'source': 'Andy', 'target': 'Organic Tiny', 'value': 3},
 {'source': 'Andy', 'target': 'Kroger', 'value': 7},
 {'source': 'Andy', 'target': 'Albanese Tropical', 'value': 9},
 {'source': 'Andy', 'target': 'Black Forest', 'value': 8},
 {'source': 'Gretchen', 'target': 'Haribo', 'value': 2},
 {'source': 'Gretchen', 'target': 'Sweet Smiles', 'value': 10},
 {'source': 'Gretchen', 'target': 'Lolli&Pops', 'value': 6},
 {'source': 'Gretchen', 'target': 'Tooty Fruity', 'value': 4},
 {'source': 'Gretchen', 'target': "Herbert's Best", 'value': 3},
 {'source': 'Gretchen', 'target': 'HEB ', 'value': 12},
 {'source': 'Gretchen', 'target': "Lily's Sweet Fruity", 'value': 1},
 {'source': 'Gretchen', 'target': 'Albanese', 'value': 11},
 {'source': 'Gretchen', 'target': 'Organic Tiny', 'value': 9},
 {'source': 'Gretchen', 'target': 'Kroger', 'value': 7},
 {'source': 'Gretchen', 'target': 'Albanese Tropical', 'value': 5},
 {'source': 'Gretchen', 'target': 'Black Forest', 'value': 8},
 {'source': 'Matt', 'target': 'Haribo', 'value': 12},
 {'source': 'Matt', 'target': 'Sweet Smiles', 'value': 8},
 {'source': 'Matt', 'target': 'Lolli&Pops', 'value': 2},
 {'source': 'Matt', 'target': 'Tooty Fruity', 'value': 6},
 {'source': 'Matt', 'target': "Herbert's Best", 'value': 1},
 {'source': 'Matt', 'target': 'HEB ', 'value': 9},
 {'source': 'Matt', 'target': "Lily's Sweet Fruity", 'value': 3},
 {'source': 'Matt', 'target': 'Albanese', 'value': 10},
 {'source': 'Matt', 'target': 'Organic Tiny', 'value': 4},
 {'source': 'Matt', 'target': 'Kroger', 'value': 10},
 {'source': 'Matt', 'target': 'Albanese Tropical', 'value': 5},
 {'source': 'Matt', 'target': 'Black Forest', 'value': 7},
 {'source': 'Gary', 'target': 'Haribo', 'value': 12},
 {'source': 'Gary', 'target': 'Sweet Smiles', 'value': 11},
 {'source': 'Gary', 'target': 'Lolli&Pops', 'value': 7},
 {'source': 'Gary', 'target': 'Tooty Fruity', 'value': 8},
 {'source': 'Gary', 'target': "Herbert's Best", 'value': 4},
 {'source': 'Gary', 'target': 'HEB ', 'value': 10},
 {'source': 'Gary', 'target': "Lily's Sweet Fruity", 'value': 1},
 {'source': 'Gary', 'target': 'Albanese', 'value': 5},
 {'source': 'Gary', 'target': 'Organic Tiny', 'value': 9},
 {'source': 'Gary', 'target': 'Kroger', 'value': 6},
 {'source': 'Gary', 'target': 'Albanese Tropical', 'value': 3},
 {'source': 'Gary', 'target': 'Black Forest', 'value': 2},
 {'source': 'Becca', 'target': 'Haribo', 'value': 3},
 {'source': 'Becca', 'target': 'Sweet Smiles', 'value': 10},
 {'source': 'Becca', 'target': 'Lolli&Pops', 'value': 6},
 {'source': 'Becca', 'target': 'Tooty Fruity', 'value': 4},
 {'source': 'Becca', 'target': "Herbert's Best", 'value': 2},
 {'source': 'Becca', 'target': 'HEB ', 'value': 11},
 {'source': 'Becca', 'target': "Lily's Sweet Fruity", 'value': 1},
 {'source': 'Becca', 'target': 'Albanese', 'value': 12},
 {'source': 'Becca', 'target': 'Organic Tiny', 'value': 8},
 {'source': 'Becca', 'target': 'Kroger', 'value': 5},
 {'source': 'Becca', 'target': 'Albanese Tropical', 'value': 9},
 {'source': 'Becca', 'target': 'Black Forest', 'value': 7},
 {'source': 'Ashley', 'target': 'Haribo', 'value': 12},
 {'source': 'Ashley', 'target': 'Sweet Smiles', 'value': 2},
 {'source': 'Ashley', 'target': 'Lolli&Pops', 'value': 4},
 {'source': 'Ashley', 'target': 'Tooty Fruity', 'value': 10},
 {'source': 'Ashley', 'target': "Herbert's Best", 'value': 1},
 {'source': 'Ashley', 'target': 'HEB ', 'value': 5},
 {'source': 'Ashley', 'target': "Lily's Sweet Fruity", 'value': 8},
 {'source': 'Ashley', 'target': 'Albanese', 'value': 11},
 {'source': 'Ashley', 'target': 'Organic Tiny', 'value': 9},
 {'source': 'Ashley', 'target': 'Kroger', 'value': 5},
 {'source': 'Ashley', 'target': 'Albanese Tropical', 'value': 3},
 {'source': 'Ashley', 'target': 'Black Forest', 'value': 6},
 {'source': 'John', 'target': 'Haribo', 'value': 2},
 {'source': 'John', 'target': 'Sweet Smiles', 'value': 11},
 {'source': 'John', 'target': 'Lolli&Pops', 'value': 6},
 {'source': 'John', 'target': 'Tooty Fruity', 'value': 9},
 {'source': 'John', 'target': "Herbert's Best", 'value': 1},
 {'source': 'John', 'target': 'HEB ', 'value': 12},
 {'source': 'John', 'target': "Lily's Sweet Fruity", 'value': 5},
 {'source': 'John', 'target': 'Albanese', 'value': 8},
 {'source': 'John', 'target': 'Organic Tiny', 'value': 3},
 {'source': 'John', 'target': 'Kroger', 'value': 10},
 {'source': 'John', 'target': 'Albanese Tropical', 'value': 7},
 {'source': 'John', 'target': 'Black Forest', 'value': 4},
 {'source': 'Gina', 'target': 'Haribo', 'value': 3},
 {'source': 'Gina', 'target': 'Sweet Smiles', 'value': 12},
 {'source': 'Gina', 'target': 'Lolli&Pops', 'value': 5},
 {'source': 'Gina', 'target': 'Tooty Fruity', 'value': 4},
 {'source': 'Gina', 'target': "Herbert's Best", 'value': 2},
 {'source': 'Gina', 'target': 'HEB ', 'value': 11},
 {'source': 'Gina', 'target': "Lily's Sweet Fruity", 'value': 1},
 {'source': 'Gina', 'target': 'Albanese', 'value': 7},
 {'source': 'Gina', 'target': 'Organic Tiny', 'value': 10},
 {'source': 'Gina', 'target': 'Kroger', 'value': 6},
 {'source': 'Gina', 'target': 'Albanese Tropical', 'value': 9},
 {'source': 'Gina', 'target': 'Black Forest', 'value': 8},
 {'source': 'Aaron', 'target': 'Haribo', 'value': 11},
 {'source': 'Aaron', 'target': 'Sweet Smiles', 'value': 9},
 {'source': 'Aaron', 'target': 'Lolli&Pops', 'value': 7},
 {'source': 'Aaron', 'target': 'Tooty Fruity', 'value': 10},
 {'source': 'Aaron', 'target': "Herbert's Best", 'value': 4},
 {'source': 'Aaron', 'target': 'HEB ', 'value': 8},
 {'source': 'Aaron', 'target': "Lily's Sweet Fruity", 'value': 3},
 {'source': 'Aaron', 'target': 'Albanese', 'value': 10},
 {'source': 'Aaron', 'target': 'Organic Tiny', 'value': 1},
 {'source': 'Aaron', 'target': 'Kroger', 'value': 12},
 {'source': 'Aaron', 'target': 'Albanese Tropical', 'value': 5},
 {'source': 'Aaron', 'target': 'Black Forest', 'value': 2},
 {'source': 'Natalie', 'target': 'Haribo', 'value': 2},
 {'source': 'Natalie', 'target': 'Sweet Smiles', 'value': 12},
 {'source': 'Natalie', 'target': 'Lolli&Pops', 'value': 8},
 {'source': 'Natalie', 'target': 'Tooty Fruity', 'value': 5},
 {'source': 'Natalie', 'target': "Herbert's Best", 'value': 6},
 {'source': 'Natalie', 'target': 'HEB ', 'value': 9},
 {'source': 'Natalie', 'target': "Lily's Sweet Fruity", 'value': 7},
 {'source': 'Natalie', 'target': 'Albanese', 'value': 10},
 {'source': 'Natalie', 'target': 'Organic Tiny', 'value': 4},
 {'source': 'Natalie', 'target': 'Kroger', 'value': 11},
 {'source': 'Natalie', 'target': 'Albanese Tropical', 'value': 1},
 {'source': 'Natalie', 'target': 'Black Forest', 'value': 3},
 {'source': 'Justin', 'target': 'Haribo', 'value': 1},
 {'source': 'Justin', 'target': 'Sweet Smiles', 'value': 8},
 {'source': 'Justin', 'target': 'Lolli&Pops', 'value': 5},
 {'source': 'Justin', 'target': 'Tooty Fruity', 'value': 4},
 {'source': 'Justin', 'target': "Herbert's Best", 'value': 10},
 {'source': 'Justin', 'target': 'HEB ', 'value': 12},
 {'source': 'Justin', 'target': "Lily's Sweet Fruity", 'value': 2},
 {'source': 'Justin', 'target': 'Albanese', 'value': 11},
 {'source': 'Justin', 'target': 'Organic Tiny', 'value': 3},
 {'source': 'Justin', 'target': 'Kroger', 'value': 9},
 {'source': 'Justin', 'target': 'Albanese Tropical', 'value': 7},
 {'source': 'Justin', 'target': 'Black Forest', 'value': 6},
 {'source': 'Liv', 'target': 'Haribo', 'value': 12},
 {'source': 'Liv', 'target': 'Sweet Smiles', 'value': 10},
 {'source': 'Liv', 'target': 'Lolli&Pops', 'value': 6},
 {'source': 'Liv', 'target': 'Tooty Fruity', 'value': 7},
 {'source': 'Liv', 'target': "Herbert's Best", 'value': 5},
 {'source': 'Liv', 'target': 'HEB ', 'value': 8},
 {'source': 'Liv', 'target': "Lily's Sweet Fruity", 'value': 3},
 {'source': 'Liv', 'target': 'Albanese', 'value': 9},
 {'source': 'Liv', 'target': 'Organic Tiny', 'value': 1},
 {'source': 'Liv', 'target': 'Kroger', 'value': 11},
 {'source': 'Liv', 'target': 'Albanese Tropical', 'value': 2},
 {'source': 'Liv', 'target': 'Black Forest', 'value': 4},
 {'source': 'Dave', 'target': 'Haribo', 'value': 10},
 {'source': 'Dave', 'target': 'Sweet Smiles', 'value': 7},
 {'source': 'Dave', 'target': 'Lolli&Pops', 'value': 8},
 {'source': 'Dave', 'target': 'Tooty Fruity', 'value': 1},
 {'source': 'Dave', 'target': "Herbert's Best", 'value': 4},
 {'source': 'Dave', 'target': 'HEB ', 'value': 12},
 {'source': 'Dave', 'target': "Lily's Sweet Fruity", 'value': 2},
 {'source': 'Dave', 'target': 'Albanese', 'value': 9},
 {'source': 'Dave', 'target': 'Organic Tiny', 'value': 7},
 {'source': 'Dave', 'target': 'Kroger', 'value': 11},
 {'source': 'Dave', 'target': 'Albanese Tropical', 'value': 5},
 {'source': 'Dave', 'target': 'Black Forest', 'value': 3},
 {'source': 'Allyson', 'target': 'Haribo', 'value': 8},
 {'source': 'Allyson', 'target': 'Sweet Smiles', 'value': 11},
 {'source': 'Allyson', 'target': 'Lolli&Pops', 'value': 3},
 {'source': 'Allyson', 'target': 'Tooty Fruity', 'value': 2},
 {'source': 'Allyson', 'target': "Herbert's Best", 'value': 5},
 {'source': 'Allyson', 'target': 'HEB ', 'value': 12},
 {'source': 'Allyson', 'target': "Lily's Sweet Fruity", 'value': 4},
 {'source': 'Allyson', 'target': 'Albanese', 'value': 10},
 {'source': 'Allyson', 'target': 'Organic Tiny', 'value': 9},
 {'source': 'Allyson', 'target': 'Kroger', 'value': 1},
 {'source': 'Allyson', 'target': 'Albanese Tropical', 'value': 6},
 {'source': 'Allyson', 'target': 'Black Forest', 'value': 7},
 {'source': 'Ricky', 'target': 'Haribo', 'value': 6},
 {'source': 'Ricky', 'target': 'Sweet Smiles', 'value': 9},
 {'source': 'Ricky', 'target': 'Lolli&Pops', 'value': 3},
 {'source': 'Ricky', 'target': 'Tooty Fruity', 'value': 2},
 {'source': 'Ricky', 'target': "Herbert's Best", 'value': 5},
 {'source': 'Ricky', 'target': 'HEB ', 'value': 12},
 {'source': 'Ricky', 'target': "Lily's Sweet Fruity", 'value': 7},
 {'source': 'Ricky', 'target': 'Albanese', 'value': 10},
 {'source': 'Ricky', 'target': 'Organic Tiny', 'value': 8},
 {'source': 'Ricky', 'target': 'Kroger', 'value': 11},
 {'source': 'Ricky', 'target': 'Albanese Tropical', 'value': 4},
 {'source': 'Ricky', 'target': 'Black Forest', 'value': 1},
 {'source': 'Luis', 'target': 'Haribo', 'value': 1},
 {'source': 'Luis', 'target': 'Sweet Smiles', 'value': 4},
 {'source': 'Luis', 'target': 'Lolli&Pops', 'value': 3},
 {'source': 'Luis', 'target': 'Tooty Fruity', 'value': 12},
 {'source': 'Luis', 'target': "Herbert's Best", 'value': 2},
 {'source': 'Luis', 'target': 'HEB ', 'value': 7},
 {'source': 'Luis', 'target': "Lily's Sweet Fruity", 'value': 5},
 {'source': 'Luis', 'target': 'Albanese', 'value': 6},
 {'source': 'Luis', 'target': 'Organic Tiny', 'value': 9},
 {'source': 'Luis', 'target': 'Kroger', 'value': 10},
 {'source': 'Luis', 'target': 'Albanese Tropical', 'value': 11},
 {'source': 'Luis', 'target': 'Black Forest', 'value': 8}]
nodes
[{'name': 'Ben', 'group': 'left'},
 {'name': 'Holly', 'group': 'left'},
 {'name': 'Andy', 'group': 'left'},
 {'name': 'Gretchen', 'group': 'left'},
 {'name': 'Matt', 'group': 'left'},
 {'name': 'Gary', 'group': 'left'},
 {'name': 'Becca', 'group': 'left'},
 {'name': 'Ashley', 'group': 'left'},
 {'name': 'John', 'group': 'left'},
 {'name': 'Gina', 'group': 'left'},
 {'name': 'Aaron', 'group': 'left'},
 {'name': 'Natalie', 'group': 'left'},
 {'name': 'Justin', 'group': 'left'},
 {'name': 'Liv', 'group': 'left'},
 {'name': 'Dave', 'group': 'left'},
 {'name': 'Allyson', 'group': 'left'},
 {'name': 'Ricky', 'group': 'left'},
 {'name': 'Luis', 'group': 'left'},
 {'name': 'HEB ', 'group': 'right'},
 {'name': 'Albanese', 'group': 'right'},
 {'name': 'Sweet Smiles', 'group': 'right'},
 {'name': 'Kroger', 'group': 'right'},
 {'name': 'Haribo', 'group': 'right'},
 {'name': 'Organic Tiny', 'group': 'right'},
 {'name': 'Tooty Fruity', 'group': 'right'},
 {'name': 'Albanese Tropical', 'group': 'right'},
 {'name': 'Black Forest', 'group': 'right'},
 {'name': 'Lolli&Pops', 'group': 'right'},
 {'name': "Herbert's Best", 'group': 'right'},
 {'name': "Lily's Sweet Fruity", 'group': 'right'}]
SplitChord(links, nodes, margin=180, width=1000, noun="out of 12", verb="as", conjunction="was scored by").show()
PlotAPI - Chord Diagram
import json

data = {"links": links,
        "nodes": nodes}

with open("gummy.json", "w") as fp:
    json.dump(data, fp)