Lesson 11
Strip, Swarm, and catplot
Show every observation with sns.stripplot and sns.swarmplot, layer the dots over a box plot, and switch any categorical chart with sns.catplot.
Box and violin plots summarise. Sometimes you want to see every row. stripplot draws one dot per row along each category, and swarmplot nudges the dots sideways so none overlap. Laid over a box plot, they give both the summary and the evidence.
One dot per row with stripplot
stripplot adds a little random sideways jitter so dots with equal values do not sit exactly on top of each other. jitter=False lines them up, which shows how much they overlap.
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=(10, 4), sharey=True)
sns.stripplot(data=study, x="track", y="score", jitter=False, ax=axes[0])
axes[0].set_title("jitter=False")
sns.stripplot(data=study, x="track", y="score", jitter=0.25, alpha=0.7, ax=axes[1])
axes[1].set_title("jitter=0.25")
plt.show()
print(study.groupby("track")["score"].count())Output:
track
Data 34
Python 35
Web 51
Name: score, dtype: int64No overlaps with swarmplot
swarmplot places each dot next to its neighbours instead of randomly, so the outline of the swarm traces the distribution. It works up to a few hundred rows per group; beyond that there is no room and a strip or violin plot is better.
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.swarmplot(data=study, x="track", y="score", hue="mode", size=4)
plt.show()
print(study.groupby(["track", "mode"]).size().unstack())Output:
mode classroom online
track
Data 17 17
Python 15 20
Web 22 29Dots over a box
Draw the box plot first without its outlier dots, then the points on the same axes. A dark single colour keeps the points from competing with the boxes.
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()
ax = sns.boxplot(data=study, x="track", y="score", showfliers=False, color="#DDD6FE")
sns.stripplot(data=study, x="track", y="score", color="#1E293B", size=3, alpha=0.7, ax=ax)
plt.show()
print(study.groupby("track")["score"].agg(["min", "median", "max"]))Output:
min median max
track
Data 40.0 68.0 85.0
Python 40.0 65.0 85.0
Web 47.0 63.0 85.0Any categorical chart with catplot
catplot is the figure-level function for the whole categorical family, as relplot is for scatter and line charts. kind picks "strip", "swarm", "box", "violin", "bar", "count", "point", or "boxen", and col or row add facets.
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()
g = sns.catplot(data=study, x="track", y="score", col="mode", kind="box", height=3.4)
plt.show()
print(type(g).__name__, g.axes.shape)Output:
FacetGrid (1, 2)pointplot for the mean
kind="point" draws only the mean and its interval as a dot with a line, then joins the dots across categories. It makes a change between groups easy to see.
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.catplot(data=study, x="mode", y="score", hue="track", kind="point", height=3.6)
plt.show()
print(study.pivot_table(index="mode", columns="track", values="score").round(1))Output:
track Data Python Web
mode
classroom 67.1 64.9 64.7
online 67.4 63.7 62.1Key takeaways
-
stripplot draws one dot per row with a little random jitter
-
swarmplot spreads dots so none overlap; best for hundreds, not thousands
-
Layer points over a box plot drawn with showfliers=False
-
catplot is the figure-level function for every categorical kind
-
Next: Heatmaps
-
Previous: Violin Plots
Loading playground…
Check your understanding
Strip, Swarm, and catplot Quiz
What does stripplot draw?
1 / 10