Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
438 views
in Technique[技术] by (71.8m points)

python 3.x - Annotate gridspec bar graph above bars

I've created a bar graph, and would like to add some text indicating the value above each bar, however since I'm using gridspec I'd like different values present in different axes. Here's some code to reproduce the graph I've got at the moment;

a = [1e+6, 3e+5, 3e+5, -4e+5, 5e+5, 1.7e+6]
idx = ['a', 'b', 'c', 'd', 'e', 'f']
series = pd.Series(a)
# add gridspec figure
fig = plt.figure(constrained_layout=True)
gs = fig.add_gridspec(ncols=1,nrows=2, height_ratios=[1,2])
ax1 = fig.add_subplot(gs[0,0])
ax2 = fig.add_subplot(gs[1,0])
# prepare neg,pos,neu values for different colors
pos = np.maximum(series, 0)
pos.iloc[0], pos.iloc[-1] = 0, 0
neg = np.minimum(series, 0)
neg.iloc[0], neg.iloc[-1] = 0, 0
neu = np.zeros(neg.shape)
neu[0], neu[-1] = series.iloc[0], series.iloc[-1]
df = pd.DataFrame({'pos':pos,'neg':neg,'neu':neu})
# create bottom buffer for middle values
bottom = series.cumsum().shift(1).fillna(0)
# prepare steps for lines between bars in graph
step = bottom.reset_index(drop=True).repeat(3).shift(-1)
step[1::3] = np.nan
# manually put 0 as last bottom buffer
bottom.iloc[-1] = 0
# create bar plot
df.plot(kind='bar', ax=ax1, stacked=True, bottom=bottom, color=['g', 'r', 'b'], legend=None)
df.plot(kind='bar', ax=ax2, stacked=True, bottom=bottom, color=['g', 'r', 'b'], legend=None)
ax1.plot(step.index, step.values, 'k', linewidth=0.5)
ax2.plot(step.index, step.values, 'k', linewidth=0.5)
# set x/y-ticks for both axes
minv = min([series.iloc[0], series.iloc[-1]])
maxv = max([series.iloc[0], series.iloc[-1]])
ax1.axis(ymin=minv, ymax=maxv * 1.1)
ax2.axis(ymin=0, ymax=maxv * 1.02)
ax1.set_yticklabels([x / 1e+03 for x in ax1.get_yticks()])
ax2.set_yticklabels([x / 1e+03 for x in ax2.get_yticks()])
ax1.set_xticklabels('')
ax2.set_xticklabels(idx, rotation = 0)
ax2.set_ylabel('thousands EUR', position=(0.5,0.75))

Running the code above will give the resulting graph;

enter image description here

I would like ax2 to include text above the first bar, and ax1 to include text above the other bars. So I start with trying to add text to all bars in ax1 by adding the two rows below;

for p in ax1.patches:
    ax1.annotate(int(p.get_height()/1000), xy = (p.get_x(), p.get_height() * 1.005))

enter image description here

This somehow destroys the values on the y-axis, and creates a gap between the two subplots in my gridspec. Also, since I haven't added the bottom, the middle bars doesn't get correct coordinates for where to place the text. I try to add bottom by replacing the for-loop with

for i,p in enumerate(ax1.patches):
    ax1.annotate(int(p.get_height()/1000), xy = (p.get_x(), bottom.iloc[i]+p.get_height() * 1.005))

However since len(ax1.patches)=18 (which I don't understand why), and len(bottom)=6, the indexer is out of bounds.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
等待大神答复

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...