Home > Net >  How to annotate bar plots when adding error bars
How to annotate bar plots when adding error bars

Time:12-14

I have a dataframe dictionary like this

{'region': {0: 'R0',1: 'R1',2: 'R2',3: 'R3',4: 'R4',5: 'R5',6: 'R6'},
 'DT': {0: 0.765, 1: 0.694, 2: 0.778, 3: 0.694, 4: 0.629, 5: 0.67, 6: 0.668},
 'GB': {0: 0.714, 1: 0.741, 2: 0.752, 3: 0.741, 4: 0.683, 5: 0.706, 6: 0.656},
 'KNN': {0: 0.625, 1: 0.641, 2: 0.628, 3: 0.641, 4: 0.552, 5: 0.544, 6: 0.578},
 'LR': {0: 0.624, 1: 0.662, 2: 0.634, 3: 0.662, 4: 0.581, 5: 0.629, 6: 0.649},
 'lstm': {0: 0.803,1: 0.633,2: 0.845,3: 0.668,4: 0.717,5: 0.726,6: 0.674}}

In neat format

    region DT   GB      KNN      LR     lstm
0   R0  0.765   0.714   0.625   0.624   0.803
1   R1  0.694   0.741   0.641   0.662   0.633
2   R2  0.778   0.752   0.628   0.634   0.845
3   R3  0.694   0.741   0.641   0.662   0.668
4   R4  0.629   0.683   0.552   0.581   0.717
5   R5  0.67    0.706   0.544   0.629   0.726
6   R6  0.668   0.656   0.578   0.649   0.674

I want to plot stacked bar graph with error bar. This dataframe dont have information about standard deviation, but i have another dataframe of standard deviation.

Suppose there are two dataframe mean, and std

I tried this code

fig, ax = plt.subplots()
width=0.5
clfs=['DT', 'KNN', 'LR', 'GB', 'lstm']
ax.bar(mean_df['region'], mean_df[clfs[0]], width,yerr=std_df[clfs[0]], label=clfs[0])
for i in range(1,5):
    ax.bar(mean_df['region'], mean_df[clfs[i]], width,yerr=std_df[clfs[i]], label=clfs[i],bottom=mean_df[clfs[i-1]])

plt.xticks(rotation=90)
plt.legend()
plt.show()

but the bars are not being stacked properly. I am also looking a way to write value on each bar segment to increase the readability of plot enter image description here

EDIT: Solution is to add first two list in bottom while plotting third one.

fig, ax = plt.subplots()
ax.bar(mean_df['region'], mean_df[clfs[0]], width,yerr=std_df[clfs[0]], label=clfs[0])
ax.bar(mean_df['region'], mean_df[clfs[1]], width,yerr=std_df[clfs[1]], label=clfs[1],bottom=mean_df[clfs[0]])
ax.bar(mean_df['region'], mean_df[clfs[2]], width,yerr=std_df[clfs[2]], label=clfs[2],
       bottom=mean_df[clfs[0]] mean_df[clfs[1]])

But i am looking for an elegant way to do this and also how to write values on segment of bar

EDIT 2: I came to this

ax = mean_df.plot(kind='bar', stacked=True, figsize=(8, 6),yerr=std_df, rot=0, xlabel='region', ylabel='DT')

But now i am looking way to write text. I tried this

for c in ax.containers:
    ax.bar_label(c, label_type='center')

but i got this error

AttributeError: 'ErrorbarContainer' object has no attribute 'patches'

EDIT 3
This error is because of yerr=std_df, but i also want to keep error bars

CodePudding user response:

  • Stacked bars are not an ideal way to present the data. With error bars, stacked bars are even more difficult to read, may overlap with the error bar within a given stack, and with the annotations, which can lead to a confusing visualization.
  • The issue will occur for stacked=True or stacked=False, and it applies to using enter image description here

    Horizontal Bars

    # plot the dataframe and add yerr
    ax = pen_mean.plot(kind='barh', stacked=True, figsize=(9, 6), rot=0, xerr=pen_std)
    
    # move the legend
    ax.legend(bbox_to_anchor=(1, 1.02), loc='upper left')
    
    # iterate through every other container; the even containers are ErrorbarContainer
    for c in ax.containers[1::2]:
    
        # add the annotation
        ax.bar_label(c, label_type='center')
    

    enter image description here


    Axes.bar with Axes.errorbar

    • The BarContainer objects are at the even indices, which can be extracted with ax.containers[0::2]
    data = pen_mean
    
    cols = pen_mean.columns
    rows = pen_mean.index
    
    # Get some pastel shades for the colors
    colors = ['tab:blue', 'tab:green']
    n_rows = len(data)
    
    index = np.arange(len(cols))
    bar_width = 0.4
    
    # Initialize the vertical-offset for the stacked bar chart.
    y_offset = np.zeros(len(cols))
    
    # Plot bars and create text labels for the table
    fig, ax = plt.subplots(figsize=(8, 5))
    
    for i, row in enumerate(rows):
        ax.bar(cols, data.loc[row], bar_width, bottom=y_offset, color=colors[i])
        ax.errorbar(cols, y_offset data.loc[row], pen_std.loc[row], color='k', ls='none')
        y_offset = y_offset   data.loc[row]
        
    # note the order of the container objects is different
    for c in ax.containers[0::2]:
        ax.bar_label(c, label_type='center')
    
    plt.show()
    

    enter image description here


    seaborn bars

    • seaborn bar plots with the default ci=True do not return ErrorbarContainer objects in containers.

    sns.catplot with kind='bar'

    • See this enter image description here

      sns.barplot

      fig = plt.figure(figsize=(9, 6))
      p = sns.barplot(data=pen, x='sex', y='bill_depth_mm', hue='species')
      
      p.legend(bbox_to_anchor=(1, 1.02), loc='upper left')
      
      for c in p.containers:
      
          # add the annotation
          p.bar_label(c, label_type='center')
      

      enter image description here

  • Related