Issue
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
Solution
- As of
matplotlib 3.4.0
it is better to usematplotlib.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. - Tested in
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)
- Annotation with two custom labels (
'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)
- The issue with the existing code is there are 9 patches, as can be seen with
print(patches)
orlist(patches)
, which can be resolved by selecting only the patches with a height greater than 0.- This occurs because
hue
is being used, but there is only one value in'groups'
for each value in'rules'
.
- This occurs because
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")
Answered By - Trenton McKinney
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.