Home > Enterprise >  Share histogram bins across columns
Share histogram bins across columns

Time:12-08

I am trying to create a series of histograms (from a pd.DataFrame). I would like different quantities (fare in the example below) in different rows and different classes (class in the example below) in different columns, as follows:

import seaborn as sns
from matplotlib import pyplot as plt

titanic = sns.load_dataset("titanic")
df = pd.melt(titanic, id_vars=["sex", "class"], value_vars=["fare", "age"])
sns.displot(
    data=df,
    x="value",
    row="variable",
    col="class",
    hue="sex",
    facet_kws={"sharex": "row", "sharey": "row"},
)
plt.show()

enter image description here

Unfortunately, because of the common_bins=True parameter it seems that the axis are shared both across the rows and across the columns, which I don't want because different rows have different quantities that can be in very different ranges. Using common_bins=False I get the following:

import seaborn as sns
from matplotlib import pyplot as plt

titanic = sns.load_dataset("titanic")
df = pd.melt(titanic, id_vars=["sex", "class"], value_vars=["fare", "age"])
sns.displot(
    data=df,
    x="value",
    row="variable",
    col="class",
    hue="sex",
    facet_kws={"sharex": "row", "sharey": "row"},
    common_bins=False,
)
plt.show()

enter image description here

In this case the axis are shared only across the same row as I want (which is better suited to represent quantities with different ranges), but the bins are not shared anymore. I'd like the bins to be shared across columns, or at least in the same histogram (but much preferably across columns).

I also tried to use sns.FacetGrid directly, but in such case it does not seem possible to get a legend enter image description here

Is there a simple way to share bins and axis on the same row and get a legend based on hue? Essentially the last figure but with the legend of the first two figures (and possibly bins shared across the same row).

CodePudding user response:

FacetGrid is designed to show the same x/y relationship in every facet. If that's not what you're trying to do, you may have more success using a more procedural approach. Here's one idea where we use FacetGrid to set up the figure but loop over the axes individually.

titanic = sns.load_dataset("titanic")
df = pd.melt(titanic, id_vars=["sex", "class"], value_vars=["fare", "age"])

binwidths = {"fare": 20, "age": 5}

g = sns.FacetGrid(df, col="class", row="variable", sharex="row", sharey="row", height=2.5)
for (var, cls), ax in g.axes_dict.items():
    sns.histplot(
        data=titanic[titanic["class"] == cls],
        x=var, hue="sex",
        binwidth=binwidths[var],
        ax=ax, legend=ax is g.axes.flat[-1])

g.set_titles(template="{col_name} {col_var}")
plt.setp(g.axes[1], title="")
sns.move_legend(ax, loc="lower left", bbox_to_anchor=(1, 1))

enter image description here

  • Related