2025, Oct 06 13:00

Remove Horizontal Padding in Matplotlib: control Axes margins vs Figure layout with tight_layout, subplots_adjust, constrained_layout

Remove horizontal padding in Matplotlib by tuning Axes margins and Figure layout: tight_layout, subplots_adjust, and constrained_layout with order tips.

Matplotlib plots sometimes come with unwanted horizontal padding, where the actual Axes content looks squeezed by extra space on the sides. If you want the plotted data to span the available width more tightly, you need to distinguish between margins inside the Axes and the empty space around the Axes within the Figure.

Minimal example that shows the issue

The following snippet creates a line plot and calls tight_layout, yet still leaves visible horizontal padding between the plot and the window.

import numpy as np, matplotlib.pyplot as plt
series = np.random.rand(100)
plt.figure(figsize=(12, 6), dpi=100)
plt.tight_layout()
plt.plot(series)
plt.show()

A slightly different setup, changing DPI and size on a Figure and Axes pair, produces the same visual outcome.

import numpy as np, matplotlib.pyplot as plt
series = np.random.rand(100)
board, axes = plt.subplots()
board.set_dpi(100)
board.set_size_inches(12, 6)
axes.autoscale_view('tight')
axes.plot(series)
plt.show()

What’s actually happening

There are two layers to the spacing problem. First, the Axes can reserve margins around the data itself; this is the space that margins manipulates. Second, the Figure keeps outer padding around the Axes; this is controlled by layout settings and subplots_adjust. Changing Axes margins reduces the whitespace inside the plotting area, but it does not affect the blank strip between the Axes and the Figure boundary. That is why a call like axes.margins(x=0) may have no visible effect on the space between the plot and the window while still trimming a bit inside the Axes.

There is also a timing detail: layout engines need to run after artists are added. Moving layout calls to the end helps them compute correct extents.

Move the tight_layout call after the plot call. Better still, using the more modern constrained layout.

Fixes that target the right layer

If the goal is to remove horizontal padding inside the Axes, set margins along x to zero, or explicitly set x-limits to the data range.

import numpy as np, matplotlib.pyplot as plt
vals = np.random.rand(100)
frame, art = plt.subplots(figsize=(12, 6), dpi=100)
art.plot(vals)
art.margins(x=0) # trims horizontal whitespace inside the Axes
# Equivalent intent via explicit limits:
# art.set_xlim(0, len(vals) - 1)
plt.tight_layout() # place after artists are added
plt.show()

If the target is the space between the Axes and the Figure edge, adjust the subplot region or enable constrained layout. Shrinking margins directly addresses the outer padding.

import numpy as np, matplotlib.pyplot as plt
values = np.random.rand(100)
fig_box, ax_obj = plt.subplots(figsize=(12, 6), dpi=100)
ax_obj.plot(values)
ax_obj.margins(x=0) # optional: tight data fit inside Axes
fig_box.subplots_adjust(left=0.02, right=0.98, top=0.98, bottom=0.05)
plt.show()

Alternatively, request the layout engine that manages outer padding automatically.

import numpy as np, matplotlib.pyplot as plt
seq = np.random.rand(100)
sheet, pane = plt.subplots(figsize=(12, 6), dpi=100, constrained_layout=True)
pane.plot(seq)
pane.margins(x=0) # optional for data-wide fit
plt.show()

Why you should care

Precise control over margins ensures that plots communicate signal instead of wasting pixels on empty space. It keeps dashboards compact, exports consistent, and side-by-side comparisons aligned. When layout and data margins are treated separately, you avoid chasing the wrong knob and get predictable results.

Takeaways

Decide whether you want to reduce whitespace inside the Axes or the outer padding around it. Use axes.margins(x=0) or explicit limits like axes.set_xlim(0, len(data) - 1) to tighten the data fit horizontally. Use fig.subplots_adjust to shrink the gap between Axes and Figure, or enable constrained_layout for a modern automatic layout. Place tight_layout after all plotting calls so the layout routine sees the final scene.

The article is based on a question from StackOverflow by Paul Jurczak and an answer by Subir Chowdhury.