How to Create Beautiful Waffle Charts for Data Visualisation in Python
Waffle Charts are a great way to visualise categorical data, are aesthetically pleasing and easy for readers to understand – which is one of the key goals of effective data visualisations. They also provide a nicer looking alternative to pie charts.
Waffle charts are square or rectangular displays made up of smaller squares in a grid pattern. Most commonly, it is a 10 x 10 grid, but they can be any dimension you want them to be, and this will depend on the data you are looking to display. Each square within the grid is coloured based on a category and represents a portion of the whole. From these plots, we can see contributions of individual categories or display progress towards a goal.

Waffle charts have several uses, including visualising progress towards a goal, understanding how individual parts making up a whole, and even seeing how much expenses are eating into gross profit.
Advantages and Disadvantages of Waffle Charts
As with any data visualisation tool, there are advantages and disadvantages to displaying data in specific formats.
Waffle charts have several advantages for displaying data, including:
- A great alternative to the pie chart
- Interesting and aesthetically pleasing to look at
- Easy to read and interpret
- Data is not distorted
- Great way to visualise a small number of categories
- Easy to visualise progress towards a goal
Even with these advantages, there are a few disadvantages to using waffle charts:
- It can be hard to read if you have a large number of categories to display
- It can be hard to compare non-adjacent categories
- Can't include text or numbers within the squares due to their size
- Accuracy can be impacted if the values are smaller than the square's representative value. eg. if a square equals one unit, and you have a half unit
Within the article, we will use geological lithology data to see how we can use Python to display Waffle charts.
Building Waffle Charts Using Python and PyWaffle
Importing Libraries and Creating Data
It is possible to create waffle charts using matplotlib; however, there is a very convenient library called pywaffle which makes this process much easier.
First, we have to install the pywaffle library by using the following command within the terminal:
pip install pywaffle
Then, within our code, we need to import a few more libraries: pandas and matplotlib.
from pywaffle import Waffle
import pandas as pd
import matplotlib.pyplot as plt
For this example, we will be using some basic lithology data. This represents the different percentages of rock types within two different wells.
Rather than loading data, we can quickly create our pandas dataframe from a dictionary.
lith_dict = {'LITH': ['Shale', 'Sandstone',
'Sandstone/Shale', 'Chalk',
'Limestone', 'Marl', 'Tuff'],
'Well1': [61,15, 10, 5,
5, 3, 1],
'Well2': [35 ,21, 16, 12,
7, 5, 4]}
lith_data_df = pd.DataFrame.from_dict(lith_dict)
When we view the dataframe, this is what we are presented with:

And finally, we will create some basic colours that will make the waffle chart visually appealing and make it easy for us to compare across the different wells.
colours = ['#8dd3c7', '#deb887', '#bebada', '#fb8072',
'#80b1d3', '#fdb462', '#b3de69']
Creating a Simple Waffle Chart with PyWaffle
Including values and labels inside a waffle chart can make it very messy. So, for this example, we will be storing that information within a legend.
To do this, we will create a new variable called plot_labels
and using a list comprehension; we will join together the values with the label.
plot_labels = [f'{i} ({str(j)} %)' for i,j in zip(lith_data_df.LITH,
lith_data_df.Well1)]
Next, we can move on to creating the waffle chart.
We do this by calling upon plt.figure
and passing in Waffle
to the FigureClass
parameter. This is followed by several additional parameters, including the data we are going to display, the colours and the labels.
Finally, we can setup the legend directly within the call to plt.figure()
.
plt.figure(FigureClass=Waffle, figsize=(10,10), rows=5, columns = 20,
values=list(lith_data_df['Well1']),
colors=colours,
labels=plot_labels,
legend={'loc':'lower center', 'bbox_to_anchor': (0.5, -0.8),
'ncol':3, 'fontsize':12})
plt.show()
When we run the above code, we will get back the following waffle chart.

We can see right away the contribution of each lithology to the overall composition of the well.
When the squares are filled in, the process starts from the bottom left corner. If we want to change the starting position, we can do so by adding a starting_location
parameter to the plt.figure()
method.
plt.figure(FigureClass=Waffle, figsize=(10,10), rows=5, columns = 20,
values=list(lith_data_df['Well1']),
colors=colours,
labels=plot_labels,
legend={'loc':'lower center', 'bbox_to_anchor': (0.5, -0.8),
'ncol':3, 'fontsize':12},
starting_location='NW')
plt.show()
When we run this, we will see that the waffle chart has flipped along the horizontal axis.

Comparing Different Waffle Charts with Python
Now that we have the first well, we can run the same code on the second well. We do this be changing the values to use Well2 instead of Well1.
fig = plt.figure(FigureClass=Waffle, figsize=(10,10), rows=5, columns = 20,
values=list(lith_data_df['Well2']),
colors=colours,
labels=plot_labels,
legend={'loc':'lower center', 'bbox_to_anchor': (0.5, -0.8),
'ncol':3, 'fontsize':12})
plt.show()

This looks great, and we could easily create individual plots per well and stitch them together in PowerPoint. However, there is a way to do it through PyWaffle, and that is by passing in a dictionary containing the subplot position and contents.
fig = plt.figure(FigureClass=Waffle,
plots = {211: {'values':list(lith_data_df['Well1']),
'labels': [f'{i} ({str(j)} %)' for i,j in zip(lith_data_df.LITH,
lith_data_df.Well1)],
'legend':{'loc':'center left', 'bbox_to_anchor': (1.0, 0.5),
'ncol':1, 'fontsize':12},
'title':{'label':'Well 1 Lithology Composition', 'fontsize':18}
},
212: {
'values':list(lith_data_df['Well2']),
'labels': [f'{i} ({str(j)} %)' for i,j in zip(lith_data_df.LITH,
lith_data_df.Well2)],
'legend':{'loc':'center left', 'bbox_to_anchor': (1.0, 0.5),
'ncol':1, 'fontsize':12},
'title':{'label':'Well 2 Lithology Composition', 'fontsize':18}
}
},
figsize=(15,10),
rows=5,
columns = 20,
colors=colours)
plt.tight_layout()
plt.show()
The above code will generate the following waffle plots allowing us to compare the lithology differences between the two wells.

How to Split Categories into Individual Waffle Charts
To make things easier for our readers, we can break out each of the categories into their own waffle subplot. This can make it easier for the reader to follow the story and make direct comparisons with other groups containing the same categories.
To do this, we need to leverage matplotlib's subplots functionality.
We need to loop through each of the categories and then add the individual subplots using Waffle.make_waffle()
.
# Set up the colour for unused squares
off_colour = 'lightgrey'
# Figsize numbers must be equal or the height greater than the width
# othewise the plot will appear distorted
fig, axs = plt.subplots(len(lith_data_df), 1, figsize=(10, 15))
for (i, ax), color in zip(enumerate(axs.flatten()), colours):
plot_colours = [color, off_colour]
perc = lith_data_df.iloc[i]['Well1']
values = [perc, (100-perc)]
lith = lith_data_df.iloc[i]['LITH']
Waffle.make_waffle(ax=ax, rows=5, columns=20,
values=values, colors=plot_colours)
ax.set_title(lith)
plt.tight_layout()
plt.show()
When we run this code, we get back the following figure with the individual categories.

Using Circles and Icons Instead of Squares with PyWaffle
If we want to spice up our data visualisation, we can introduce different shapes and icons instead of the standard squares.
To do this, we can add two parameters: icons
and font_size
. The first allows us to specify an icon from the font-awesome website, and the second allows us to control the size of the icon.
plt.figure(FigureClass=Waffle, figsize=(10,10), rows=5, columns = 20,
values=list(lith_data_df['Well1']),
colors=colours,
labels=plot_labels,
icons='circle',
font_size='20',
legend={'loc':'lower center', 'bbox_to_anchor': (0.5, -0.8),
'ncol':3, 'fontsize':12},
starting_location='NW')
plt.show()
After running the code, we get the following plot.

This provides the user with a way to greatly customise the waffle chart, especially if you are looking for a particular style when presenting the data to the user. For example, if you were creating a presentation of a poster discussing bees, you could swap the circles/squares out for bee icons.
Summary
Waffle charts are an excellent tool for data visualisation. They bring extra interest to the story being conveyed by the data and provide a nice alternative to pie charts. The PyWaffle library is simple to use and can be applied to various datasets, including geological and petrophysical ones.
Thanks for reading. Before you go, you should definitely subscribe to my content and get my articles in your inbox. You can do that here! Alternatively, you can sign up for my newsletter to get additional content straight into your inbox for free.
Secondly, you can get the full Medium experience and support thousands of other writers and me by signing up for a membership. It only costs you $5 a month, and you have full access to all of the fantastic Medium articles, as well as the chance to make money with your writing.
If you sign up using my link, you will support me directly with a portion of your fee, and it won't cost you more. If you do so, thank you so much for your support.