Inset & Broken Axes

Reviewed & published by Brayan K

An inset axes is a small zoomed-in plot drawn inside a larger one, and a broken axis cuts a gap out of a scale so distant data clusters can share a single chart without wasting empty space.

Part of the free Matplotlib course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

In this lesson you'll add a zoom-in with ax.inset_axes(), mark the zoomed region with indicate_inset_zoom(), and hand-build a broken axis from two subplots with diagonal break marks.

What You'll Learn in This Lesson

1 A Zoom-In Inset

ax.inset_axes([x, y, w, h]) drops a miniature Axes inside the main one. The four numbers are in axes-fraction coordinates: position the bottom-left corner at (x, y) and give it width w and height h. Plot the same data on the inset, then narrow its limits to the region you want to magnify.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 5, 500)
y = np.sin(x) * np.exp(-x / 5)

fig, ax = plt.subplots()
ax.plot(x, y, color="darkblue")
ax.set_title("Damped Sine with a Zoom Inset")

# A small inset in the upper-right corner
axins = ax.inset_axes([0.55, 0.55, 0.4, 0.4])
axins.plot(x, y, color="darkblue")

# Zoom into the first peak
axins.set_xlim(1.0, 2.2)
axins.set_ylim(0.4, 0.85)
axins.set_title("zoom", fontsize=9)

plt.show()
The chart this code draws: Create a zoomed-in inset axes
What this code draws, rendered with Matplotlib.

What you'll see: a damped sine curve filling the plot with a small framed copy in the upper-right that magnifies just the first peak, revealing detail that is hard to read in the full view.

2 Linking the Inset With indicate_inset_zoom

A floating zoom is clearer when the reader can see where it came from. After setting the inset's limits, call ax.indicate_inset_zoom(axins). Matplotlib draws a rectangle on the main axes around the zoom region and connector lines to the inset.

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(0)
x = np.linspace(0, 10, 1000)
y = np.sin(x) + rng.normal(0, 0.08, x.size)

fig, ax = plt.subplots()
ax.plot(x, y, color="seagreen", linewidth=0.8)
ax.set_title("Noisy Signal with Linked Zoom")

axins = ax.inset_axes([0.6, 0.08, 0.36, 0.36])
axins.plot(x, y, color="seagreen", linewidth=0.8)
axins.set_xlim(4.0, 4.8)
axins.set_ylim(-0.5, 0.5)
axins.set_xticklabels([])
axins.set_yticklabels([])

# Draw the indicator rectangle + connector lines
ax.indicate_inset_zoom(axins, edgecolor="black")

plt.show()
The chart this code draws: Link the inset to its region with a rectangle and connectors
What this code draws, rendered with Matplotlib.

3 A Broken Axis From Two Subplots

Matplotlib has no one-line broken axis, but the recipe is reliable: stack two subplots that share the x-axis, give the top one the high range and the bottom one the low range, hide the facing spines, and draw small diagonal marks where the break sits.

import matplotlib.pyplot as plt
import numpy as np

cats = ["A", "B", "C", "D", "E"]
vals = [9, 11, 8, 980, 1000]   # two values are huge

fig, (ax_top, ax_bot) = plt.subplots(
    2, 1, sharex=True,
    gridspec_kw={"height_ratios": [1, 1], "hspace": 0.08})

ax_top.bar(cats, vals, color="indigo")
ax_bot.bar(cats, vals, color="indigo")

# Top shows the high cluster, bottom shows the low one
ax_top.set_ylim(950, 1050)
ax_bot.set_ylim(0, 15)

# Hide the facing spines to open the gap
ax_top.spines["bottom"].set_visible(False)
ax_bot.spines["top"].set_visible(False)
ax_top.tick_params(bottom=False)

# Diagonal break marks
d = 0.5
kw = dict(marker=[(-1, -d), (1, d)], markersize=12,
          linestyle="none", color="k", mec="k", mew=1, clip_on=False)
ax_top.plot([0, 1], [0, 0], transform=ax_top.transAxes, **kw)
ax_bot.plot([0, 1], [1, 1], transform=ax_bot.transAxes, **kw)

fig.suptitle("Broken Y-Axis")

plt.show()
The chart this code draws: Two subplots, hidden spines, and diagonal marks
What this code draws, rendered with Matplotlib.

What you'll see: two short bars and two towering bars sharing one chart — the y-axis has a visible gap with little diagonal slashes, so the huge values and the tiny values are both readable without one dwarfing the other.

🎯 Your Turn: Fill in the Blanks

Replace each ___ to add an inset and zoom it into the data's first second.

import matplotlib.pyplot as plt
import numpy as np

t = np.linspace(0, 10, 500)
v = np.cos(2 * t)

fig, ax = plt.subplots()
ax.plot(t, v, color="darkred")

# Create an inset in the lower-left: [x, y, w, h]
axins = ax.___([0.08, 0.1, 0.35, 0.35])
axins.plot(t, v, color="darkred")

# Zoom the inset to t between 0 and 1
axins.set_xlim(0, ___)
axins.set_ylim(-1, 1)

ax.set_title("Inset Practice")

# Display the figure
plt.___()

📋 Common Errors & Quick Reference

❌ The inset is empty

You created the inset but plotted only on the main axes. ✅ Call axins.plot(...) on the inset too, then set its limits.

❌ indicate_inset_zoom draws nothing useful

You called it before narrowing the inset. ✅ Set axins.set_xlim/ylim first so it knows the region.

❌ Break marks vanish at the figure edge

They are clipped to the axes. ✅ Pass clip_on=False and draw them in transAxes coordinates.

CallWhat It Does
ax.inset_axes([x,y,w,h])Adds a small inset axes
axins.set_xlim / set_ylimSets the zoom window
ax.indicate_inset_zoom(axins)Draws the linking rectangle
sharex=TrueLinks subplot x-axes for a break
spine.set_visible(False)Opens the gap in a broken axis

🎯 Mini-Challenge: Zoom Into a Spike

Plot a curve with a sharp spike, add an inset zoomed onto the spike, and link it with indicate_inset_zoom.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 1000)
y = np.exp(-((x - 5) ** 2) / 0.05)   # a tall narrow spike at x=5

fig, ax = plt.subplots()
ax.plot(x, y, color="darkorange")
ax.set_title("Spike with Linked Zoom")

axins = ax.inset_axes([0.08, 0.5, 0.35, 0.4])
axins.plot(x, y, color="darkorange")
axins.set_xlim(4.7, 5.3)
axins.set_ylim(0, 1.05)

ax.indicate_inset_zoom(axins, edgecolor="gray")

plt.show()
The chart this code draws: Inset zoom onto a sharp spike, linked with a rectangle
What this code draws, rendered with Matplotlib.
import matplotlib.pyplot as plt

x = list(range(20))
y = [v * v for v in x]

fig, ax = plt.subplots()
ax.plot(x, y)

inset = ax.inset_axes([0.55, 0.1, 0.4, 0.4])
inset.plot(x[:5], y[:5])
inset.set_xlim(0, 4)
inset.set_ylim(0, 16)
ax.indicate_inset_zoom(inset)

print("Inset axes attached to the main one:", len(ax.child_axes))
print("Inset x range:", [float(v) for v in inset.get_xlim()])
print("Inset y range:", [float(v) for v in inset.get_ylim()])
print("Main axes reaches:", max(y))
print("The inset shows only the start:", inset.get_xlim()[1] < max(x))
print("Inset sits inside the main axes:", inset.get_position().x0 > ax.get_position().x0)

# ✅ Expected output:
# Inset axes attached to the main one: 1
# Inset x range: [0.0, 4.0]
# Inset y range: [0.0, 16.0]
# Main axes reaches: 361
# The inset shows only the start: True
# Inset sits inside the main axes: True
The chart this code draws: The inset is a child of the main axes, with its own limits showing only the start of the data
What this code draws, rendered with Matplotlib.

❓ Frequently Asked Questions

Lesson complete — you can zoom and break axes!

You added a zoom-in inset, linked it to its region with indicate_inset_zoom, and hand-built a broken axis from two subplots with diagonal break marks.

Practice quiz

What does ax.inset_axes([x, y, w, h]) create?

  • A new figure window
  • A small inset axes inside the main axes
  • A colorbar
  • A second x-axis

Answer: A small inset axes inside the main axes. inset_axes drops a miniature Axes inside the parent axes.

In what coordinate system are the four inset_axes numbers given?

  • Pixels
  • Data units
  • Inches
  • Axes-fraction coordinates

Answer: Axes-fraction coordinates. [x, y, w, h] are in axes-fraction coordinates of the parent axes.

How do you restrict an inset to a zoom region?

  • Set its xlim and ylim
  • Call inset.zoom()
  • Pass zoom=True
  • Use plt.magnify()

Answer: Set its xlim and ylim. You narrow the inset with axins.set_xlim() and set_ylim() to the region of interest.

What does ax.indicate_inset_zoom(axins) draw?

  • A colorbar
  • A second legend
  • A rectangle and connector lines linking the region to the inset
  • A title

Answer: A rectangle and connector lines linking the region to the inset. It marks the zoom region with a rectangle and connectors to the inset.

When must you set the inset's limits relative to indicate_inset_zoom?

  • After it
  • Before it, so it knows the region
  • It does not matter
  • Only inside a loop

Answer: Before it, so it knows the region. indicate_inset_zoom reads the inset's xlim/ylim, so set them first.

Does Matplotlib have a single built-in broken-axis function?

  • Yes, plt.brokenaxis()
  • Yes, ax.break_axis()
  • Yes, plt.gap()
  • No, you build it from two subplots

Answer: No, you build it from two subplots. There is no one-liner; the recipe uses two subplots sharing an axis.

Which argument links two subplots to share their x-axis for a break?

  • sharex=True
  • linkx=True
  • joinx=True
  • samex=True

Answer: sharex=True. plt.subplots(2, 1, sharex=True) links the x-axes of the stacked panels.

How do you open the visual gap between the two broken-axis panels?

  • Increase the DPI
  • Use a log scale
  • Hide the facing spines with spine.set_visible(False)
  • Call ax.gap()

Answer: Hide the facing spines with spine.set_visible(False). Hiding the bottom spine of the top panel and the top spine of the bottom panel opens the break.

Why pass clip_on=False when drawing the diagonal break marks?

  • To color them red
  • So they are not clipped at the axes edge
  • To make them dashed
  • To rotate them

Answer: So they are not clipped at the axes edge. The marks sit at the axes boundary, so clip_on=False keeps them from being clipped.

When is a broken axis preferable to a log scale?

  • When values span many orders of magnitude smoothly
  • When all values are negative
  • When you want a circular plot
  • When two clusters are separated by a large empty gap

Answer: When two clusters are separated by a large empty gap. A broken axis compresses an empty gap between two clusters without distorting spacing within them.

Continue this course

Frequently asked questions

What is an inset axes and when should I use one?

An inset axes is a small second plot drawn inside the main one, created with ax.inset_axes([x, y, w, h]). The four numbers are in axes-fraction coordinates. Use an inset to zoom into a crowded region while still showing the full picture, so readers see both the overview and the detail in a single figure.

How does indicate_inset_zoom show which region is zoomed?

After you set the inset's xlim and ylim to the zoom region, call ax.indicate_inset_zoom(axins). Matplotlib draws a rectangle on the main axes around that region and connector lines linking it to the inset, so the reader instantly sees where the close-up came from.

Does Matplotlib have a built-in broken axis?

There is no single broken-axis function. The standard recipe is two stacked (or side-by-side) subplots that share an axis, each showing a different range, with the facing spines hidden and small diagonal tick marks drawn to signal the break. It looks like one axis with a gap cut out.

Why use a broken axis instead of a log scale?

A broken axis is best when your data has two clusters separated by a large empty gap — for example most values near 10 and one near 1000. It compresses the empty middle without distorting the spacing within each cluster, which a log scale would. Use a log scale when values span many orders of magnitude smoothly.