Search code examples
pythonmatplotlibseaborn

reverse order of bars in seaborn stacked barplot


i have a plot like this: enter image description here

now I want to inverse the order of the stacked bars without changing the color-value-mapping: low value->red, high_value-> green. in short: lowest bar is red with value 1 and the top par is green with value 10.

basically it should look like this (but without the inverted yaxis...) enter image description here

here is some sample code to generate the data and the plot...

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

#dummy data
relative_amount_bad_scores = np.random.rand(1000)
aggregated_score = np.random.choice(
    np.arange(1, 11), 1000, p=[0.05, 0.05, 0.1, 0.1, 0.15, 0.15, 0.15, 0.1, 0.1, 0.05]
)
df_dummy = pd.DataFrame({
    "relative_amount_of_bad_scores": relative_amount_bad_scores,
    "aggregated_score": aggregated_score
})


# Plot
palette = sns.color_palette("RdYlGn", as_cmap=True)
fig, ax = plt.subplots(figsize=(15, 15))
sns.histplot(
    data=df_dummy.sort_values('aggregated_score', ascending = True),
    x="relative_amount_of_bad_scores",
    hue="aggregated_score",
    multiple="fill",
    ax=ax,
    binwidth=0.1,
    palette = palette
)

plt.show() # to run outside IDE

Solution

  • I get this running your code:

    enter image description here

    and this one below:

    enter image description here

    changing :

    sns.histplot(
        data=df_dummy.sort_values('aggregated_score', ascending = True),
        x="relative_amount_of_bad_scores",
        hue="aggregated_score",
        multiple="fill",
        ax=ax,
        binwidth=0.1,
        palette = palette
    )
    

    to:

    plot = sns.histplot(
        data=df_dummy.sort_values('aggregated_score', ascending = True),
        x="relative_amount_of_bad_scores",
        hue="aggregated_score",
        multiple="fill",
        ax=ax,
        binwidth=0.1,
        palette = palette
    )
    
    plot.set(ylim=plot.get_ylim()[::-1])
    

    not sure it is what you were asking for , your pics look different from mine

    Ok after comments and having read again the question , noticing:

    basically it should look like this (but without the inverted yaxis...)

    adding:

    plot.set_yticklabels(plot.get_yticks()[::-1]) 
    

    I get:

    enter image description here

    Not I have to deal with:

    matplotlib.axes.Axes.ticklabel_format

    using after the previous :

    plot.set_yticklabels(plot.get_yticks()[::-1]) 
    
    plot.set(ylim=plot.get_ylim()[::-1])
    

    this lines from Specify format of floats for tick labels

    labels = [item.get_text() for item in plot.get_yticklabels()]
    # Beat them into submission and set them back again
    plot.set_yticklabels([str(round(float(label), 2)) for label in labels])
    

    ***see addendum

    Finally I get:

    enter image description here

    Wasnt able to use code like:

    from matplotlib.ticker import StrMethodFormatter
    plt.gca().yaxis.set_major_formatter(StrMethodFormatter('{x:,.0f}')) # No decimal places
    plt.gca().yaxis.set_major_formatter(StrMethodFormatter('{x:,.2f}')) # 2 decimal places
    

    (obviously translated to plot.yaxis.set_m ..... ) , because the command was reversing the y axis limits back from 1 to 0

    And of course you get :

    UserWarning: FixedFormatter should only be used together with FixedLocator
      plot.set_yticklabels([str(round(float(label), 2)) for label in labels])
    

    and:

    UserWarning: FixedFormatter should only be used together with FixedLocator
      plot.set_yticklabels(plot.get_yticks()[::-1])
    

    See UserWarning: FixedFormatter should only be used together with FixedLocator

    ***ADDENDUM:

    plot.set_yticklabels(np.round(plot.get_yticks()[::-1],2)) 
    

    works too