Lesson 09
Box Plots
Summarise a distribution in five numbers with sns.boxplot: read the box, median, whiskers, and outlier dots, and compare several groups side by side.
A box plot squeezes a distribution into a few marks. The box runs from the first quartile to the third, so it holds the middle half of the rows. The line inside is the median. The whiskers reach the furthest values within 1.5 box lengths, and anything beyond them is drawn as a separate dot.
The five numbers
Draw one box for the whole column, then print the quartiles. The box edges are the 25% and 75% values, the line is the 50% value, and the distance between the edges is the interquartile range, or IQR.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
rng = np.random.default_rng(26)
study = pd.DataFrame({
"track": rng.choice(["Python", "Data", "Web"], 120),
"mode": rng.choice(["online", "classroom"], 120),
"week": rng.integers(1, 7, 120),
})
study["hours"] = (rng.uniform(1, 6, 120) + study["week"] * 0.8).round(1)
study["score"] = (35 + study["hours"] * 4.5 + rng.normal(0, 7, 120)).clip(0, 100).round()
sns.boxplot(data=study, y="score")
plt.show()
q1, median, q3 = study["score"].quantile([0.25, 0.5, 0.75])
print(q1, median, q3)
print("IQR:", q3 - q1)Output:
57.0 65.0 72.0
IQR: 15.0Groups side by side
Put a category on x and a number on y to get one box per group. Compare the medians first, then the box heights: a taller box means the middle half of that group is more spread out.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
rng = np.random.default_rng(26)
study = pd.DataFrame({
"track": rng.choice(["Python", "Data", "Web"], 120),
"mode": rng.choice(["online", "classroom"], 120),
"week": rng.integers(1, 7, 120),
})
study["hours"] = (rng.uniform(1, 6, 120) + study["week"] * 0.8).round(1)
study["score"] = (35 + study["hours"] * 4.5 + rng.normal(0, 7, 120)).clip(0, 100).round()
sns.boxplot(data=study, x="track", y="score", order=["Python", "Data", "Web"])
plt.show()
summary = study.groupby("track")["score"].quantile([0.25, 0.5, 0.75]).unstack()
print(summary)Output:
0.25 0.50 0.75
track
Data 63.0 68.0 72.75
Python 57.0 65.0 72.00
Web 54.5 63.0 70.50Whiskers and outliers
Whiskers stop at the last value within 1.5 times the IQR of the box. Values beyond that are drawn as dots. Working out the fences yourself shows exactly which rows became dots.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
rng = np.random.default_rng(26)
study = pd.DataFrame({
"track": rng.choice(["Python", "Data", "Web"], 120),
"mode": rng.choice(["online", "classroom"], 120),
"week": rng.integers(1, 7, 120),
})
study["hours"] = (rng.uniform(1, 6, 120) + study["week"] * 0.8).round(1)
study["score"] = (35 + study["hours"] * 4.5 + rng.normal(0, 7, 120)).clip(0, 100).round()
data = study[study["track"] == "Data"]["score"]
q1, q3 = data.quantile([0.25, 0.75])
low, high = q1 - 1.5 * (q3 - q1), q3 + 1.5 * (q3 - q1)
print("fences:", low, high)
print("outliers:", sorted(data[(data < low) | (data > high)]))
sns.boxplot(x=data)
plt.show()Output:
fences: 48.375 87.375
outliers: [40.0]A dot is not automatically a mistake. It is a value worth checking, which may be a typo or may be the most interesting learner in the table.
Split by a second group
hue puts boxes for each mode next to each other inside every track. whis changes the whisker reach, and showfliers=False hides the outlier dots when you plan to draw every point yourself.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
rng = np.random.default_rng(26)
study = pd.DataFrame({
"track": rng.choice(["Python", "Data", "Web"], 120),
"mode": rng.choice(["online", "classroom"], 120),
"week": rng.integers(1, 7, 120),
})
study["hours"] = (rng.uniform(1, 6, 120) + study["week"] * 0.8).round(1)
study["score"] = (35 + study["hours"] * 4.5 + rng.normal(0, 7, 120)).clip(0, 100).round()
fig, axes = plt.subplots(1, 2, figsize=(11, 4), sharey=True)
sns.boxplot(data=study, x="track", y="score", hue="mode", ax=axes[0])
sns.boxplot(data=study, x="track", y="score", whis=3, showfliers=False, ax=axes[1])
axes[1].set_title("whis=3, no outlier dots")
plt.show()
print(study.groupby(["track", "mode"])["score"].median().unstack())Output:
mode classroom online
track
Data 68.0 68.0
Python 63.0 66.5
Web 66.5 63.0Key takeaways
-
The box spans the first to third quartile; the line is the median
-
The IQR is the box height: the spread of the middle half
-
Whiskers reach 1.5 IQR; values beyond are drawn as dots
-
hue splits boxes by a second group; whis and showfliers adjust the rest
-
Next: Violin Plots
-
Previous: Count Plots and Bar Plots
Loading playground…
Check your understanding
Box Plots Quiz
What does the box span?
1 / 10