Export, accessibility & small-multiple dashboards
A chart that looks fine in a notebook can turn blurry in a PDF, unreadable on a phone, or meaningless to a colour-blind manager. This lesson covers the final mile: exporting, accessibility and arranging many charts as one dashboard.
Saving for the right destination
fig.savefig("sales.png", dpi=200, bbox_inches="tight") # slides, WhatsApp, email
fig.savefig("sales.svg", bbox_inches="tight") # web pages, crisp at any zoom
fig.savefig("sales.pdf", bbox_inches="tight") # printed reports, LaTeX
fig.savefig("sales_web.png", dpi=110, transparent=True) # dark or light site themes
| Format | Type | Best for |
|---|---|---|
| PNG | Raster | Slides, chat apps, email |
| SVG | Vector | Websites, further editing in Figma/Inkscape |
| Vector | Print and reports | |
| JPG | Lossy raster | Avoid - blurs text and lines |
bbox_inches="tight" trims white space so labels are not cut off. Set figsize to the real size you will display (in inches) before plotting; scaling an image later changes font sizes unpredictably.
In a Django view, render into memory rather than disk:
import io
import matplotlib
matplotlib.use("Agg") # non-GUI backend for servers
import matplotlib.pyplot as plt
def chart_png(values):
fig, ax = plt.subplots(figsize=(6, 3))
ax.plot(values)
buf = io.BytesIO()
fig.savefig(buf, format="png", dpi=150, bbox_inches="tight")
plt.close(fig) # free memory on every request
return buf.getvalue()
Accessible colour and design
About 1 in 12 men have some colour vision deficiency, so red-versus-green alone is a poor way to show profit and loss.
- Use tested palettes:
plt.style.use("tableau-colorblind10"), seaborn's"colorblind", orviridis/cividisfor continuous data. - Add a second channel besides colour: markers, line styles, or direct labels.
- Keep text at least 9-10 pt at final size and ensure strong contrast against the background.
- Use grey for context and one strong colour for the series you want readers to notice.
- For web images, write meaningful alt text such as "Line chart: Pune sales rose 18% from April to October".
Small multiples
When comparing many categories, one crowded chart with ten coloured lines fails. Small multiples - a grid of identical mini-charts with shared axes - let the eye compare shapes instantly.
import numpy as np
import pandas as pd
rng = np.random.default_rng(3)
months = pd.date_range("2025-04-01", periods=12, freq="MS")
cities = ["Pune", "Nagpur", "Nashik", "Thane", "Aurangabad", "Kolhapur"]
data = {c: 50 + rng.normal(0, 6, 12).cumsum() for c in cities}
fig, axes = plt.subplots(2, 3, figsize=(10, 5), sharex=True, sharey=True)
for ax, city in zip(axes.flat, cities):
for other in cities: # faint context lines
ax.plot(months, data[other], color="0.85", lw=1)
ax.plot(months, data[city], color="#1f5fa8", lw=2)
ax.set_title(city, fontsize=10, loc="left")
ax.spines[["top", "right"]].set_visible(False)
fig.suptitle("Monthly sales (lakh INR): each branch vs the rest", x=0.01, ha="left")
fig.autofmt_xdate()
fig.tight_layout()
sharey=True is essential: without a common scale, panels look similar even when values differ tenfold.
Key takeaways
- Choose the format by destination: SVG/PDF for vectors, PNG at a known DPI for everything else.
- Always
plt.close(fig)in loops and web views. - Never rely on colour alone; pick colour-blind-safe palettes.
- Small multiples with shared axes beat spaghetti charts.
Practice
Build a 3x3 small-multiple dashboard of weekly orders for nine sweet-shop outlets using a colour-blind-safe colour, highlight each outlet against grey context lines, and save it as both SVG and 200-DPI PNG. Write one sentence of alt text describing the main pattern.