Print

102 – Waffle charts by category

In this tutorial I will show you how to create Waffle charts using Python and Matplotlib. For more matplotlib charts, check out the gallery:

1 dataset 100 matplotlib visualizations

This is what we will create in matplotlib:

43 of 100: Waffle chart in matplotlib

Import the packages

We will need the following packages:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import decimal

Generate the data

We could actually go from numpy to matplotlib, but most data projects use pandas to transform the data, so I am using a pandas dataframe as the starting point.

color_dict = {"Norway": "#2B314D", "Denmark": "#A54836", "Sweden": "#5375D4", }

xy_ticklabel_color, xy_label_color, dot_color ='#101628',"#757C85", "#E2E2E2"

data = {
    "year": [2004, 2022, 2004, 2022, 2004, 2022],
    "countries" : ["Sweden", "Sweden", "Denmark", "Denmark", "Norway", "Norway"],
    "sites": [13,15,4,10,5,8]
}

df= pd.DataFrame(data)
#custom sort
sort_order_dict = {"Denmark":2, "Sweden":3, "Norway":1, 2004:5, 2022:4}
df = df.sort_values(by=['year','countries',], key=lambda x: x.map(sort_order_dict))
#map the colors of a dict to a dataframe
df['color']= df.countries.map(color_dict)
df['sub_total'] = df.groupby('year')['sites'].transform('sum')
df['pct_group'] = 100* df['sites'] / df.sub_total
df['pct_group'] = df['pct_group'].astype(float).round(1)
# use decimal library to round up .5 values to add to 100
df['pct_group'] = df['pct_group'].apply(
  lambda x: decimal.Decimal(x).to_integral_value(rounding=decimal.ROUND_HALF_UP)
)
df
yearcountriessitescolorsub_totalpct_group
52022Norway8#2B314D3324
32022Denmark10#A548363330
12022Sweden15#5375D43346
42004Norway5#2B314D2223
22004Denmark4#A548362218
02004Sweden13#5375D42259

We need to create the basis for the waffle:

#create a 10 by 10 matrix by creating x and y coordinates 
X= np.repeat(np.arange(1,11),10)
Y = np.tile(np.arange(1,11),10)

#create the colors 
dot_colors = []
for color, pct in zip(df.color, df['pct_group'].astype(int)):
    dot_colors.append([color] * pct + [dot_color]* (100-pct))

Add the variables

years = df.year.unique()
countries = df.countries
unique_countries = df.countries.unique()
colors = df.color.unique()

Plot the chart

fig, axes = plt.subplots(nrows= len(years),ncols=len(unique_countries),figsize=(8,6), facecolor = "#FFFFFF")
fig.subplots_adjust(hspace=.5)

for ax, pct, country,  dot_color, color in zip(axes.ravel(), df['pct_group'], countries, dot_colors, df.color):
    ax.scatter(Y, X, s= 30, marker="o", c= dot_color )

    ax.set_title(country, color= xy_label_color,size=10, y = 1.2)
    ax.set_xlabel(f'{pct}%', color= color,  weight = "bold", size=18,)
    ax.xaxis.set_label_position('top') 
    ax.tick_params(axis='both', which='both',length=0)
    ax.set_xticks([])
    ax.set_yticks([])
    ax.spines[['top', 'left', 'right','bottom']].set_visible(False)

The result:

43 of 100: Waffle chart in matplotlib

Was this helpful?

Reader Interactions

Leave a Reply

Your email address will not be published. Required fields are marked *

Table of Contents