Seaborn Barplot - Inconsistence when displaying values

Viewed 135

For a multi-group bar plot in Seaborn, I would like to add text which is reffered from the int_txt on top each of the bar plot. However, the text is not placed as intended.

For example, the code below

import seaborn as sns
import pandas as pd
from matplotlib import pyplot as plt


# Create an example dataframe
data = {'pdvalue': [1, 1, 1, 1, 4, 4, 4, 4, 2, 2, 2, 2, 8, 8, 8, 8],
        'xval': [0, 0, 0.5, 0.5, 0.2, 0, 0.2, 0.2, 0.3, 0.3, 0.4, 0.1, 1, 1.1, 3, 1],
        'int_txt': [11, 14, 4, 5.1, 1, 2, 5.1, 1, 2, 4, 1, 3, 6, 6, 2, 3],
        'group': ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd']}
df = pd.DataFrame(data)

df['int_txt'] = df['int_txt'].round(0).astype(int)
df=df.sort_values(by='pdvalue', ascending=True)
g = sns.barplot (data=df,x="pdvalue",y="xval",hue="group",)

for idx,p in enumerate(g.patches):
    if p.get_height()!=0:
        val_me=df['int_txt'][idx]
        g.annotate(format(val_me, '.1f'),
                       (p.get_x() + p.get_width() / 2., p.get_height()),
                       ha = 'center', va = 'center',
                       xytext = (0, 9),
                       textcoords = 'offset points')
plt.show()

will produced

enter image description here

Whereas, the expected output shall be something like

enter image description here

The appended text is based on the look-up table

enter image description here

and for any xval equal to zero, no text will be appended.

May I know where did I do wrong?

1 Answers

You didn't do anything wrong really. It's just sns plots the bars by hue first. To see this do:

for idx,p in enumerate(g.patches):
    # annotate the enumeration
    g.annotate(format(idx, '.1f'),
                   (p.get_x() + p.get_width() / 2., p.get_height()),
                   ha = 'center', va = 'center',
                   xytext = (0, 9),
                   textcoords = 'offset points')

And you see (notice the enumeration on top)

enter image description here

One way around is to sort your data by hue column, then access with .iloc:

# sort by group first
df=df.sort_values(by=['group','pdvalue'], ascending=True)

g = sns.barplot (data=df,x="pdvalue",y="xval",hue="group",)
for idx,p in enumerate(g.patches):
    if p.get_height()!=0:
        # access with `iloc`, not `loc`
        val_me=df['int_txt'].iloc[idx]
        g.annotate(format(val_me, '.1f'),
                   (p.get_x() + p.get_width() / 2., p.get_height()),
                   ha = 'center', va = 'center',
                   xytext = (0, 9),
                   textcoords = 'offset points')

And you would get the expected annotation:

enter image description here

Related