Quick Reference · waffle & pictogram charts

pywaffle cheat sheet

The unusual part first: PyWaffle gives you a Matplotlib Figure class, not a plotting function. You call plt.figure(FigureClass=Waffle, ...) and every waffle option rides along as a keyword. It counts out one block per unit on a rows × columns grid — and those blocks can be squares, Unicode characters, or Font Awesome icons.

import / setup the Waffle class values & grid blocks & layout characters & icons colour · legend · output gotcha most common

Distilled & cross-checked against PyWaffle 1.1.1 (gyli; current, re-verified 2026-08-28): pywaffle.readthedocs.io (full API Reference + Examples, fetched) · github.com/gyli/PyWaffle · PyPI · python-charts.com · python-graph-gallery.com

One figure call — values become blocks
FROM NUMBERS TO BLOCKS 1 · values + grid list · dict · Series rows and/or columns 2 · scale & round values → block counts rounding_rule 3 · draw blocks square · character · icon location · direction · style 4 · a Figure a normal matplotlib Figure savefig · show · style it THE UNUSUAL PART — A FIGURE CLASS, NOT A FUNCTION fig = plt.figure(FigureClass=Waffle, rows=5, values=data, ...) FigureClass & figsize belong to pyplot · every other keyword belongs to Waffle a matplotlib Figure object everything you already know still applies Drawing into an Axes you already have? Waffle.make_waffle(ax=ax, rows=5, values=[48, 46, 6]) Multiple waffles in one figure? plots={311: {...}, 312: {...}} — each subplot inherits the parent's parameters.
A pictogram chart in one call
import matplotlib.pyplot as pltfrom pywaffle import Waffledata = {"Car": 58, "Pickup": 21, "Truck": 11, "Motorcycle": 7}fig = plt.figure( FigureClass=Waffle, # a Figure CLASS, not a function rows=5, # only one of rows/columns → no scaling values=data, # dict keys become legend labels colors=["#c1d82f", "#00a4e4", "#fbb034", "#6a737b"], icons=["car-side", "truck-pickup", "truck", "motorcycle"], # Font Awesome font_size=12, icon_legend=True, legend={"loc": "upper left", "bbox_to_anchor": (1, 1)}, # a dict! figsize=(8, 4), # figsize is pyplot's)fig.savefig("waffle.png", bbox_inches="tight")
01Setup & Importonce per project
02The Waffle Figure Classthe mental model
03Valueswhat to count
04The Grid: rows & columnsthe key rule
05Scaling & Roundingvalues → blocks
06Colours & Colormapsthe palette
07Legendpass a dict
08Title & Textalso a dict
09Block Shape & Spacingtile geometry
10Location, Direction & Arrangingfill order
11Characters & Iconspictogram charts
12Multiple Wafflesplots=
13Figure, Anchor & Outputit's Matplotlib
14Recipescopy & adapt

Four ideas that explain the chart

What a block actually represents, the one rule that decides whether your numbers get rescaled, how categories flow across the grid, and the three things a block can be drawn as. The grids below are laid out in PyWaffle's real fill order.

one block = one unit

The docs' canonical example: rows=5, columns=10, values=[30, 16, 4] — exactly 50 blocks, filled column by column from the lower-left.

30 + 16 + 4 = 50 blocks · starting_location="SW" · vertical=False

absolute vs scaled

Pass one of rows/columns and your values are block counts. Pass both and the grid is fixed, so values get scaled to fit it.

rows=5 only values [10, 7, 9] = 26 blocks, exact rows=5, columns=10 values [48, 46, 6] scaled → 24, 23, 3 = 50

block_arranging_style

Where each new category begins. Same two categories (7 then 5 blocks) on the same grid, three different styles.

normal snake new-line continues in place direction alternates starts a fresh line note the gap new-line leaves behind

three things a block can be

The same counts drawn as rectangles (default), as a Unicode character, or as a Font Awesome icon — a pictogram chart.

default rectangles characters characters="⬤" icons icons="star" icons ignore block_aspect_ratio and interval_ratio_x / _y

Worth memorizing

FigureClass=Wafflea Figure class passed to plt.figure — not a plotting function
rows and/or columnsat least one is required
one givenvalues are absolute block counts — no scaling
both givengrid is fixed and your values get scaled to fit
rounding_rulefloor is the most consistent; nearest/ceil can overflow
qualitative colormaps onlySet2 default — sequential ones don't work
legend, title, tightall take dicts, not loose keywords
dict valueskeys become labels; a DataFrame's index does not
starting_locationdefault SW — lower-left, column by column
icons ignore spacingblock_aspect_ratio & interval_ratio_* do nothing; use font_size
icon_size deprecateduse font_size
make_waffle & plotsdraw into an existing Axes, or many waffles in one figure