I would like to print some text on the right-side of every stacked bar. I have found a way to do it, by annotating, but there are some problems: the autolabel
function seems to me like a very superfluous way to annotate, is there anything simpler for achieving the same visual effect, easier? And more importantly, how can I fix this text overrunning the right side of the figure, as you can see below? I have tried to subplots_adjust
, but didn't quite work...
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
labels = ['G1', 'G2', 'G3', 'G4', 'G5']
men_means = [20, 34, 30, 35, 27]
women_means = [25, 32, 34, 20, 25]
x = np.arange(len(labels)) # the x-axis label locations
width = 0.35 # the width of the bars
fig, ax = plt.subplots()#(figsize=(6, 4), dpi=200)
# FOR SIDE-BY-SIDE plotting:
# rects1 = ax.bar(x - width/2, men_means, width, label='Men')
# rects2 = ax.bar(x + width/2, women_means, width, label='Women')
rects1 = ax.bar(x, men_means, width, label='Men')
rects2 = ax.bar(x, women_means, width, bottom=men_means, label='Women')
# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(x)
ax.set_xticklabels(labels)
ax.legend()
def autolabel(rects):
for rect in rects:
height = rect.get_height()
ax.annotate('{}'.format(height),
xy=(rect.get_x() + 3 * rect.get_width() / 2, rect.get_y() + height / 2),
xytext=(0, -5),
textcoords="offset points",
ha='center', va='center')
autolabel(rects1)
autolabel(rects2)
fig.subplots_adjust(left=0.9, right=1.9, top=0.9, bottom=0.1)
plt.show()
If you don't mind your rightmost bar being slightly off-centre from the tick label, you could just move it to the left slightly so that your label fits.
If you print what gets returned by the call to bar()
you see it's a BarContainer
object with 5 artists:
<BarContainer object of 5 artists>
... which you can iterate over:
Rectangle(xy=(-0.175, 0), width=0.35, height=20, angle=0)
Rectangle(xy=(0.825, 0), width=0.35, height=34, angle=0)
Rectangle(xy=(1.825, 0), width=0.35, height=30, angle=0)
Rectangle(xy=(2.825, 0), width=0.35, height=35, angle=0)
Rectangle(xy=(3.825, 0), width=0.35, height=27, angle=0)
Each Rectangle
object has a set_xy() method. So you can move the final upper and lower bar in the following way:
bar, bar2 = rects1[4], rects2[4]
bar.set_xy((bar.get_x()-0.05,bar.get_y()))
bar2.set_xy((bar2.get_x()-0.05,bar2.get_y()))
by putting the above code just below your
rects1 = ax.bar(x, men_means, width, label='Men')
rects2 = ax.bar(x, women_means, width, bottom=men_means, label='Women')
and by removing your call to subplots_adjust()
, and using tight_layout()
instead, I was able to achieve this:
Alternatively, if you don't mind the spacing between the label and the bar reducing you could just change rect.get_x() + 3
to rect.get_x() + 2.5
in the autolabel()
function, to give you this: