Reputation: 880
I have this dataframe
rules count percentage groups weight
A 15 24% 1 10
B 5 2% 2 30
C 25 50% 3 50
I have the following code:
sns.set(rc={'figure.figsize':(18,9.5)})
plots = sns.barplot(x="rules", y="count", data=df, hue=df['groups'], dodge=False)
percentage = df['percentage'].tolist()
weight = df['weight'].tolist()
patches = plots.patches
for i in range(len(patches)):
x = patches[i].get_x() + patches[i].get_width()/2
y = patches[i].get_height()+.09
plots.annotate('{:.1f}%'.format(percentage[i]), (x, y), ha='center', va='bottom', size=14)
plots.annotate('{:.0f}'.format(weight[i]), (x, y), ha='center', va='top', color='white', size=15, fontweight="bold")
and I get the following error when trying to annotate the barchart with the percentage.
Inside the barchart is another number corresponding to the weight column in the df.
IndexError Traceback (most recent call last)
<ipython-input-120-0ef14f891711> in <module>()
7 x = patches[i].get_x() + patches[i].get_width()/2
8 y = patches[i].get_height()+.09
----> 9 plots.annotate('{:.1f}%'.format(percentage[i]), (x, y), ha='center', va='bottom', size=14)
10 plots.annotate('{:.0f}'.format(weight[i]), (x, y), ha='center', va='top', color='white', size=15, fontweight="bold")
11 plots.set_xticklabels(plots.get_xticklabels(), rotation=90, fontsize=15)
IndexError: list index out of range
Upvotes: 2
Views: 1896
Reputation: 62373
matplotlib 3.4.0
it is better to use matplotlib.pyplot.bar_label
, as explained in How to add value labels on a bar chart. However, that answer doesn't explain how to get both annotations.python 3.10
, pandas 1.4.2
, matplotlib 3.5.1
, seaborn 0.11.2
import pandas as pd
import seaborn as sns
# test data
data = {'rules': ['A', 'B', 'C'], 'count': [15, 5, 25],
'percentage': ['24%', '2%', '50%'], 'groups': [1, 2, 3], 'weight': [10, 30, 50]}
df = pd.DataFrame(data)
# plot
ax = sns.barplot(x="rules", y="count", data=df, hue='groups', dodge=False)
# since you are using hue, there are multiple containers
for c in ax.containers:
# set the bar label based on the y-axis
ax.bar_label(c, label_type='center', padding=1)
# add an annotations with custom labels
ax.bar_label(c, labels=df.percentage, label_type='edge', padding=1)
# pad the spacing between the number and the edge of the figure
ax.margins(y=0.1)
'weight'
and 'percentage'
).ax = sns.barplot(x="rules", y="count", data=df, hue='groups', dodge=False)
# since you are using hue, there are multiple containers
for c in ax.containers:
# add an annotations with custom labels
ax.bar_label(c, labels=df.weight, label_type='center')
# add an annotations with custom labels
ax.bar_label(c, labels=df.percentage, label_type='edge', padding=1)
ax.margins(y=0.1)
print(patches)
or list(patches)
, which can be resolved by selecting only the patches with a height greater than 0.
hue
is being used, but there is only one value in 'groups'
for each value in 'rules'
.plots = sns.barplot(x="rules", y="count", data=df, hue=df['groups'], dodge=False)
percentage = df['percentage'].tolist()
weight = df['weight'].tolist()
patches = plots.patches
# select patches with a height > 0
patches = [p for p in patches if p.get_height() > 0]
for i, p in enumerate(patches):
x = p.get_x() + patches[i].get_width()/2
y = p.get_height()+.09
plots.annotate(f'{percentage[i]}', (x, y), ha='center', va='bottom', size=14)
plots.annotate(f'{weight[i]:.0f}', (x, y), ha='center', va='top', color='white', size=15, fontweight="bold")
Upvotes: 4