Box & Violin Plots

Reviewed & published by Brayan K

A box plot is a compact chart that summarizes a distribution with its median, quartiles, and outliers, while a violin plot wraps that same summary in a smoothed density curve to reveal the shape of the data.

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 draw box plots for several groups at once, customize whiskers and outliers, switch to violin plots, and learn to read quartiles and spread at a glance.

What You'll Learn in This Lesson

1 Your First Box Plot

A box plot turns a column of numbers into a five-number summary. The box spans the middle 50% of the data — from the first quartile (Q1) to the third quartile (Q3) — and the line inside the box marks the median. The whiskers reach out to the smallest and largest values that aren't considered outliers. Pass one array to ax.boxplot() to get started.

import matplotlib.pyplot as plt
import numpy as np

# Reproducible random data: 200 exam scores
rng = np.random.default_rng(42)
scores = rng.normal(loc=72, scale=10, size=200)

fig, ax = plt.subplots()
ax.boxplot(scores)

ax.set_title("Exam Scores: One Box Plot")
ax.set_ylabel("Score")
ax.set_xticklabels(["Class A"])

plt.show()
The chart this code draws: Summarize 200 scores with one box
What this code draws, rendered with Matplotlib.

What you'll see: a single vertical box centered near 72. The orange line inside marks the median, the box edges sit around 65 and 79, and the whiskers stretch out to roughly 45 and 100. A few stray circles beyond the whiskers flag the unusual scores.

2 Comparing Groups, Notch & Outliers

The real power of box plots shows up when you compare several groups. Pass a list of arrays and Matplotlib draws one box per array. Name each box with tick_labels. Two handy options: notch=True pinches the box around the median to hint at a confidence interval, and showfliers=False hides the outlier dots when they clutter the view.

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(7)

# Three branches with different spreads
north = rng.normal(50, 5, 100)
south = rng.normal(60, 12, 100)
east  = rng.normal(55, 8, 100)

fig, ax = plt.subplots()
ax.boxplot([north, south, east],
           tick_labels=["North", "South", "East"],
           notch=True,
           showfliers=True)

ax.set_title("Daily Sales by Branch")
ax.set_ylabel("Units Sold")

plt.show()
The chart this code draws: One box per branch, with notches
What this code draws, rendered with Matplotlib.

What you'll see: three boxes side by side. South is the tallest box (largest spread) and sits highest, North is short and compact, and the pinched notches near each median make it easy to judge whether the typical values really differ between branches.

3 Violin Plots: Seeing the Shape

A box plot hides one thing: the shape of the distribution. Two groups can share the same median and quartiles yet look completely different — one bell-shaped, one with two peaks. A violin plot fixes this by drawing a mirrored density curve. Call ax.violinplot() and turn on showmedians=True to mark the center.

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(3)

# One bell-shaped group, one with two peaks
single_peak = rng.normal(50, 8, 300)
two_peaks = np.concatenate([rng.normal(35, 4, 150),
                            rng.normal(65, 4, 150)])

fig, ax = plt.subplots()
parts = ax.violinplot([single_peak, two_peaks],
                      showmedians=True)

ax.set_xticks([1, 2])
ax.set_xticklabels(["One Peak", "Two Peaks"])
ax.set_title("Violin Plots Reveal Shape")
ax.set_ylabel("Value")

plt.show()
The chart this code draws: See structure a box plot would hide
What this code draws, rendered with Matplotlib.

What you'll see: two violin shapes. The left one bulges in the middle like a single bell, while the right one pinches in the center and bulges at top and bottom — clearly showing two clusters that a box plot alone would have flattened into one box.

🎯 Your Turn: Fill in the Blanks

Replace each ___ to compare two teams with a box plot and hide the outliers.

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(1)
team_a = rng.normal(80, 6, 120)
team_b = rng.normal(85, 10, 120)

fig, ax = plt.subplots()

# Draw one box per team, hide outlier dots
ax.___([team_a, team_b],
       tick_labels=["Team A", "Team B"],
       showfliers=___)

# Label the y-axis
ax.___("Productivity Score")

plt.show()

📋 Common Errors & Quick Reference

❌ Only one box appears for many groups

You passed a flat array instead of a list of arrays. Wrap your groups in a list: ax.boxplot([a, b, c]), not ax.boxplot(a, b, c).

Newer Matplotlib renamed the argument. Use tick_labels=[...] for box plots instead of labels=[...].

❌ Violin labels land on the wrong boxes

ax.violinplot() doesn't take labels. Set them yourself with ax.set_xticks([1, 2]) then ax.set_xticklabels([...]).

CallWhat It Does
ax.boxplot([a, b])One box per array
tick_labels=[...]Name each box
notch=TruePinch box around median
showfliers=FalseHide outlier dots
ax.violinplot(..., showmedians=True)Density curve with median

🎯 Mini-Challenge: Commute Times

Compare morning commute times across three transit modes with a horizontal box plot, then add a violin plot beside it on a second axis.

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(99)
bus   = rng.normal(35, 8, 150)
train = rng.normal(28, 4, 150)
bike  = rng.normal(40, 12, 150)
data = [bus, train, bike]
names = ["Bus", "Train", "Bike"]

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

# Left: horizontal box plot
ax1.boxplot(data, tick_labels=names, orientation="horizontal", showfliers=False)
ax1.set_title("Box Plot")
ax1.set_xlabel("Minutes")

# Right: violin plot of the same data
ax2.violinplot(data, showmedians=True)
ax2.set_xticks([1, 2, 3])
ax2.set_xticklabels(names)
ax2.set_title("Violin Plot")
ax2.set_ylabel("Minutes")

fig.suptitle("Morning Commute Times by Mode")
plt.tight_layout()
plt.show()
The chart this code draws: Box plot and violin plot side by side
What this code draws, rendered with Matplotlib.
import matplotlib.pyplot as plt

morning = [12, 15, 14, 18, 40, 13, 16]
evening = [22, 25, 24, 28, 21, 26, 23]

fig, ax = plt.subplots()
bp = ax.boxplot([morning, evening])
# set_xticks rather than boxplot's own label argument, which was renamed
# from labels= to tick_labels= in matplotlib 3.9 — this works on both.
ax.set_xticks([1, 2], labels=["morning", "evening"])

print("Boxes:", len(bp["boxes"]))
print("Whisker pairs:", len(bp["whiskers"]) // 2)
print("Outliers marked:", sum(len(f.get_ydata()) for f in bp["fliers"]))
print("Morning median:", sorted(morning)[len(morning) // 2])
print("Evening median:", sorted(evening)[len(evening) // 2])
print("Tick labels:", [t.get_text() for t in ax.get_xticklabels()])

# ✅ Expected output:
# Boxes: 2
# Whisker pairs: 2
# Outliers marked: 1
# Morning median: 15
# Evening median: 24
# Tick labels: ['morning', 'evening']
The chart this code draws: Count the boxes, the whisker pairs and the outliers matplotlib decided to mark
What this code draws, rendered with Matplotlib.

❓ Frequently Asked Questions

Lesson complete — you can summarize distributions!

You drew single and grouped box plots, read the median, quartiles, and outliers, tuned the notch and flier options, and switched to violin plots to expose the shape of your data.

Practice quiz

Which method draws a box plot?

  • ax.boxplot()
  • ax.box()
  • ax.whisker()
  • ax.quartile()

Answer: ax.boxplot(). ax.boxplot() draws the five-number-summary box plot.

What does the line inside the box mark?

  • The mean
  • The median
  • The maximum
  • The first quartile

Answer: The median. The line inside the box marks the median (the middle value).

The box of a box plot spans which range?

  • min to max
  • mean plus or minus one standard deviation
  • Q1 to Q3 (the interquartile range)
  • the 5th to 95th percentile

Answer: Q1 to Q3 (the interquartile range). The box runs from the first quartile (Q1) to the third quartile (Q3).

How do you draw one box per group from several arrays?

  • Call boxplot once per array
  • Pass them as separate positional args
  • Use ax.violinplot only
  • Pass a list of arrays to ax.boxplot()

Answer: Pass a list of arrays to ax.boxplot(). ax.boxplot([a, b, c]) draws one box per array in the list.

Which argument hides the outlier dots in ax.boxplot()?

  • showfliers=False
  • notch=False
  • outliers=None
  • hideoutliers=True

Answer: showfliers=False. showfliers=False hides the individual outlier markers (fliers).

In Matplotlib 3.9+, which argument names each box?

  • names
  • tick_labels
  • categories
  • xlabels

Answer: tick_labels. tick_labels names each box (older versions used labels).

What does a violin plot add that a box plot does not show?

  • The exact median value
  • The number of points
  • A smoothed density curve revealing shape
  • The axis labels

Answer: A smoothed density curve revealing shape. A violin plot draws a mirrored density curve, exposing the distribution's shape.

Which method draws a violin plot?

  • ax.density()
  • ax.shape()
  • ax.kde()
  • ax.violinplot()

Answer: ax.violinplot(). ax.violinplot() draws violin plots.

What do the dots beyond the whiskers represent?

  • Outliers (fliers) past 1.5x the IQR
  • The medians
  • The means
  • Grid lines

Answer: Outliers (fliers) past 1.5x the IQR. Those fliers are outliers more than 1.5x the IQR beyond the box.

Since ax.violinplot() takes no labels argument, how do you name the violins?

  • Pass labels= to violinplot
  • set_xticks then set_xticklabels
  • Use tick_labels=
  • Add a legend automatically

Answer: set_xticks then set_xticklabels. Set positions with ax.set_xticks([1,2]) then ax.set_xticklabels([...]).

Continue this course

Frequently asked questions

What is the difference between a box plot and a violin plot?

A box plot summarizes a distribution with five numbers — the minimum, first quartile, median, third quartile, and maximum — drawn as a box with whiskers. A violin plot shows the same idea but adds a smoothed density curve on each side, so you can also see whether the data is bunched up, spread out, or has two peaks. Box plots are compact and precise; violin plots reveal shape.

How do I draw box plots for several groups at once?

Pass a list of arrays to ax.boxplot(). Each array becomes its own box positioned along the x-axis. Set the tick labels with the tick_labels argument (or labels in older Matplotlib) so each box is named. This makes it easy to compare distributions side by side.

What do the dots beyond the whiskers mean?

Those dots are outliers — points that fall more than 1.5 times the interquartile range beyond the box. Matplotlib draws them as individual markers called fliers. You can hide them by passing showfliers=False to ax.boxplot() if they clutter the chart.

When should I use a box or violin plot instead of a histogram?

Reach for a box or violin plot when you want to compare the distributions of several groups in one figure. A histogram shows one group in detail, but stacking many histograms gets messy. Box and violin plots place each group's summary side by side, so differences in median and spread jump out instantly.