I want a seaborn multiplot that varies the x-axis variable by column, but varies the subset of data shown by row. I can use PairGrid to vary the variables graphed, and I can use FacetGrid to vary the subsets graphed, but I don’t see any facility to do both at once, even though it seems like a natural extension.
Is there a way to do this in seaborn currently? Or is this something that would need a feature request?
Here’s a mockup of what I’m trying to do:
label:A | y:Y | (plot M vs Y where label == A) | (plot N vs Y where label == A) |
label:B | y:Y | (plot M vs Y where label == B) | (plot N vs Y where label == B) |
x:M | x:N |
I’d also take the transpose of this scheme :)
Advertisement
Answer
This is not a feature that directly exists in seaborn (though it is likely to become one at some point).
That said, FacetGrid and PairGrid just instantiate different mappings between a dataframe and a figure (modulo the diagonal plots in PairGrid and a few features here and there). So a plot that is naturally expressed using one tool can generally made with the other, given a data reshaping.
So you could do something like
x_var = "body_mass_g"
col_var = "sex"
hue_var = "species"
y_vars = ["bill_length_mm", "bill_depth_mm"]
(
df
.melt([x_var, col_var, hue_var], y_vars)
.pipe(
(sns.relplot, "data"),
x=x_var,
y="value",
hue=hue_var,
col=col_var,
row="variable",
facet_kws=dict(sharey="row"),
height=3.5,
)
)
There’s your plot, basically, but the labels are a little confusing. Let’s improve that:
g = (
df
.melt([x_var, col_var, hue_var], y_vars)
.pipe(
(sns.relplot, "data"),
x=x_var,
y="value",
hue=hue_var,
col=col_var,
row="variable",
facet_kws=dict(sharey="row", margin_titles=True),
height=3.5,
)
.set_titles(col_template="{col_var} = {col_name}", row_template="")
)
for (row_name, _), ax in g.axes_dict.items():
ax.set_ylabel(row_name)
A little more cumbersome, but also not so hard to wrap up into a pretty general function:
def paired_column_facets(
data: pd.DataFrame, y_vars: list[str], other_vars: dict[str, str], **kwargs
) -> FacetGrid:
g = (
df
.melt(list(other_vars.values()), y_vars)
.pipe(
(sns.relplot, "data"),
**other_vars,
y="value",
row="variable",
facet_kws=dict(sharey="row", margin_titles=True),
**kwargs,
)
.set_titles(col_template="{col_var} = {col_name}", row_template="")
)
for (row_name, _), ax in g.axes_dict.items():
ax.set_ylabel(row_name)
return g