Library / SDK
bendichter/brokenaxes avatar
bendichter/brokenaxes

brokenaxes: matplotlib plots with a gap cut into the axes

Create matplotlib plots with broken axes

573 stars47 forksPythonMIT

At a glance

What is it?
brokenaxes is a single-module matplotlib helper that draws an axis with a break in it, so you can show two disjoint ranges of data side by side without compressing the empty middle. It is one file, one dependency, and the author includes a warning about how easily this technique can mislead.
Who is it for?
Adopt brokenaxes if you routinely need to show a discontinuous range in a matplotlib figure and want the standard broken axis look without assembling several subplots by hand. Do not adopt it for publication figures without thinking, because the author himself warns that the technique can be used to manipulate and mislead, and the visual effect of a break is to hide the size of the gap.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What a broken axis is for

brokenaxes makes matplotlib plots with breaks in the axes for showing data across a discontinuous range.

The problem it solves is the one where your data has a gap in it. A spectrum with a band of absorption in the middle, a latency measurement with a pause in the middle, a time series that skips a weekend. Drawn normally, the empty middle takes up as much space as the data, and the interesting parts get squashed into the edges.

There are three conventional answers. You can plot against a transformed axis where the gap is compressed, but the spacing on the axis no longer means anything, so the reader has to be told. You can use two stacked panels with a shared x axis, which is honest but takes twice the vertical space and is a lot of matplotlib plumbing. Or you can break the axis, which is what this library does: it draws the data ranges you specify as separate axes with a visual break, usually diagonal slashes, between them.

The library is a single module, brokenaxes.py, with matplotlib as its only runtime dependency. It is on PyPI as brokenaxes, the licence is MIT, the version in the manifest is 0.6.2, and the last push was on 2026-04-17. The repository publishes no GitHub releases, and the topic tags are matplotlib, plotting, python and visualization.

The feature list is short and each item saves specific work

The features list in the README is worth reading as a checklist of what you would otherwise have to build.

It breaks x and y axes, and it supports multiple breaks on a single axis, so the two-range case and the many-range case are both covered. It automatically scales axes according to relative ranges, which is the part that would be most tedious by hand, since the physical size of each segment has to be proportional to the data it contains or the plot lies about the relative sizes of the segments. It plots multiple lines, so you are not limited to a single series.

Then there are the three details that separate a usable library from a demo. The legend is positioned relative to the entire broken axes object, so a legend that would overlap a sub-panel in a naive implementation does not. The x and y labels are centered to the entire plot rather than to any one segment. And the brokenaxes object can be made a subplot itself with matplotlib.GridSpec.subplot_spec, which means it composes with the rest of your figure instead of demanding the whole canvas.

Two more capabilities matter for real data. The limits may be datetime.datetime objects, so you can break a time axis. And log scales are supported, with xscale and yscale parameters, which is the case where the automatic scaling has to do the most work.

The construction is one call with ranges and spacing

The basic usage is a single constructor call, then normal plotting methods on the returned object.

python
import matplotlib.pyplot as plt
from brokenaxes import brokenaxes
import numpy as np

fig = plt.figure(figsize=(5, 2))
bax = brokenaxes(xlims=((0, .1), (.4, .7)), ylims=((-1, .7), (.79, 1)), hspace=.05)
x = np.linspace(0, 1, 100)
bax.plot(x, np.sin(10 * x), label='sin')
bax.plot(x, np.cos(10 * x), label='cos')
bax.legend(loc=3)
bax.set_xlabel('time')
bax.set_ylabel('value')

The ranges are given as tuples of tuples, one inner tuple per segment, so xlims=((0, .1), (.4, .7)) means two x segments. The ylims argument does the same for the vertical axis, and in this example both axes are broken. hspace controls the gap between the segments, and .05 is a tight one.

What comes back is an object that behaves like an axes for the purposes of plotting. You call plot on it, you call legend, you call set_xlabel and set_ylabel, and the library maps those onto the right underlying pieces. The legend call and the two label calls are the ones to notice, because they are the two that a hand-built version of this gets wrong.

The value that has to be right is the range data, since the segment sizes follow from it. Get the ranges wrong and the plot is wrong in a way that still looks like a plot.

Subplots, log scales and datetime axes

The three more advanced examples in the README cover the cases where a broken axis stops being simple.

For subplots, the object takes a subplot_spec, so you build a GridSpec and hand one cell to brokenaxes. The example makes a two row grid, puts a broken sine plot in the first cell and a broken histogram in the second, and each brokenaxes instance is created with its own subplot_spec argument.

For log scales, the constructor takes xscale and yscale and the example uses a loglog plot over a logspace range with breaks at 500 and 600 in both directions. The grid calls in that example are worth noticing, because bax.grid(axis='both', which='major') and the minor grid with a dashed style and reduced alpha are how you keep a log plot readable once the axes are split.

For datetime limits, you pass datetime.datetime objects in the xlims tuple, and the example then does the fiddly part by hand. It calls fig.autofmt_xdate() to rotate the date labels, then removes the diagonal handles and redraws them, and finally it loops over bax.axs to set the major formatter on each inner axis with mdates.DateFormatter('%Y-%b-%d').

That last loop is the clearest statement of the library's boundary. The formatter has to be applied per inner axes object, because the library does not own them once it has laid them out.

Beyond the supported features means reaching into bax.axs

There is a section titled How do I do more, and it explains the internals well enough to be useful.

Brokenaxes works by creating a number of smaller axes objects, with the positions and sizes of those axes dictated by the data ranges used in the constructor. Those individual axes are stored as a list in bax.axs, and most customizations will require accessing those inner axes objects.

There is also a larger invisible axes object, bax.big_ax, which spans the entire brokenaxes region and is used for things like x and y axis labels which span all of the smaller axes.

So there are two objects to know about and they do different jobs. bax.axs is the list of real, visible axes, one per segment, and it is where you go when you need to set a formatter, adjust a tick, or change a spine. bax.big_ax is the invisible full-region axes, and it is where the spanning labels live.

The text annotation example in the README is the simplest demonstration that the object takes ordinary axes methods. bax.text(0.5, 0.5, "hello") puts text at the given coordinates, and because the object presents itself as an axes, the usual methods work on it.

The consequence of this design is worth stating plainly. The library handles the layout, the scaling, the break marks and the legend placement, and it hands you the raw axes for everything else. That is a reasonable line to draw for a single file helper, and it means anything the library does not anticipate is still possible, just at a lower level.

The author's own advice is that broken axes can mislead

The Life advice section of the README is unusual and worth quoting rather than paraphrasing. Any data visualization technique can be used to elucidate trends in the data, but can also be used to manipulate and mislead. The latter is particularly true for broken axes plots, so please try to use them responsibly.

The warning is not decorative. A broken axis compresses the gap, and the visual size of each segment then tells the reader something different from the numeric size of the range it covers. A reader glancing at a broken axis plot can reasonably conclude that the two segments are adjacent, when in fact the missing range might be larger than either of them. The diagonal slashes are a convention that says something is missing, but they are a small mark in a small space.

The other, subtler problem is the automatic scaling. The library scales the axes according to relative ranges, which is exactly the right thing to do, and exactly the thing that makes a misleading plot possible if you choose the ranges to suit the story. The library cannot tell the difference between an honest break and a dishonest one.

So the sensible rule is the one the author states. Use the technique when the gap is genuinely uninteresting, such as a region of the spectrum that carries no signal, and label the ranges so the reader knows what is not shown. The library is free to use, and the guidance about responsible use is the price of admission for a tool whose default effect is to compress an absence.

There is also a gallery invitation. If you make a plot with this tool that you are proud of, send a png and the code, and the author will add it to the gallery.

Editorial conclusion

Adopt brokenaxes if you routinely need to show a discontinuous range in a matplotlib figure and want the standard broken axis look without assembling several subplots by hand. Do not adopt it for publication figures without thinking, because the author himself warns that the technique can be used to manipulate and mislead, and the visual effect of a break is to hide the size of the gap. Before you rely on it, check that your matplotlib is newer than 3.6, and remember that the library keeps its inner axes in a list, so anything beyond the supported features means reaching into bax.axs.

Frequently asked questions

How do I install and use brokenaxes?

Install it with `pip install brokenaxes`, which requires matplotlib newer than 3.6. Then create the object with `brokenaxes(xlims=((0, .1), (.4, .7)), ylims=((-1, .7), (.79, 1)), hspace=.05)` and plot on the returned object, which behaves like an axes for plot, legend and label calls.

Can brokenaxes be used inside a GridSpec subplot?

Yes. The brokenaxes object can be made a subplot itself with matplotlib.GridSpec.subplot_spec, so you build a GridSpec and pass a cell to the constructor. The README example puts a broken sine plot in one cell and a broken histogram in the other.

How do I customise a brokenaxes plot beyond the supported features?

Reach into the inner axes. The smaller axes objects are stored as a list in bax.axs, and there is a larger invisible axes object bax.big_ax spanning the whole region that carries the x and y labels. Setting a date formatter on each inner axis is the pattern the datetime example shows.

How are brokenaxes plots tested?

The project uses pytest-mpl to check that the plots are created correctly. Run `pytest --mpl --mpl-baseline-path test_baseline test.py` from the root to test, and `pytest --mpl-generate-path test_baseline test.py` to generate new baseline plots. On a headless server you may need to set MPLBACKEND to Agg.

Official sources

  1. bendichter/brokenaxes on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bendichter-brokenaxes.svg)](https://hysenlabs.com/projects/bendichter-brokenaxes)

Was this useful?