More Plot Types: eventplot, broken_barh, stairs

Reviewed & published by Brayan K

Beyond lines and bars, Matplotlib ships specialized plot types — eventplot for timelines, broken_barh for Gantt charts, stairs for step outlines, and bxp for precomputed boxplots.

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 raster event timelines with ax.eventplot(), build Gantt-style rows with ax.broken_barh(), draw step outlines with ax.stairs(), and render boxplots from stats with ax.bxp().

What You'll Learn in This Lesson

1 Timelines With eventplot

ax.eventplot() draws a tick at every position you give it — a raster. Hand it a single array for one row, or a list of arrays for several stacked rows, perfect for comparing when events fired across different sources.

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(3)

# Three sensors, each firing at random times in 0..10
sensors = [np.sort(rng.uniform(0, 10, n)) for n in (12, 8, 15)]

fig, ax = plt.subplots()
ax.eventplot(sensors, colors=["crimson", "teal", "purple"],
             lineoffsets=[1, 2, 3], linelengths=0.8)

ax.set_yticks([1, 2, 3])
ax.set_yticklabels(["Sensor A", "Sensor B", "Sensor C"])
ax.set_xlabel("Time (s)")
ax.set_title("Event Raster")

plt.show()
The chart this code draws: Stack three rows of event ticks with eventplot
What this code draws, rendered with Matplotlib.

What you'll see: three colored rows of vertical tick marks, one per sensor, each tick landing at the moment that sensor fired — an at-a-glance timeline of who triggered when.

2 Gantt Rows With broken_barh

ax.broken_barh(xranges, yrange) draws horizontal bars from (start, width) pairs at a given (y, height). One call can place several segments — gaps and all — on a single row, which is exactly what a Gantt chart needs: one task per row spanning its active periods.

import matplotlib.pyplot as plt

fig, ax = plt.subplots()

# Each row: list of (start, duration) blocks at a (y, height)
ax.broken_barh([(0, 3), (4, 2)], (10, 8),
               facecolors="tab:blue")     # Design
ax.broken_barh([(3, 4)], (20, 8),
               facecolors="tab:orange")   # Build
ax.broken_barh([(7, 2), (10, 3)], (30, 8),
               facecolors="tab:green")    # Test

ax.set_ylim(5, 42)
ax.set_xlim(0, 14)
ax.set_xlabel("Week")
ax.set_yticks([14, 24, 34])
ax.set_yticklabels(["Design", "Build", "Test"])
ax.set_title("Project Gantt Chart")
ax.grid(True, axis="x", linestyle="--", alpha=0.4)

plt.show()
The chart this code draws: Build task rows with broken_barh
What this code draws, rendered with Matplotlib.

3 stairs & Precomputed Boxplots

ax.stairs(values, edges) draws a step outline — the natural partner for np.histogram, which returns counts and bin edges. And when you only have summary statistics, ax.bxp() renders a boxplot from a list of stat dictionaries instead of raw data.

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(0)
data = rng.normal(0, 1, 1000)

# Bin the data ourselves, then draw the outline
counts, edges = np.histogram(data, bins=20)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))

# Left: a filled histogram outline via stairs
ax1.stairs(counts, edges, fill=True, color="slateblue", alpha=0.7)
ax1.set_title("Histogram via stairs()")

# Right: a boxplot from PRECOMPUTED stats with bxp
stats = [{
    "label": "A", "med": 5, "q1": 3, "q3": 7,
    "whislo": 1, "whishi": 9, "fliers": [],
}, {
    "label": "B", "med": 6, "q1": 4.5, "q3": 8,
    "whislo": 2, "whishi": 11, "fliers": [12.5],
}]
ax2.bxp(stats, showfliers=True)
ax2.set_title("Boxplot via bxp()")

fig.tight_layout()
plt.show()
The chart this code draws: Draw a histogram outline and a stats-only boxplot
What this code draws, rendered with Matplotlib.

What you'll see: on the left a filled bell-shaped step outline drawn straight from the histogram counts, and on the right two boxplots built entirely from the median and quartile numbers — no raw samples required.

🎯 Your Turn: Fill in the Blanks

Replace each ___ to raster a single row of event times.

import matplotlib.pyplot as plt

# Times (in seconds) when a button was pressed
presses = [0.5, 1.2, 1.3, 3.0, 4.8, 5.1, 7.7]

fig, ax = plt.subplots()

# Draw a raster of ticks at those times
ax.___(presses, colors="darkgreen")

ax.set_xlabel("Time (s)")
ax.set_title("Button Presses")

# Display the figure
plt.___()

📋 Common Errors & Quick Reference

❌ broken_barh shows one giant bar

You passed an end point as the width. ✅ The tuple is (start, duration) — use the length, not the end coordinate.

❌ stairs: len(values) must equal len(edges) - 1

There is always one more edge than value. ✅ Use the counts, edges straight from np.histogram without trimming.

Each stats dict needs med, q1, q3, whislo, whishi. ✅ Supply all required keys (and fliers as a list).

CallBest For
ax.eventplot(data)Timelines / spike rasters
ax.broken_barh(ranges, yr)Gantt-style schedules
ax.stairs(values, edges)Histogram / step outlines
ax.bxp(stats)Boxplots from precomputed stats

🎯 Mini-Challenge: A Two-Task Schedule

Use broken_barh to chart two tasks, each with a gap, on their own rows.

import matplotlib.pyplot as plt

fig, ax = plt.subplots()

# Task 1 works days 0-2 and 5-7
ax.broken_barh([(0, 2), (5, 2)], (10, 6), facecolors="tab:purple")

# Task 2 works days 2-4 and 8-9
ax.broken_barh([(2, 2), (8, 1)], (20, 6), facecolors="tab:red")

ax.set_ylim(5, 30)
ax.set_xlim(0, 10)
ax.set_yticks([13, 23])
ax.set_yticklabels(["Task 1", "Task 2"])
ax.set_xlabel("Day")
ax.set_title("Two-Task Schedule")

plt.show()
The chart this code draws: A two-row schedule with gaps using broken_barh
What this code draws, rendered with Matplotlib.
import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 3, figsize=(10, 3))

axes[0].stem([1, 2, 3, 4], [3, 1, 4, 2])
axes[1].step([1, 2, 3, 4], [3, 1, 4, 2], where="mid")
axes[2].barh(["a", "b", "c"], [5, 3, 8])

print("Panels:", len(fig.axes))
print("Stem containers:", len(axes[0].containers))
print("Step is drawn as a line:", len(axes[1].lines))
print("Horizontal bars:", len(axes[2].patches))
print("Longest bar:", max(p.get_width() for p in axes[2].patches))
print("Step style:", axes[1].lines[0].get_drawstyle())

# ✅ Expected output:
# Panels: 3
# Stem containers: 1
# Step is drawn as a line: 1
# Horizontal bars: 3
# Longest bar: 8
# Step style: steps-mid
The chart this code draws: Three different plot types and what each leaves behind: a container, a line, and patches
What this code draws, rendered with Matplotlib.

❓ Frequently Asked Questions

Lesson complete — your plotting toolbox grew!

You rastered timelines with eventplot, built Gantt rows with broken_barh, drew step outlines with stairs, and rendered boxplots from precomputed stats with bxp.

Practice quiz

What does ax.eventplot() draw?

  • A filled area
  • A raster of tick marks at given positions
  • A pie chart
  • A 3D surface

Answer: A raster of tick marks at given positions. eventplot draws a tick at every position, forming a raster like a spike train or timeline.

How do you make eventplot show several stacked rows?

  • Call it many times
  • Pass rows=3
  • Use stack=True
  • Pass a list of arrays, one per row

Answer: Pass a list of arrays, one per row. A list of arrays gives eventplot one row of ticks per array.

What does ax.broken_barh take as its first argument?

  • A list of (start, width) pairs
  • A single number
  • Two y-values
  • A colormap

Answer: A list of (start, width) pairs. broken_barh's first argument is a list of (start, width) horizontal segments.

Why is broken_barh ideal for Gantt charts?

  • It logs the axis
  • It draws pies
  • One call can place several segments with gaps on one row
  • It auto-sorts tasks

Answer: One call can place several segments with gaps on one row. Each task occupies one row and may have disconnected active periods, which broken_barh handles in one call.

What does ax.stairs(values, edges) draw?

  • A scatter plot
  • A step outline, like a histogram shape
  • A smooth curve
  • A pie chart

Answer: A step outline, like a histogram shape. stairs draws the outline of a step function from edges and values.

Which NumPy function pairs naturally with ax.stairs()?

  • np.linspace
  • np.sort
  • np.mean
  • np.histogram

Answer: np.histogram. np.histogram returns counts and bin edges, exactly what stairs(counts, edges) needs.

What does ax.bxp() take as input?

  • A list of stat dictionaries (median, quartiles, whiskers)
  • Raw observations
  • A colormap
  • A list of images

Answer: A list of stat dictionaries (median, quartiles, whiskers). bxp renders a boxplot from precomputed statistics, not raw data.

When would you choose ax.bxp() over ax.boxplot()?

  • When data has no outliers
  • When you want a log axis
  • When you only have precomputed stats, not raw data
  • When plotting a single point

Answer: When you only have precomputed stats, not raw data. boxplot computes quartiles from raw data; bxp accepts the summary stats you already have.

What relationship must hold between values and edges in ax.stairs()?

  • They must be equal length
  • values can be empty
  • edges must be sorted descending
  • len(edges) is one more than len(values)

Answer: len(edges) is one more than len(values). There is always one more edge than value, just like histogram bins.

In a broken_barh tuple, what does the second number represent?

  • The y-position
  • The duration (width), not the end point
  • The color
  • The row height

Answer: The duration (width), not the end point. Each (start, width) tuple uses a duration as the second value, not an end coordinate.

Continue this course

Frequently asked questions

What is an eventplot used for?

ax.eventplot() draws a raster of vertical (or horizontal) tick marks at a list of positions — think spike trains in neuroscience, event timestamps, or a timeline of incidents. Pass a list of arrays and each becomes its own row of ticks, so you can compare when events happened across several series at a glance.

How is broken_barh different from a normal bar chart?

ax.broken_barh() draws horizontal bars defined by (start, width) pairs at a chosen y-position, and a single call can place several disconnected segments on one row. That makes it ideal for Gantt-style timelines where each task occupies one row and may have gaps, rather than one bar per category rising from a baseline.

What does ax.stairs() draw and how does it differ from a step plot?

ax.stairs() draws the outline of a step function from edges and values, the same shape a histogram uses. It is the modern replacement for drawstyle='steps' and pairs naturally with np.histogram output, letting you draw or fill a histogram outline without re-binning the data.

When would I use ax.bxp() instead of ax.boxplot()?

ax.boxplot() takes raw data and computes the quartiles for you. ax.bxp() takes the statistics you have already computed — median, quartiles, whiskers — as a list of dictionaries. Use bxp when the numbers come from a database or a summary table and you never had the raw observations to hand to boxplot.