Search code examples
pythonmatplotlibseabornerrorbar

How to plot errorbars on seaborn barplot


I have the following dataframe:

data = {'Value':[6.25, 4.55, 4.74, 1.36, 2.56, 1.4, 3.55, 3.21, 3.2, 3.65, 3.45, 3.86, 13.9, 10.3, 15], 
        'Name':['Peter', 'Anna', 'Luke',
                 'Peter', 'Anna', 'Luke',
                 'Peter', 'Anna', 'Luke',
                 'Peter', 'Anna', 'Luke',
                 'Peter', 'Anna', 'Luke'],
        'Param': ['Param1', 'Param1', 'Param1', 
                 'Param2', 'Param2', 'Param2', 
                 'Param3', 'Param3', 'Param3', 
                 'Param4', 'Param4', 'Param4',
                 'Param5', 'Param5', 'Param5'],
        'error': [2.55, 1.24, 0, 0.04, 0.97, 0, 0.87, 0.7, 0, 0.73, 0.62, 0, 0, 0, 0]}
df = pd.DataFrame(data)

I'd like to add errorbars (pre-defined in the error column) to the bar plot, but I can't seem to get the x-coordinates right? It shows errorbars for Param5 but there are no errors for Param5? Also for Luke, there are no errors, but in Param1 an errorbar is plotted.

plt.figure()
ax = sns.barplot(x = 'Param', y = 'Value', data = df, hue = 'Name', palette = sns.color_palette('CMRmap_r', n_colors = 3))
x_coords = [p.get_x() + 0.5*p.get_width() for p in ax.patches]
y_coords = [p.get_height() for p in ax.patches]
plt.errorbar(x=x_coords, y=y_coords, yerr=df["error"], fmt="none", c= "k")

enter image description here


Solution

  • The bars in ax.patches come ordered by hue value. To get the bars and the dataframe in the same order, the dataframe could be sorted first by Name and then by Param:

    from matplotlib import pyplot as plt
    import seaborn as sns
    import pandas as pd
    
    data = {'Value': [6.25, 4.55, 4.74, 1.36, 2.56, 1.4, 3.55, 3.21, 3.2, 3.65, 3.45, 3.86, 13.9, 10.3, 15],
            'Name': ['Peter', 'Anna', 'Luke',
                     'Peter', 'Anna', 'Luke',
                     'Peter', 'Anna', 'Luke',
                     'Peter', 'Anna', 'Luke',
                     'Peter', 'Anna', 'Luke'],
            'Param': ['Param1', 'Param1', 'Param1',
                      'Param2', 'Param2', 'Param2',
                      'Param3', 'Param3', 'Param3',
                      'Param4', 'Param4', 'Param4',
                      'Param5', 'Param5', 'Param5'],
            'error': [2.55, 1.24, 0, 0.04, 0.97, 0, 0.87, 0.7, 0, 0.73, 0.62, 0, 0, 0, 0]}
    df = pd.DataFrame(data)
    df = df.sort_values(['Name', 'Param'])
    
    plt.figure(figsize=(8, 5))
    ax = sns.barplot(x='Param', y='Value', data=df, hue='Name', palette='CMRmap_r')
    x_coords = [p.get_x() + 0.5 * p.get_width() for p in ax.patches]
    y_coords = [p.get_height() for p in ax.patches]
    ax.errorbar(x=x_coords, y=y_coords, yerr=df["error"], fmt="none", c="k")
    plt.show()
    

    sns.barplot with custom error bars

    PS: Note that by default, the columns are sorted alphabetically. If you want to maintain the original order, you can make the column categorical via pd.Categorical(df['Name'], df['Name'].unique()).

    df = pd.DataFrame(data)
    df['Name'] = pd.Categorical(df['Name'], df['Name'].unique())
    df['Param'] = pd.Categorical(df['Param'], df['Param'].unique())
    df = df.sort_values(['Name', 'Param'])
    

    sns.barplot with erorbars in original order