7.4 Layouts, Styles, and Reusable Plotting Functions
One panel should answer one coherent comparison. When a question needs several complementary views, layout should create a reading sequence rather than a dense collection of equally loud charts.
Design a visual hierarchy across Axes
Regular grids are concise:
fig, axs = plt.subplots(
2,
2,
figsize=(10, 7),
layout="constrained",
)
for ax in axs.flat:
ax.grid(axis="y", alpha=0.2)axs is a NumPy array for a multi-panel grid; .flat provides a simple iterator. Share an x- or y-axis only when panels use the same variable, units, scale, and meaningful range. Shared axes synchronize limits, not merely label visibility. Sharing unrelated ranges can flatten one panel or falsely suggest direct comparability.
For a dashboard-like hierarchy, subplot_mosaic names panels and allows a primary view to span cells:
fig, axd = plt.subplot_mosaic(
[
["trend", "trend"],
["region", "distribution"],
],
figsize=(10, 7),
height_ratios=[1.4, 1],
layout="constrained",
)
axd["trend"].plot(daily["date"], daily["sales"])
axd["region"].barh(region_sales.index, region_sales.values)
axd["distribution"].hist(order_values, bins="fd")The returned dictionary makes panel intent readable. Use a Figure-level title for the shared question and short panel titles for each view. Remove repeated labels and legends only when their meaning remains unambiguous.
layout="constrained" asks Matplotlib to reserve space for titles, labels, tick labels, legends, and colorbars. It is more capable than calling tight_layout() afterward, and calling tight_layout() disables constrained layout. Automatic layout is still not proof of a good output: a small canvas, long translations, outside Artists, and backend-specific fonts can still crowd or clip.
Keep style local and drawing functions composable
A style sheet configures many Artist defaults. Global mutation through plt.rcParams[...] can leak into unrelated notebook cells and tests. Use a context when style belongs only to one output:
import matplotlib as mpl
import matplotlib.pyplot as plt
with plt.style.context("default"), mpl.rc_context({
"font.size": 10,
"axes.titleweight": "bold",
"axes.spines.top": False,
"axes.spines.right": False,
}):
fig, ax = plt.subplots(layout="constrained")
artists = draw_monthly_sales(ax, monthly)Style communicates hierarchy; it must not change the evidence. Decorative gradients, three-dimensional perspective, and excessive ink can make values harder to compare. Use a restrained palette, sufficient contrast, and redundant series cues. Test both light and dark backgrounds if transparency is part of the export contract.
Separate responsibilities:
1. Data preparation validates and aggregates observations.
2. A drawing function accepts ax and prepared data, then returns important Artists.
3. The composition layer creates Figures, layouts, shared legends, and captions.
4. The export layer chooses file format, dimensions, DPI, background, and destination.
For example:
def draw_monthly_sales(ax, data):
line, = ax.plot(
data["month"],
data["sales"],
marker="o",
label="Sales",
)
ax.set(
title="Monthly sales",
xlabel="Month",
ylabel="Sales (USD)",
)
return {"line": line}The function does not call show, savefig, or create a Figure, so it can be composed into a report, dashboard, notebook, or test.
Export for the actual destination
SVG and PDF preserve vector text and geometry, making them strong choices for reports with lines and labels. PNG is raster output. Its approximate pixel dimensions follow
A 7.5-inch-wide Figure saved at 160 DPI is about 1,200 pixels wide before tight bounding-box adjustments:
fig.savefig(
"monthly-sales.svg",
bbox_inches="tight",
)
fig.savefig(
"monthly-sales.png",
dpi=160,
bbox_inches="tight",
facecolor="white",
)
plt.close(fig)Higher DPI increases pixel count and file size; it cannot repair labels that were already too small or clipped. Check the final file at its actual placement size. Verify fonts, mathematical notation, long Chinese and English labels, line visibility, legend inclusion, background, and color behavior.
For accessibility, accompany important exported charts with a concise text summary or data table. Alternative text should state the chart's purpose and principal pattern rather than listing every decorative feature.
This chapter built a visual evidence pipeline: choose a faithful encoding, draw it on explicit Axes, disclose scales and uncertainty, compose readable layouts, and export deliberately. Chapter 8 will apply the same discipline to machine-learning experiments, where data boundaries and evaluation design matter as much as the model itself.