PyWaffle draws waffle charts as ordinary matplotlib figures
🧇 Make Waffle Charts in Python.
At a glance
- What is it?
- PyWaffle is an MIT-licensed Python package for plotting waffle charts, also known as square pie charts and, with icons, pictogram charts. It ships a function that returns a matplotlib figure and axes pair, and a Figure constructor class for people who build their figures the pyplot way, and it adds one dependency floor and one optional extra.
- Who is it for?
- PyWaffle suits anyone who has already decided that a waffle chart communicates better than a pie, and who wants the grid, the scaling and the legend handled without writing a rectangle loop by hand, since the output is a normal matplotlib figure you can keep styling. It is the wrong tool if you need a chart type it does not cover, if you want the pictogram icons without installing the extra, or if you are pinned to a matplotlib older than 3.6.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 22 days ago.
- What is it written in?
- Mainly Jupyter Notebook, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One block stands for a fixed quantity
The README states the idea in a sentence: a waffle chart is a grid of blocks where one block stands for a fixed quantity, so a proportion is something the reader can count rather than estimate from the angle of a pie slice.
That is the whole argument for the chart type, and it is a good one. A pie slice asks a reader to compare angles. A waffle chart asks them to count squares, and counting is something people do well. The chart is also called a square pie chart, and when the squares are drawn as icons rather than rectangles it becomes a pictogram chart.
The quickstart uses the passengers of the Titanic as the running example, 2,201 of them in a grid of 100 blocks, so one block is about 22 people. The figures come from the British Board of Trade inquiry of 1912, tabulated at the Sinking of the Titanic reference in the README, which is a good sign about the level of care the examples take.
The audience is people who already work in matplotlib and want one more chart type. PyWaffle is a plotting package, not a dashboard tool, not a data pipeline, and not a chart maker that outputs its own file format. It produces a matplotlib Figure, and everything you know about matplotlib still applies to it afterwards.
A function, and a Figure constructor class
PyWaffle offers two ways in, and the README is explicit that they build the same chart.
The first is the waffle_chart function. It takes the data and the chart options and returns the matplotlib figure and axes pair:
fig, ax = waffle_chart(
aboard,
rows=10,
columns=10,
colors=class_colors,
title={"label": "Who was aboard the Titanic: 2,201 people", "loc": "left"},
legend=aside,
show_values=True,
figsize=(6.5, 4),
)The README notes that because the function returns the figure and axes pair, everything you already know about matplotlib still applies, and you can pass ax to draw into a layout you have already built.
The second is the Figure constructor class Waffle, which is the form used throughout the examples and is fully supported:
fig = plt.figure(FigureClass=Waffle, rows=10, columns=10, values=aboard)That form plugs into pyplot.figure, so it inherits the whole parameter list of that function, including figsize, and the chart specific parameters such as rows, columns and values come from the Waffle class itself and are documented in the API reference.
The functional form is the one to reach for when you are assembling a figure from several panels, because it hands you the axes to draw into. The class form is the one to reach for when you are writing a script that makes one chart per dataset and you want the familiar pyplot call.
Auto-sizing rescales your values to fit the grid
This is the behaviour to understand before you publish a chart, because it changes the numbers you put in.
The README takes the four Titanic class counts, 325, 285, 706 and 885 for a total of 2,201 people, and states that they are automatically scaled to 7, 6, 16 and 20 to fit the 5 by 10 chart size, so one block stands for about 22 people.
So the values you pass are proportions, and the library decides how many blocks each one occupies. That is what makes the chart work with any dataset, and it is also where the precision goes. When a value does not divide evenly into blocks, the chart either rounds or, in the more careful mode, uses a partial block.
The README points at the full quickstart for partial blocks that lose nothing to rounding, along with continuous tiled grids, sorting, subplots, and drawing into an existing layout. If the chart is going in front of an audience that will add up the squares, that page is the one to read.
There is a second mode. If you want the absolute values treated as block numbers rather than scaled, leave columns unspecified and pass absolute values:
data = {'First class': 6, 'Second class': 24, 'Third class': 79}
fig = plt.figure(
FigureClass=Waffle,
rows=5,
values=data,
legend={'loc': 'upper left', 'bbox_to_anchor': (1.05, 1)},
)The example uses the children aboard the Titanic, so here one block really is one child. Only rows is specified and columns is empty, and the README notes that rows could equally be the optional one if columns is specified. When values is a dict, the keys are used as labels in the legend.
Icons turn the grid into a pictogram, if you install the extra
The pictogram form replaces the rectangles with Font Awesome icons, so one figure stands for a number of people:
by_group = {"Men": 1667, "Women": 425, "Children": 109}
fig, ax = waffle_chart(
by_group,
rows=5,
columns=10,
colors=["#3d4f5d", "#c9a227", "#b5653f"],
icons=["person", "person-dress", "child"],
font_size=22,
icon_legend=True,
background_color="#f4f2ee",
title={"label": "One figure = 44 people aboard", "loc": "left"},
legend=aside,
show_values=True,
figsize=(6.5, 2.8),
)The icon names are the Font Awesome names, and the legend is switched to icon mode with icon_legend, while font_size controls how large the glyphs are drawn.
Font Awesome is an optional dependency rather than a hard one, because it is only needed to draw with icons. The install is an extra:
pip install "pywaffle[icons]"The pyproject file explains the choice, and also the failure mode. Requesting icons without the extra raises ImportError with installation instructions, so the error tells you exactly what to run rather than failing obscurely.
That is a well judged dependency arrangement for a library whose icon support is optional. It also means the same chart code runs in an environment with no font dependency until someone asks for icons.
The dependency floors are deliberate and explained
Two version constraints in the packaging are worth knowing about, because both comments in the pyproject file explain the failure they prevent.
The first is matplotlib 3.6. The comment says 3.6 is the first version with Figure.set_layout_engine, which the package uses in place of the deprecated set_tight_layout. Without the floor, installing into an environment that already pins an older matplotlib leaves the pin in place, and every figure then raises AttributeError. So the floor exists to make a broken environment impossible rather than to chase a new feature.
The second is setuptools 77, for the build. The comment notes that 77 is the first setuptools that understands PEP 639, which is what the license and license-files fields below rely on, and that an older one fails metadata generation outright with a configuration error about project.license needing to be valid exactly by one definition. The comment is careful about when this bites: build isolation hides the problem by always fetching the newest setuptools, so the floor only matters for a build with --no-build-isolation, such as conda-forge, distro packaging or offline CI.
The runtime requirements file states the same floor in one line, matplotlib>=3.6, and Python 3.9 or newer is the requirement in the project metadata.
Together these make the package predictable in the way a plotting library should be: one hard dependency, one optional extra, and floors chosen so that a stale environment fails at install time rather than at figure time.
Installing it and drawing the first chart
The installation is a single command from the Python Package Index:
pip install pywaffleThe full quickstart starts with the data and the imports:
import matplotlib.pyplot as plt
from pywaffle import waffle_chart
aboard = {"First class": 325, "Second class": 285, "Third class": 706, "Crew": 885}
class_colors = ["#c9a227", "#5f8a8b", "#b5653f", "#3d4f5d"]
aside = {"loc": "upper left", "bbox_to_anchor": (1.02, 1), "frameon": False}and then the chart call shown earlier. You should get a 10 by 10 grid of coloured squares with a legend, a left aligned title and the value annotations switched on by show_values.
The package is also on PyPI with its own project page, the documentation is hosted on Read the Docs, and there is an online demo through Binder that opens demo.ipynb from the repository. The examples directory in the repository holds the generated SVG files the README embeds, with a generate_plots.py script, so you can see exactly what each option produces without running anything.
The README_pypi.rst file is the source for the PyPI long description, which is a small sign of care: the package page and the repository read differently, because they are written for different audiences.
Maintenance signals and what the repository contains
The project is not archived, and the last push was on 2026-10-01. The one release tag listed is v1.2.0 on 2026-09-17, which is a young release line rather than a project with a long tag history, and it suggests the package is being republished on a new version scheme.
The licence is MIT, declared in the project metadata with a LICENSE file, and the classifiers mark the development status as Production/Stable. For a plotting library, that is about as permissive as it gets: you can use it inside a commercial product, and the only obligation is keeping the notice.
The repository is organised for maintenance rather than for a single release. There is a docs directory with the Read the Docs configuration, an examples directory with the plots used in the documentation, a tests directory, and a demo notebook. The build and release tooling is visible too, with build.sh, release.sh, a RELEASING.md file, a CHANGELOG.md file, and separate requirements files for development and documentation.
The examples in the README are numbered and each one isolates a single decision. There is the basic value scaling chart, the dictionary input with auto-sizing, and a fuller example that sets legend, title, colors, direction and background, with starting_location and vertical as the two options that control how the grid fills and which way it runs.
The pattern across the whole repository is that the package is small, the documentation is extensive, and the number of moving parts you depend on is one.
Editorial conclusion
PyWaffle suits anyone who has already decided that a waffle chart communicates better than a pie, and who wants the grid, the scaling and the legend handled without writing a rectangle loop by hand, since the output is a normal matplotlib figure you can keep styling. It is the wrong tool if you need a chart type it does not cover, if you want the pictogram icons without installing the extra, or if you are pinned to a matplotlib older than 3.6. Verify first that your data survives the scaling, because values are rescaled to fit the grid and one block stands for a rounded quantity, then read the quickstart page on partial blocks that lose nothing to rounding before you publish a chart where the arithmetic has to be exact.
Frequently asked questions
How do I install PyWaffle and draw a waffle chart?
Install it with pip install pywaffle, then call waffle_chart from the pywaffle package with your data and the chart options. The function returns the matplotlib figure and axes pair, so you can keep styling the result as you would any other figure.
Do I need Font Awesome to draw a PyWaffle pictogram chart?
Only if you want icons. Font Awesome is an optional extra installed with pip install "pywaffle[icons]", and requesting icons without it raises an ImportError with installation instructions.
How are my values converted into blocks in a PyWaffle chart?
They are automatically scaled to fit the grid you specify, so in the README example the counts 325, 285, 706 and 885 are scaled to 7, 6, 16 and 20 blocks in a 5 by 10 chart, with one block standing for about 22 people. You can also pass absolute values to use them directly as block numbers.
What are the minimum versions PyWaffle needs?
Python 3.9 or newer and matplotlib 3.6 or newer. The matplotlib floor exists because 3.6 is the first version with Figure.set_layout_engine, which PyWaffle uses in place of the deprecated set_tight_layout, and an older matplotlib would raise AttributeError on every figure.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/gyli-pywaffle)Was this useful?