Search code examples
pythonseabornbar-chartcatplot

How to set bar widths equal in catplot with horizontal bars


I have a data that I want to present as horizontal bar plots in rows and columns. I'm trying to do this with Python seaborn, which facet facility suits this purpose well.

The set of y-axis variables is different on the row dimension, and I don't want to present the empty items in facets. The problem is that the facets get the same heights while bar widths adjust. I wonder whether I could do this the opposite way so that all the bar widths would be equal in all facets, but the facet heights would adjust.

Here is an example:

import plotly.express as px
import seaborn as sns
df = px.data.tips()
g = sns.catplot(x="total_bill", y="day", col="sex", row="time", hue="smoker", data=df, 
                kind="bar", sharey=False, margin_titles=True)

And this is what the figure looks like (with seaborn version 0.11.2):

enter image description here


Solution

  • how to change the gridspec

    To get the output you want you can set the FacetGrid's gridspec according to the number of categories:

    df = px.data.tips()
    df = df[df["day"] != "Thur"]
    ratios = df.groupby('time')['day'].nunique().values
    g = sns.catplot(x="total_bill", y="day", col="sex", row="time",
                    data=df, kind="bar", sharey=False,
                    legend=False, margin_titles=True,
                    facet_kws={'gridspec_kws':{'height_ratios': ratios}})
    

    output:

    enter image description here

    previous answer: reproducibility

    If I reproduce your code with seaborn 0.11.0 I get the following output. Due to the Categorical values of day, the common days are broadcasted to all plots and the columns have the same width.

    >>> df = sns.load_dataset("tips")
    >>> df.dtypes
    
    total_bill     float64
    tip            float64
    sex           category
    smoker        category
    day           category
    time          category
    size             int64
    dtype: object
    

    enter image description here

    update: plotly express dataset
    >>> import plotly.express as px
    >>> df2 = px.data.tips()
    >>> df2.dtypes
    total_bill    float64
    tip           float64
    sex            object
    smoker         object
    day            object
    time           object
    size            int64
    dtype: object