Box Plots: Understanding Distributions
A box plot (box-and-whisker plot) summarizes a numeric distribution in one compact shape: the box runs from the first quartile (Q1) to the third quartile (Q3) with a line at the median, the whiskers reach the furthest values within 1.5 ร IQR of the box, and any value beyond them is drawn as an individual outlier dot. That makes it the quickest way to compare the center, spread and outliers of several groups side by side.
Quick answer: Read a box plot from the inside out. The line in the box is the median, the box spans Q1 to Q3 (the middle 50% of values, a range called the IQR), the whiskers reach the most extreme values within 1.5 ร IQR of the box, and dots beyond them are outliers: values below Q1 โ 1.5 ร IQR or above Q3 + 1.5 ร IQR. In Python, draw one with sns.boxplot(x='group', y='value', data=df).
What do the box, median line and whiskers on a box plot show?
The box shows where the middle 50% of the data sits: its bottom edge is Q1 (the 25th percentile), its top edge is Q3 (the 75th percentile), and the line inside is the median (the 50th percentile). The whiskers stop at the lowest and highest values that are still within 1.5 ร IQR of the box, so they are not necessarily the minimum and maximum of the data; anything further out is plotted as a separate outlier dot.
โข โ Outlier (above Q3 + 1.5รIQR)
| โ Upper whisker: largest value โค Q3 + 1.5รIQR
|
โโโ
โ โ โ Q3 (75th percentile)
โโโค โ Median (50th percentile)
โ โ โ Q1 (25th percentile)
โโโ
|
| โ Lower whisker: smallest value โฅ Q1 - 1.5รIQR
โข โ Outlier (below Q1 - 1.5รIQR)
Box height = IQR (Interquartile Range) = Q3 - Q1
For a refresher on what quartiles and the IQR measure, see percentiles and quartiles explained.
How do you make a box plot in Python?
Pass a category column to x and a numeric column to y in seaborn's sns.boxplot(), and it draws one box per category using the 1.5 ร IQR whisker rule. Matplotlib's plt.boxplot() and pandas' df.boxplot() draw the same chart with less styling.
import seaborn as sns
import matplotlib.pyplot as plt
# Compare restaurant bills across days of the week
data = sns.load_dataset('tips')
plt.figure(figsize=(10, 6))
sns.boxplot(x='day', y='total_bill', data=data)
plt.title('Total Bill Distribution by Day')
plt.ylabel('Total Bill ($)')
plt.show()
How do you interpret a box plot?
Read four things in turn: the median line for the typical value, the box height (IQR) for how spread out the middle half is, the median's position and the whisker lengths for skew, and the separate dots for outliers. On a vertical box plot, a median near the bottom of the box with a long upper whisker means right (positive) skew, and a median near the top with a long lower whisker means left (negative) skew.
# What to look for:
1. Center (Median line):
Where is the middle of the data?
2. Spread (Box height):
How variable is the data?
3. Skewness (Median position and whisker lengths):
- Median near top = negatively skewed
- Median in middle = symmetric
- Median near bottom = positively skewed
4. Outliers (Individual points):
- Values far from the rest
- May indicate errors or special cases
How are outliers calculated in a box plot?
A value is plotted as an outlier when it falls below Q1 โ 1.5 ร IQR or above Q3 + 1.5 ร IQR, a rule known as Tukey's fences and the default in matplotlib, seaborn and pandas. For 11 delivery times of 18, 21, 22, 24, 25, 26, 27, 29, 31, 33 and 55 minutes, Q1 is 23, the median is 26 and Q3 is 30, so the IQR is 7 and the fences sit at 12.5 and 40.5.
import pandas as pd
minutes = pd.Series([18, 21, 22, 24, 25, 26, 27, 29, 31, 33, 55])
q1, median, q3 = minutes.quantile([0.25, 0.5, 0.75])
iqr = q3 - q1 # 30 - 23 = 7
lower_fence = q1 - 1.5 * iqr # 23 - 10.5 = 12.5
upper_fence = q3 + 1.5 * iqr # 30 + 10.5 = 40.5
outliers = minutes[(minutes < lower_fence) | (minutes > upper_fence)]
print(outliers.tolist())
# [55]
The box plot draws 55 as a dot, stops the upper whisker at 33 (the largest value inside the fence) and the lower whisker at 18. Whiskers always end on a real data point, never on the fence itself.
Excel or a textbook may give slightly different quartiles for the same data. pandas and NumPy interpolate between values by default, which matches Excel's QUARTILE.INC; Excel's QUARTILE.EXC and the hand method of taking the median of each half (leaving out the overall median) both give Q1 = 22 and Q3 = 31 here. The box edges move, but 55 is an outlier either way. For other ways to flag outliers, see outlier detection methods.
Comparing Multiple Groups
Side-by-side box plots put each group's median, spread and outliers on one shared axis, so you can compare groups at a glance instead of reading a summary table. In this example, North has the highest median, South the widest box, and East the tightest box plus one unusual week.
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# Weekly sales for three regions (8 weeks each)
sales_data = pd.DataFrame({
'region': ['North']*8 + ['South']*8 + ['East']*8,
'sales': [120, 125, 130, 128, 122, 135, 127, 131,
90, 140, 105, 160, 95, 150, 110, 145,
118, 121, 119, 123, 120, 122, 117, 190],
})
sns.boxplot(x='region', y='sales', data=sales_data)
plt.title('Weekly Sales by Region')
plt.show()
# North: highest median (127.5), box from 124.25 to 130.25
# South: widest box (102.5 to 146.25), the least consistent region
# East: tightest box (118.75 to 122.25) plus one outlier week at 190
When to Use Box Plots
Use a box plot when you need to compare the distribution of one numeric variable across several groups, or to flag outliers quickly. Avoid it when a group has only a handful of values, because summary numbers from so few points are unstable, or when the data may have more than one peak, which a box cannot show.
- Comparing distributions across categories
- Identifying outliers
- Understanding data spread
- Spotting skewness
- Quality control analysis
Box Plot vs Violin Plot
A box plot shows summary statistics, while a violin plot draws the full density shape of the data, so choose a violin plot when the shape matters. A group with one peak and a group with two peaks can produce similar boxes; only a violin plot or a histogram shows the difference. For a fuller comparison of distribution charts, see box plot vs violin and other distribution plots.
# Box plot: Shows summary statistics
# Violin plot: Shows full distribution shape
sns.violinplot(x='day', y='total_bill', data=data)
# Violin plots show density - good for multimodal distributions
What are the most common box plot mistakes?
The most common mistake is reading the whiskers as the minimum and maximum: by default they stop at the last value within 1.5 ร IQR, so the true extremes may be outlier dots. Watch for these as well:
- Hiding sample size: a box built from 19 values looks as solid as one built from 87 (Friday vs Saturday in seaborn's tips dataset). Overlay the raw points or label n under each box.
- Mistaking the line for the mean: the line is the median. Add
showmeans=Truetosns.boxplot()to mark the mean as well. - Deleting outliers automatically: a dot beyond the whiskers is a value to check, not proof of an error. If it is real, keep it and annotate the outlier.
- Plotting outliers twice: when you overlay points with
sns.stripplot(), passshowfliers=Falseto the box plot so outliers are not drawn twice.
# Box plot with the mean marked and every bill shown as a point
sns.boxplot(x='day', y='total_bill', data=data, showfliers=False, showmeans=True)
sns.stripplot(x='day', y='total_bill', data=data, color='black', size=3)
plt.show()
Pro Tip: Box plots are excellent for comparing distributions side-by-side. Pay attention to outliers: they often represent data quality issues or interesting edge cases worth investigating.
โ Back to Visualization Tips