Box and Violin Plots

Comparing many distributions at once, and what each summary throws away.

Overview

What a box plot is

Box plots and violin plots summarise the distribution of a numeric variable. They answer the question "how is this data spread?" for one or more groups, drawn side by side for comparison.

A box plot reduces each group to five summary statistics. The median, the first and third quartiles, and the whisker ends. That compression is its strength and its weakness. It shows the centre, the spread and the outliers in a compact form. It hides the shape of the distribution, the number of points, and any gaps or clusters.

A violin plot adds shape information. It draws a smoothed density estimate on each side, so you can see whether the distribution is symmetric, skewed, bimodal or uniform. It uses more space and is harder to read without practice, but it shows things a box plot cannot.

This page covers both. It starts with box plots: what the five numbers are, when to use them, what they hide. Then violin plots: what the shape shows, how the kernel bandwidth affects it, and the internals worth knowing. It also covers alternatives: swarm plots, strip plots and combinations that show both the summary and the individual points.

The choice between box and violin depends on the audience and the data. Box plots are widely understood and compact. Violin plots are less familiar but more informative. For a technical audience with continuous data, violins are usually better. For a broad audience or a small number of groups, box plots are clearer.

Sample size matters for both. A box plot of five points is misleading because the quartiles are not stable. A violin plot of five points is worse because the density estimate is fiction. Showing the individual points alongside the summary catches this. A swarm plot or a jittered strip plot adds the raw data without much extra space.

Ordering the groups is worth thinking about. Alphabetical order is rarely useful. Sorting by median makes the ranking visible. Grouping by a higher-level category makes comparisons within and across groups easier. The page covers both.

Five numbers, drawn:

The box spans the first to the third quartile. It holds the middle half of the data. Its height is the interquartile range.

The line inside is the median — not the mean.

The whiskers reach the furthest data point within 1.5×IQR of the box. They aren't a fixed multiple of anything. They stop at real findings.

Points beyond that are drawn on their own as outliers.

That 1.5 is a rule, chosen so that roughly 0.7% of most times spread data falls outside. It has no deeper justification. Matplotlib lets you change it with whis=.

Worth knowing

A box is five numbers: quartiles, median, and whiskers reaching the furthest point within 1.5×IQR. Everything beyond is drawn individually.
Box plots exist to compare many groups at once in the space one histogram would take.
Any distribution with the same five numbers draws the same box — including a bimodal one with a hole in the middle.
A violin shows the shape, at the cost of a bandwidth you did not choose, and it extends past the observed data.
Overlaying the points with jitter restores sample size and shape; showfliers=False stops outliers being drawn twice.
vert=False and sorting by median apply the same readability arguments as barh.

Box and Violin Plots

Checking many spreads at once, and what each summary throws away.

What the box contains

Five numbers, and everything beyond the whiskers drawn individually.

example_01.pymatplotlib
Output

Comparing groups is what it is for

Many distributions in the space one histogram would take.

example_02.pymatplotlib
Output

What the box hides

Two very different distributions can produce the same box.

example_03.pymatplotlib
Output

Violins show the shape

At the cost of a smoothing parameter you did not choose.

example_04.pymatplotlib
Output

Showing the points as well

The best of both, when the sample is small enough.

example_05.pymatplotlib
Output

Horizontal, and ordered

The same readability arguments as bar charts apply.

example_06.pymatplotlib
Output

What they are for

Checking many groups.

Five histograms take five panels and need the reader to move between them. Five boxes sit side by side on one axis, sharing a scale, and the compare is direct.

That is the whole arg, and it's a strong one. For one spread, a histogram is better. For eight, a box plot is much better. one spot around three the answer changes.

patch_artist=True makes the boxes filled rather than outlines. This Is needed before set_facecolor does anything. A common small frustration.

What they hide

A box plot is a summary, and each summary discards.

Any spread sharing those five numbers makes an same box. That includes a bimodal spread. Two clusters with a gap in the middle. Whose median sits in the empty space where no data is.

The third editor draws just that: two spreads with nearly same boxes and fully other shapes.

This isn't a hypothetical failure. Bimodality often means two groups have been mixed. This Is often the most key thing in the data. The box plot conceals it just right.

The hands-on rule: look at a histogram of each group once before settling on box plots for the compare.

Violin plots

A violin shows an guessed density, mirrored. The width at any height is how much data is around that value. It shows the bimodality a box hides.

Two honest caveats.

It's a kernel density guess, with a bandwidth that smooths the data. That param has just the arbitrariness of a histogram's bin count. Matplotlib picks it for you. Too much smoothing merges peaks. Too little invents them.

It extends past the seen data at both ends. The kernel has tails. A violin of a strictly plus quantity will happily show density below zero. This Is a claim the data doesn't support.

showmedians=True adds the median line. The defaults draw the extrema.

Showing the points

For small samples, the best display is often both. A box or violin for the summary. The single points with jitter on top.

showfliers=False blocks the outliers being drawn twice, once by the box and once by the scatter.

zorder=3 on the scatter puts the points above the box.

Below roughly fifty points per group this is strictly more helpful than either element alone. You get the quartiles and the actual sample. Above a few hundred, the points overplot and the summary is doing the work.

Layout

vert=False draws them flatly. This Lets long group names read most times. The same arg that makes barh preferable to bar.

Sorting the groups by median makes the ranking direct rather than a thing the reader assembles.

And as with any grouped compare, the y axis must be shared. This It's by build here. That is part of why the display works.

Notches and confidence

notch=True draws a waist around the median whose width approximates a 95% trust interval for it.

The heuristic it enables is that if two notches don't share, the medians differ much. That is roughly true and depends on assumptions the plot doesn't state.

Two hands-on problems. On small samples the notch can extend beyond the box, making a folded shape that looks like a rendering error. And the heuristic is just the share thinking that is unreliable for error bars, applied to a other statistic.

handy when checking many groups quickly. Not a substitute for a test.

Ordering and orientation

The same args as bar charts apply.

Sort by median unless the group order means a thing. Time, size, an test sequence.

Use vert=False when the labels are words.

And keep the y axis shared. This Happens on its own here because all groups share one axes. Is just why the display works for compare.

Styling the parts

boxplot returns a dict of the artists, keyed by "boxes", "whiskers", "caps", "medians", "fliers":

bp = ax.boxplot(data, patch_artist=True)
for b in bp["boxes"]:
    b.set(facecolor="#a8dadc", edgecolor="0.3")
for m in bp["medians"]:
    m.set(color="crimson", linewidth=2)

patch_artist=True is needed before boxes can be filled. Without it they're unfillable outlines and set_facecolor does nothing, silently.

The property dicts — medianprops, boxprops, whiskerprops, flierprops — do the same at call time and are often tidier.

Violin internals

violinplot returns a dict too, with "bodies" and optional "cmedians", "cmeans", "cbars".

By default it draws the extrema and no median. This Is an odd choice. showmedians=True is nearly always wanted.

widths controls the max thickness, and bw_method the smoothing. A number, or "scott" / "silverman". Halving the bandwidth roughly doubles the visible detail and the visible noise.

Because the two halves are mirror images, one half is redundant. A half violin paired with the raw points on the other side uses the space better. Is built by clipping the body path. More work than it sounds. Worth it for a figure that will be published.

Choosing between them

Box — many groups, when the quartiles are the summary you want and shape isn't expected to be notable.

Violin — when shape matters and the samples are large enough for a density guess to mean a thing, roughly a hundred per group.

Strip or swarm — small samples. Here The single values are the honest display.

Box plus points — the all-round answer under about fifty per group.

Histogram grid — when the shapes are the subject and there are few enough groups to give each a panel.

The mistake is treating the box plot as the default for all spread compare. This Is how bimodality goes unnoticed.

Outliers

The points beyond the whiskers are labelled outliers. The label is doing more work than the statistics support.

They're simply points more than 1.5×IQR from the box. On most times spread data about 0.7% of findings qualify. A sample of a thousand makes seven "outliers" that are nothing of the kind.

On skewed data the rule flags far more, all on one side. This Is a property of the spread rather than of the points.

So: a point beyond a whisker is worth looking at and isn't evidence of an error. showfliers=False hides them when they're distracting. whis=(5, 95) changes the rule to percentiles. This Is often more readable.

Sample size

A box plot looks the same for twelve findings and twelve thousand. This Is a real weakness when groups differ in size.

Three ways to show it:

Width scaled to n — widths= accepts a list. Scaling by the square root of the count is standard.

The count in the tick label — "group a (n=12)".

The points overlaid, which shows the sample size right.

Without one of these, a group of eight and a group of eight hundred are presented as equally solid. The reader has no way to know.

Ordering and grouping

With more than a handful of groups, the setup is most of the chart.

Sort by median unless the groups have a natural order. It turns a search into a reading.

Group related groups together and split the groups with space. This spots= allows. Passing [1, 2, 3, 5, 6, 7] leaves a gap between two clusters.

Use colour for the grouping, not for the single boxes. The colour carries the structure rather than repeating the labels.

Consider a link line — the total median, a target — drawn across the whole axes. This Turns each box from an isolated summary into a compare.

With those, twenty boxes on one chart remains readable. Without them, six is already hard.

Box and Violin Plots, in summary

A box is five numbers, and whiskers reach the furthest point within 1.5×IQR rather than a fixed multiple.

The display exists to compare many groups at once. This Is what it does better than anything else.

It hides shape, and namely hides bimodality. Two groups mixed together give a box that looks plain.

A violin restores the shape and adds a bandwidth you didn't choose, plus tails extending past the seen data.

Under about fifty points per group, showing the points with jitter is more helpful than either.

And patch_artist=True, showfliers=False and vert=False are the three args that come up most. Fillable boxes, no double-drawn outliers. Flat layout for wordy labels.

Beeswarm and the alternatives

A strip plot with random jitter shows each point and lets some of them share by chance.

A beeswarm places the points deterministically so none share. This Reads better and isn't built into matplotlib. Seaborn's swarmplot is the usual route. It refuses to plot when there are too many points to place. This Is a fair failure.

A rain cloud mixes three: a half violin for the shape, a box for the summary. The points below. It's the most full display of a spread compare and takes the most space. This Is the trade.

For most work the sorting is: points if the sample is small, box if there are many groups, box plus points in between. A violin when the shape is truly the question and the samples are large enough to guess it.

What matters more than the choice is knowing what each one hides. This Is the theme of this whole module.

A closing note on Box and Violin Plots

each spread display is a compromise between completeness and comparability.

The points show all and compare badly past a few groups. The box checks many groups and hides the shape. The violin restores the shape and adds a smoothing choice. The histogram shows one spread well and a few badly.

There's no display that does all of it. This Is why the handy skill is knowing what each one discards rather than having a favourite.

The exact thing worth remembering: a box plot can't show bimodality. Bimodality often means two groups have been mixed. Which is often the most key fact about the data. Looking at a histogram of each group once, before settling on boxes for the compare, costs a minute and blocks the failure this module is mostly about.

Box and Violin Plots: the short version

Both displays trade completeness for comparability. Both hide a thing exact. The box hides shape, the violin hides its own smoothing choice.

Knowing what each discards is more handy than preferring one. Looking at a histogram of each group once before choosing is what blocks the bimodality failure this module exists to show.

Questions people ask about Box and Violin Plots

What does a box plot show? The median, the quartiles as the box, and whiskers at 1.5 times the interquartile range. Points beyond the whiskers are drawn as outliers.

When should I use a violin instead? When the shape matters, such as two peaks. A violin shows the density, which a box plot cannot.

What do the whiskers mean? By default, the farthest data point within 1.5 times the IQR of the box. Some plots use min and max or percentiles, so check.

How do I show the individual points too? Overlay a jittered scatter or a strip plot. This helps most when each group has few observations.

How do I make horizontal boxes? Pass vert=False to ax.boxplot, or orientation='horizontal' in recent versions.

Are violins reliable for small groups? No. The smoothed density can imply shape that is not there. Use a box plot or show the points.

Reading the code back

A box plot is one call with three args worth passing. Patch_artist to make the boxes fillable, showfliers to control the outliers. Vert to choose the orientation. The choices before it are which groups, in what order. Whether the points should be shown as well - and those are what find whether the chart works.

Check yourself

0 of 4

Answer without scrolling back up.

  1. What do the whiskers on a matplotlib box plot reach?

  2. What can a box plot completely hide?

  3. What is the cost of a violin plot?

  4. Why pass `showfliers=False` when overlaying the raw points?

Cheat sheet

Box and Violin Plots

Draw box plots and violin plots side by side in matplotlib. Compare what each summary shows and what each one hides about the distribution.

MATPLOTLIB · vizlearn.in/matplotlib/box_and_violin.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.