7
votes

I wrote some code to try and solve this question: https://stackguides.com/questions/39477748/how-to-annotate-bars-with-values-on-pandas-on-seaborn-factorplot-bar-plot

I used part of the code that can be found here: matplotlib advanced bar plot

Why is the graph so small? The code just tells to grab the accuracies from Pandas dataframe .

enter image description here

The code:

sns.set(style="white")
g = sns.factorplot(x="Stages", y="Accuracy", hue="Dataset", data=df, saturation = 5, size=4, aspect=2, kind="bar",
              palette= myPalette, legend=False)

ax=g.ax
def annotateBars(row, ax=ax):
    if row['Accuracy'] < 20:
        color = 'white'
        vertalign = 'bottom'
        vertpad = 2
    else:
        color = 'black'
        vertalign = 'top'
        vertpad = -2

    ax.text(row.name, row['Accuracy'] + vertpad, "{:.1f}%".format(row['Accuracy']),
            zorder=10, rotation=90, color=color,
            horizontalalignment='center',
            verticalalignment=vertalign,
            fontsize=12, weight='heavy')

junk = df.apply(annotateBars, ax=ax, axis=1)

This is code to annotate each bar, but ...with Pandas and Matplotlib. The only problem is that I do not know how to change colors and group the "x axis" :(

    df = df.set_index('Stages')
    ax = df.plot.bar(title="Accuracy")
    ax.set_ylim(0, 120)
    for p in ax.patches:
        ax.annotate("%.2f" % p.get_height(), (p.get_x() + p.get_width() / 2., p.get_height()),
             ha='center', va='center', rotation=90, xytext=(0, 20), textcoords='offset points')  #vertical bars

enter image description here

2
the graph is so small b/c the annotation go so far out to the right. you're not providing ax.annotate with good x-valuesPaul H
How can I find the good values? I am using g.ax; just as you used: fig, ax = plt.subplots(figsize=(8,3))Aizzaac
Look at g.ax.get_xlim() (the range of X values you should be using) and compare to df.index (the actual X values you are using)Paul H
Values I should be using: (-0.5, 5.5). Values I am using: (0, 24). But I have 24 accuracies. I cannot change that. So instead I changed (-0.5, 5.5) to (0, 24). The plot always become smaller. :( So "factorplot" is considering each group of 4 bars as 1 bar. :( I guess the only way to do it is with matplotlib. :(Aizzaac
That's because you assigned the output of get_xlim to your ax variable. Don't do that. I stea write a function that annotates the bars as they are drawn and map that across a FacetGridPaul H

2 Answers

17
votes
    #Seaborn --factorplot

    colors = ["windows blue", "orange red", "grey", "amber"]  
    myPalette = sns.xkcd_palette(colors) #envío "colors" a la función xkcd_palette

    sns.set(style="white") #fondo blanco
    g = sns.factorplot(x="Stages", y="Accuracy", hue="Dataset", data=df, saturation=5, size=4, aspect=3, kind="bar",
              palette= myPalette, legend=False) #se suprime la leyenda

    g.set(ylim=(0, 140)) 
    g.despine(right=False) 
    g.set_xlabels("") 
    g.set_ylabels("")  
    g.set_yticklabels("") 


   #Matplotlib --legend creation

     myLegend=plt.legend(bbox_to_anchor=(0., 1.2, 1., .102), prop ={'size':10}, loc=10, ncol=4,  #left, bottom, width, height
                title=r'TOTAL ACCURACY AND PER STAGE-RANDOM FOREST')                    
     myLegend.get_title().set_fontsize('24')



     #Matplotlib --anotación de barras

       ax=g.ax #annotate axis = seaborn axis
       def annotateBars(row, ax=ax): 
       for p in ax.patches:
             ax.annotate("%.2f" % p.get_height(), (p.get_x() + p.get_width() / 2., p.get_height()),
                 ha='center', va='center', fontsize=11, color='gray', rotation=90, xytext=(0, 20),
                 textcoords='offset points')  verticales


     plot = df.apply(annotateBars, ax=ax, axis=1)

enter image description here

0
votes

This can now be plotted much more concisely

  1. Axes.bar_label automatically labels bars [since matplotlib 3.4]

    for container in ax.containers:
        ax.bar_label(container)
    
  2. Axes.legend includes fontsize and title_fontsize params [since matplotlib 3.0]

    ax.legend(fontsize=10, title='ACCURACY', title_fontsize=24)
    
  3. Also note that seaborn.factorplot has been renamed to seaborn.catplot [since seaborn 0.9]


Updated seaborn.catplot

colors = ['xkcd:windows blue', 'xkcd:orange red', 'xkcd:grey', 'xkcd:amber']  
g = sns.catplot(x='Stages', y='Accuracy', hue='Dataset', data=df,
                kind='bar', height=4, aspect=3, palette=colors, legend=False)

# auto-label bars
for container in g.ax.containers:
    g.ax.bar_label(container, fmt='%.2f', padding=2, rotation=90)

# add legend with custom font sizes
ax.legend(bbox_to_anchor=(0, 1.2, 1, 0.102), loc=10, ncol=4, fontsize=10,
          title='TOTAL ACCURACY AND PER STAGE-RANDOM FOREST', title_fontsize=24)

# redecorate
g.despine(right=False)
g.set_xlabels('')
g.set_ylabels('')
g.ax.set_yticklabels([])
updated catplot

Updated DataFrame.plot.bar

ax = df.pivot('Stages', 'Dataset', 'Accuracy').plot.bar(legend=False)

# auto-label bars
for container in ax.containers:
    ax.bar_label(container, fmt='%.2f', padding=3, rotation=90, size='small')

# add legend with custom font sizes
ax.legend(bbox_to_anchor=(0, 1.1, 1, 0.102), loc=10, ncol=4, fontsize='small',
          title='TOTAL ACCURACY AND PER STAGE-RANDOM FOREST', title_fontsize='xx-large')

# redecorate
sns.despine(right=False)
ax.set_yticklabels([])
plt.xticks(rotation=0)
updated dataframe plot