Mixing up the two is the top source of seaborn confusion: why ax= is ignored, why figsize does nothing, why the legend sits outside.
seaborn has two kinds of functions.
Axes-level functions (scatterplot, lineplot, histplot, kdeplot, boxplot, barplot, heatmap, regplot ...) draw onto one Matplotlib Axes. They accept ax= and return that Axes, so they compose with any Matplotlib layout you build.
Figure-level functions create their own figure, possibly with many panels, and return a grid object (FacetGrid, PairGrid or JointGrid). There is one per family:
relplot(kind"scatter"/"line"),displot("hist"/"kde"/"ecdf"),catplot("strip","box","violin","bar", ...),lmplot,pairplot,jointplot.
They add faceting: col="region", row="year", and col_wrap=3 to wrap many columns. They do not accept ax=; size panels with height= (inches per panel) and aspect= (width / height) instead of figsize. The legend is placed outside the panels by default.
The returned grid exposes g.figure, g.axes (2D array), g.axes_dict (by facet value), and helpers: g.set_axis_labels("x", "y"), g.set_titles("{col_name}"), g.set(xlim=...), g.figure.suptitle(...) and g.savefig(...).
Rule of thumb: use figure-level functions to explore and facet quickly; use axes-level functions when placing a chart into a custom Matplotlib layout.
Example
import matplotlib
matplotlib.use("Agg")
import seaborn as sns
import pandas as pd
import numpy as np
rng = np.random.default_rng(3)
df = pd.DataFrame({
"x": rng.normal(size=300),
"region": rng.choice(["north", "south", "east", "west"], 300),
"plan": rng.choice(["free", "pro"], 300),
})
df["y"] = df["x"] * 2 + rng.normal(size=300)
g = sns.relplot(data=df, x="x", y="y", hue="plan", col="region",
col_wrap=2, height=3, aspect=1.2)
g.set_titles("{col_name}")
g.set_axis_labels("Visits", "Spend")
print(type(g).__name__) # FacetGrid
print(g.axes.shape) # (4,) col_wrap flattens the array
print(sorted(g.axes_dict)) # ['east', 'north', 'south', 'west']
print(g.figure.get_size_inches()) # [8. 6.] (panels plus legend space)
g.savefig("facets.png")Remember
- Axes-level functions take
ax=; figure-level functions build their own figure - Figure-level functions facet with
col,row,col_wrapand size withheightandaspect - Customize grids through
g.figure,g.axes,g.set_titlesandg.set_axis_labels
Watch out for
- Passing
ax=or settingplt.figure(figsize=...)beforerelplot; both are ignored by figure-level functions.