Guide
Diagnosing Matplotlib Figure Size and Layout Problems
A diagnostic guide for fixing clipped labels, pixelated output, uneven subplot spacing, ignored figsize changes, and backend rendering differences in Matplotlib using constrained_layout, figsize, and DPI settings.
Published by Tasadduq Burney
27 May 2026, 14:10 UTC
5 min15.6K views0

Common Symptoms
When a Matplotlib figure is saved or displayed you may see one or more of the following:
- Axis labels, titles, or legends are clipped or overlap.
- The saved image looks pixelated or has unexpected dimensions.
- Subplots in a grid have uneven spacing or large gaps of white space.
- Changing
figsizeafter the figure is created has no effect. - Fonts or colors differ when the script runs on another machine or with a different backend.
Cause & Diagnostic Table
| Observed condition | Likely cause | Quick test |
|---|---|---|
| Clipped/overlapping text | Figure too small for the content or layout engine not adjusting | Print fig.get_size_inches() and compare with the number of subplots |
| Low‑resolution output | dpi passed to savefig differs from the figure’s DPI | Open the saved file in an image editor and verify pixel dimensions = inches × DPI |
| Uneven subplot spacing | Default tight_layout not active; constrained_layout disabled | Enable constrained_layout=True and re‑render |
figsize change ignored | Figure already instantiated; size fixed at creation | Set figsize in plt.figure(figsize=…) before any axes are added |
| Backend‑specific rendering differences | Backend chosen after pyplot import or incompatible with environment | Run matplotlib.get_backend() early; switch with matplotlib.use('Agg') before imports |
Step‑by‑Step Checks
- Verify figure creation order. Ensure
plt.figure(figsize=(w,h), dpi=…, constrained_layout=True)is called before anyadd_subplotorsubplotscall. - Inspect the active layout engine. After creating the figure, run
print(fig.get_layout_engine().name). It should reportconstrainedwhen you expect automatic spacing. - Check DPI consistency. Compare the figure’s DPI (
fig.dpi) with thedpiargument you pass tosavefig. They must match for predictable pixel size. - Confirm backend. At the very top of the script (before any Matplotlib import) set the backend explicitly, e.g.
import matplotlib; matplotlib.use('Agg'). Then importpyplot. - Measure the saved file. Use Pillow or a shell command to read the pixel dimensions:
Expected size =from PIL import Image img = Image.open('output.png') print(img.size) # (width_px, height_px)(figsize[0]*dpi, figsize[1]*dpi).
Fixes per Finding
Clipped or overlapping elements
- Increase
figsizeproportionally to the number of subplots (e.g.,(12, 8)for a 2×2 grid). - Enable
constrained_layout=Trueat figure creation; it automatically resizes axes to accommodate labels. - If you need manual control, call
fig.subplots_adjust(left=…, right=…, top=…, bottom=…, wspace=…, hspace=…)after plotting.
Pixelated or wrong‑size output
- Set the figure DPI once:
fig = plt.figure(figsize=(8,6), dpi=300, constrained_layout=True). - Call
fig.savefig('output.png', dpi=300)with the same DPI value; omit the argument to inherit the figure’s DPI. - Verify with the Pillow snippet above; adjust
figsizeor DPI until the pixel dimensions match the target.
Uneven subplot spacing
- Prefer
constrained_layout=Trueover the oldertight_layout()call; it works for complex grids and colorbars. - If you must use
tight_layout, call it after all artists are added:fig.tight_layout(pad=2.0).
Figsize changes ignored
- Never modify
fig.set_size_inches()after axes exist if you rely onconstrained_layout; the layout engine may override the change. - Create a new figure with the desired size, or set the size before any axes are added.
Backend‑related rendering issues
- Set the backend before any Matplotlib import:
import matplotlib; matplotlib.use('Agg')for headless servers. - For interactive work, choose a GUI backend compatible with the environment (e.g.,
TkAgg,Qt5Agg). - If you must switch backends at runtime, restart the interpreter; changing after import leads to undefined behavior.
Escalation Criteria
Escalate to a deeper investigation when:
- All checks above pass but clipping persists – possible bug in the layout engine for the specific Matplotlib version.
- Pixel dimensions still differ after matching DPI – verify that the backend’s renderer (e.g., Agg vs. Cairo) does not apply additional scaling.
- Fonts missing or substituted – install the required font files or configure
matplotlib.rcParams['font.family']explicitly. - Performance degrades with large grids – consider using
plt.subplots(..., sharex=True, sharey=True)and disablingconstrained_layoutfor that figure.
Verification Checklist
- Run the script with
constrained_layout=Trueand confirm no labels are cut off visually. - Save the figure, open it with Pillow, and assert
img.size == (int(figsize[0]*dpi), int(figsize[1]*dpi)). - Execute the same script under two backends (e.g.,
AggandTkAgg) and compare the saved PNGs pixel‑by‑pixel (they should be identical for non‑interactive output). - Attempt to change
figsizeafter axes creation; verifyfig.get_size_inches()remains unchanged, confirming the expected immutability.
Concrete Example
import matplotlib
matplotlib.use('Agg') # set backend before pyplot import
import matplotlib.pyplot as plt
from PIL import Image
# 1. Create figure with explicit size, DPI, and constrained layout
fig = plt.figure(figsize=(10, 8), dpi=200, constrained_layout=True)
axes = fig.subplots(2, 2)
for i, ax in enumerate(axes.flat):
ax.plot([0, 1], [0, i+1])
ax.set_title(f'Subplot {i+1}')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
# 2. Save using the same DPI (omit dpi argument to inherit)
fig.savefig('grid.png')
# 3. Verify pixel dimensions
img = Image.open('grid.png')
expected = (int(10*200), int(8*200)) # (2000, 1600)
print('Saved size:', img.size, 'Expected:', expected)
assert img.size == expected, 'Dimension mismatch'
Running the snippet on a headless server produces a 2000 × 1600 px PNG with all titles and axis labels fully visible. If the assertion fails, revisit the DPI consistency check (step 3).
Limitations
constrained_layoutdoes not support every artist type (e.g., some custom annotations). In those cases fall back to manualsubplots_adjust.- Backend behavior can differ for vector formats (PDF, SVG); the DPI checks apply only to raster outputs.
- Changing the backend after the first import is unsupported; the only reliable method is to set it before any Matplotlib module loads.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.