Skip to main content
MahiDev — Learn. Build. Become AI-ready.

Lesson 05

relplot and Small Multiples

Split one chart into a grid of small multiples with sns.relplot, using col, row, and col_wrap, and learn how figure-level functions differ from axes-level ones.

Intermediate25 min
relplot and Small Multiples hero Module 26 Lesson 5relplot and Small Multiples hero Module 26 Lesson 5

One crowded chart is often clearer as several small ones that share the same axes. Seaborn calls these facets. sns.relplot draws the same scatter or line chart once per group and lays the panels out in a grid for you.

Three small scatter panels of hours against score, one for each trackThree small scatter panels of hours against score, one for each track

Figure-level versus axes-level

scatterplot and lineplot are axes-level: they draw into one Matplotlib Axes and return it. relplot is figure-level: it creates a whole figure and returns a FacetGrid that owns every panel. That is why relplot has no ax= argument.

python
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.relplot(data=study, x="hours", y="score", kind="scatter")
plt.show()

print(type(g).__name__)
print(g.axes.shape)

Output:

text
FacetGrid
(1, 1)

One panel per group with col

col="track" makes a column of panels, one per track. Every panel shares the x and y axes, so comparing positions across panels is fair.

python
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.relplot(data=study, x="hours", y="score", col="track", hue="track", legend=False)
plt.show()

print(g.axes.shape)
print([ax.get_title() for ax in g.axes.flat])

Output:

text
(1, 3)
['track = Web', 'track = Data', 'track = Python']

Rows and columns together

Add row= for a second grouping. Two modes by three tracks gives a two by three grid. height sets each panel's height in inches and aspect its width relative to that height.

python
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.relplot(data=study, x="hours", y="score", row="mode", col="track",
                height=2.6, aspect=1.1)
plt.show()

print(g.axes.shape)

Output:

text
(2, 3)

Wrap a long row

Six weeks in one row would be squashed. col_wrap=3 starts a new row after three panels. kind="line" switches every panel to a line chart.

python
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.relplot(data=study, x="hours", y="score", col="week", col_wrap=3,
                height=2.4, s=20)
g.set_titles("week {col_name}")
plt.show()

print(len(g.axes.flat))

Output:

text
6

Label the whole grid

Because a FacetGrid owns many panels, it has its own labelling methods: set_axis_labels for every axis at once, set_titles with {col_name} and {row_name} placeholders, and figure.suptitle for one title over everything.

python
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.relplot(data=study, x="week", y="score", col="mode", kind="line", height=3)
g.set_axis_labels("week", "quiz score")
g.set_titles("{col_name} learners")
g.figure.suptitle("Scores rise as the weeks go on", y=1.05)
plt.show()

print([ax.get_title() for ax in g.axes.flat])

Output:

text
['classroom learners', 'online learners']

Key takeaways

  • Axes-level functions draw into one Axes; figure-level ones build a whole figure

  • relplot returns a FacetGrid and has no ax= argument

  • col=, row=, and col_wrap= split the chart into small multiples

  • Use set_axis_labels, set_titles, and figure.suptitle on the grid

  • Next: Histograms with histplot

  • Previous: Line Plots and Error Bands

Loading playground…

Check your understanding

relplot and Small Multiples Quiz

What does relplot return?

1 / 10