Written and maintained by CASRAI Editorial Board
Last updated
Short answer: in Matplotlib a box plot is drawn with Axes.boxplot. Pass one array for one box, a list of arrays for one box per group, or a 2D array for one box per column. This page is for researchers who need a box plot for a figure in a paper, and it covers the call, the whisker definition, grouped data from a CSV, per-box colours, and the argument changes between Matplotlib releases that older tutorials get wrong. The same function answers the searches ‘boxplot matplotlib’, ‘boxplot python’ and ‘box plot python’. Matplotlib documentation checked 2026-11-07.
What a box plot in Matplotlib is used for in research
A box plot summarises the distribution of a numerical variable per group: the box spans the first to third quartile, a line marks the median, whiskers extend from the box, and points beyond the whiskers are drawn individually as fliers. In research it is used to compare groups side by side (treatment arms, sites, batches) without assuming a normal distribution. For the reading rules themselves, see the guides on the IQR in a box plot and box-and-whisker plots compared.
Install or access Matplotlib
The Matplotlib installation page lists these official routes (read 2026-11-07):
python -m pip install -U matplotlib
conda install -c conda-forge matplotlib
The same page also lists pixi add matplotlib and uv add matplotlib, and suggests --prefer-binary if pip tries to compile from source. More detail is in the guide on installing Matplotlib with pip. Import it as shown in import matplotlib.pyplot as plt.
Step by step: draw a box plot
1. One box (boxplot python, the minimal case)
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng(0)
values = rng.normal(loc=10, scale=2, size=100)
fig, ax = plt.subplots()
ax.boxplot(values)
plt.show()
Per the Axes.boxplot reference, x may be a 1D array (one box), a sequence of 1D arrays (one box each), or a 2D array (one box per column).
2. Several groups with labels
a = rng.normal(10, 2, 80)
b = rng.normal(12, 3, 80)
c = rng.normal(9, 1, 80)
fig, ax = plt.subplots()
ax.boxplot([a, b, c], tick_labels=['A', 'B', 'C'])
ax.set_ylabel('Measurement')
plt.show()
3. Box plot from a CSV, by group (matplotlib boxplot by group, multiple columns)
Matplotlib takes arrays, not a DataFrame grouping, so read the CSV with pandas and build one array per group. Dropping missing values per group before plotting keeps each array clean.
import pandas as pd
df = pd.read_csv('results.csv') # columns: group, value
grouped = list(df.groupby('group'))
names = [name for name, _ in grouped]
groups = [g['value'].dropna().to_numpy() for _, g in grouped]
fig, ax = plt.subplots()
ax.boxplot(groups, tick_labels=names)
plt.show()
For several numeric columns at once, pass a 2D array (for example df[['c1', 'c2', 'c3']].to_numpy()) and you get one box per column. The pandas shortcut DataFrame.boxplot(column=..., by=...) draws one box per value of the by column and takes an ax argument, so it can target an existing Matplotlib axes (pandas documentation, read 2026-11-07).
4. Whiskers, notches, means and fliers
fig, ax = plt.subplots()
ax.boxplot([a, b, c],
whis=(5, 95), # whiskers at the 5th and 95th percentiles
notch=True,
showmeans=True,
showfliers=False,
tick_labels=['A', 'B', 'C'])
plt.show()
5. Colour each box
Boxes are lines by default. Set patch_artist=True to get fillable patches, then style the returned artists:
fig, ax = plt.subplots()
res = ax.boxplot([a, b, c], patch_artist=True, tick_labels=['A', 'B', 'C'])
for box, colour in zip(res['boxes'], ['#0072B2', '#E69F00', '#009E73']):
box.set_facecolor(colour)
plt.show()
The return value is a dictionary of lists of Line2D objects with the keys boxes, medians, whiskers, caps, fliers and means. Style groups of elements with the boxprops, medianprops, whiskerprops, capprops, flierprops and meanprops arguments.
6. Horizontal box plot
ax.boxplot([a, b, c], orientation='horizontal')
The orientation argument was added in Matplotlib 3.10; in 3.11 the older vert argument is deprecated (see the version table below).
Output and how to read it
With the default whis=1.5, whiskers end at the lowest datum at or above Q1 – 1.5 x IQR and the highest datum at or below Q3 + 1.5 x IQR; points beyond are fliers. Other settings change what the whiskers mean:
| whis value | Whiskers end at |
|---|---|
1.5 (default) |
The most extreme data within 1.5 x IQR of the box |
A pair such as (5, 95) |
The given lower and upper percentiles of the data |
(0, 100) |
The full range, so no fliers are drawn |
Because the whisker rule is a choice, state it in the figure caption (for example ‘whiskers show the 5th to 95th percentile’). If Q1 equals Q3, the autorange=True option sets whis=(0, 100). A notch (notch=True) is a confidence interval around the median; the default uses a Gaussian asymptotic estimate, and bootstrap accepts a number of resamples (the reference suggests 1,000 to 10,000). Notches can extend beyond the box. The quartile interpolation method is not specified in the reference, so do not describe it in a methods section as if it were.
Version differences that break older tutorials
| Argument | Change in the Matplotlib documentation |
|---|---|
tick_labels |
Renamed from labels in 3.9; labels is removed in 3.11 |
orientation |
Added in 3.10 ('vertical' or 'horizontal') |
vert |
Deprecated in 3.11; use orientation |
label |
Added in 3.9, for the legend entry |
Troubleshooting
- TypeError about an unexpected keyword
labels: you are on Matplotlib 3.11 or later with code written for older versions. Usetick_labels. - A deprecation warning about
vert: replacevert=Falsewithorientation='horizontal'. - Boxes cannot be filled with a colour: set
patch_artist=Truefirst. - Fliers clutter the figure:
showfliers=Falsehides them, orsym=''. Hiding them changes what readers see, so say so in the caption. - Group labels sit under the wrong boxes:
positionsdefaults to 1 through N; if you pass custom positions, pass labels in the same order. - Check your version:
import matplotlib; print(matplotlib.__version__).
Free alternatives
| Tool | Relation to Matplotlib |
|---|---|
pandas DataFrame.boxplot |
Draws on Matplotlib; groups with by; accepts an ax argument |
| seaborn | Builds on Matplotlib; see the guide on the seaborn boxplot |
| R / ggplot2 | Separate ecosystem; see labelling a boxplot in R |
FAQ
How do I make a boxplot in Python?
Call ax.boxplot(data) from Matplotlib on an axes created with plt.subplots(). Pass a list of arrays to get one box per group.
How do I draw a Matplotlib boxplot with multiple columns?
Pass a 2D array: Matplotlib draws one box per column. A list of 1D arrays gives the same result when the columns differ in length.
How do I make a Matplotlib boxplot by group?
Split the data into one array per group, for example with groupby in pandas, and pass the list to ax.boxplot with tick_labels. Alternatively use DataFrame.boxplot(column=..., by=...).
How do I change the whiskers?
Use whis: a number multiplies the IQR (default 1.5), a pair sets percentiles, and (0, 100) spans the full range.
Matplotlib or seaborn?
Seaborn builds on Matplotlib and works directly on DataFrames; Matplotlib gives lower-level control of every artist. See the seaborn boxplot guide; for distributions as histograms see histograms in Matplotlib.







