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