import matplotlib.pyplot as plt
# Just import maidr package: plt.show() now renders accessible output
import maidr Accessible Pie Charts and Word Clouds in matplotlib with py-maidr
A pie chart and a word cloud have something in common that makes them hard for a screen reader: their data is drawn as angle or as glyph size and written down nowhere on the page. py-maidr reads both as a flat row of labelled values, so the Left and Right arrows walk the slices or terms and each stop is announced by its label, its value and, for a pie, the share of the whole it works out to.
PIE is in the stable set. WORD_CLOUD is experimental and may change without a deprecation period: see Plot Type Stability.
Setup
Every example on this page needs only the import below. The plot cells repeat it so each one can be copied on its own.
Pie Chart
A pie is one flat row of slices, so navigation runs left and right across them and there is no second dimension to move through. Each slice is announced by its label, its value, and the share of the whole that value works out to. The percentage is derived from the values at render time rather than authored, so it can never disagree with the numbers it is derived from.
ax.pie() normalises what it is given — it draws x / sum(x), and each wedge keeps only its angles — so the counts below survive as counts because maidr reads them off the call rather than off the drawn wedges.
import matplotlib.pyplot as plt
import seaborn as sns
import maidr
# Load the tips dataset
tips = sns.load_dataset("tips")
# Count the tips recorded on each day of the week
day_counts = tips["day"].value_counts()
fig, ax = plt.subplots(figsize=(8, 8))
# autopct draws the percentages onto the chart for sighted readers; maidr
# derives its own from the values, so the two cannot drift apart.
ax.pie(
list(day_counts.values),
labels=list(day_counts.index),
autopct="%1.1f%%",
startangle=90,
)
ax.set_title("Share of Tips by Day")
# Name the two dimensions of a slice, so it is read out as
# "Day: Sat, Number of tips: 87" rather than "X: Sat, Y: 87".
ax.set_xlabel("Day")
ax.set_ylabel("Number of tips")
plt.show() A pie has no x or y scale, so its two axis labels name the dimensions of a slice instead: xlabel says what the slice labels mean and ylabel says what the slice values measure. Leave them unset and maidr falls back to “Category” and “Value”, which at least read as English where “X” and “Y” would not.
Negative sizes are rejected — matplotlib refuses to draw a wedge with no area, so ax.pie() raises before maidr sees the call at all; maidr repeats the check for anyone building a PiePlot directly. Drawing options that only move the wedges around (startangle, counterclock, explode, shadow) leave the slice order alone, so the data stays index-aligned with what is on screen.
Word Cloud
This is one of the experimental plot types. It has not been through a user study, and it may change without a deprecation period. See Plot type stability.
A word cloud is the extreme case of a chart that carries real data while being readable only by eye: each term’s weight is drawn as glyph size and written down nowhere on the page. Structurally it is a categorical label and a magnitude, so maidr reads it as a term and its number.
wordcloud is an optional dependency — pip install maidr[wordcloud].
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import maidr
mentions = {
"accessibility": 412, "sonification": 300, "braille": 250,
"screenreader": 190, "keyboard": 155, "contrast": 120,
"captions": 95, "semantics": 70,
}
# Pass the `WordCloud` object itself -- see the note below on `to_array()`.
cloud = WordCloud(
width=800, height=400, background_color="white",
max_words=8, random_state=42,
).generate_from_frequencies(mentions)
fig, ax = plt.subplots(figsize=(8, 4))
ax.set_title("What the accessibility reports talk about")
ax.imshow(cloud)
ax.set_axis_off()
plt.show() A cloud has no x or y scale — the glyph positions are packing, not data — so maidr names the two axes after what a point holds rather than where it sits. No labels were set above, so the defaults fire and a term reads as “Term: braille, Relative frequency: 0.607”. The generic “X”/“Y” the base class would otherwise give says nothing about a word.
The weights are relative, and that y axis name says so. WordCloud divides every frequency by the largest one in generate_from_frequencies and keeps only the ratio — the raw counts are on no attribute of the object. So the counts above are announced as 1.0, 0.728, 0.607, and the axis is called “Relative frequency” rather than “Occurrences”. Naming it after counts would hand a reader “accessibility, 1.0” for a term that occurred 412 times.
That is also what the chart draws: glyph size is proportional to the ratio, so a reader hearing 1.0 and 0.728 hears what a sighted reader sees. (The R binding is handed the raw counts, so it can honestly say “Occurrences” — same chart, two different honest readings.)
Show the object, not the picture. wc.to_array() and wc.to_image() hand imshow a plain RGB array, and the terms are not in it. A cloud displayed that way stays a picture and is not read — which is the honest answer, since the data never reached maidr.
Name the axes yourself if the defaults are too generic. An authored label always wins, so a cloud over survey topics can say so:
ax.set_xlabel("Topic")
ax.set_ylabel("Share of mentions")which reads as “Topic: braille, Share of mentions: 0.607”. Keep the y name honest about being a ratio — “Occurrences” would be wrong for the same reason the default avoids it.
No highlighting. imshow rasterises the whole cloud into a single image element, so there is no per-term element to outline. The reading ships without a visual highlight rather than pairing the terms with some other layer’s marks.