Issue
I wanted to get annotations based on the text which is appearing in the legend to a barplot. Please have a look at my minimal reproduction example:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
data = { 'cat1':[np.nan, 0.33, 0.25], 'cat2':[0.4, 0.33, np.nan], 'cat3':[np.nan, np.nan, 0.25]}
df = pd.DataFrame(data)
fig = plt.figure();
ax = df.plot.barh(stacked=True, figsize=(10,8));
ax.grid()
ax.legend()
# annotations:
for p in ax.patches:
left, bottom, width, height = p.get_bbox().bounds
if width > 0:
ax.annotate((str(round(width*100))+' %'), xy=(left+width/2, bottom+height/2),
ha='center', va='center')
What I get looks like this:
But I want an output like that (with the description of the legend as annotation in the single bars)
I tried to get the annotation text out of the ax.patches[0].get_label()
but it gives the output '_nolegend_'
Solution
- This can be accomplished more easily using
matplotlib.pyplot.bar_label
, which was added inmatplotlib 3.4.0
.- See this answer for additional details and examples with
.bar_label
.
- See this answer for additional details and examples with
:=
is an assignment expression, which requirespython >= 3.8
.[f'{l}: {v.get_width()*100:0.0f}%' if v.get_width() > 0 else '' for v in c]
without the assignment expression.
- Tested in
python 3.10
,pandas 1.4.3
,matplotlib 3.5.1
,seaborn 0.11.2
import pandas as pd
import seaborn as sns
# setup the dataframe
df = pd.read_csv('test.csv')
df.set_index('hyd_yr', inplace=True)
# new color palette
colors = sns.color_palette('husl', n_colors=len(df.columns))
# plot
ax = df.plot.barh(stacked=True, figsize=(20, 16), color=colors)
# extract the legend labels
handles, legend_labels = ax.get_legend_handles_labels()
# iterate through the zipped containers and legend_labels
for c, l in zip(ax.containers, legend_labels):
# customize the labels: only plot values greater than 0 and append the legend label
labels = [f'{l}: {w*100:0.0f}%' if (w := v.get_width()) > 0 else '' for v in c]
# add the bar annotation
ax.bar_label(c, labels=labels, label_type='center')
Original Answer
- See inline code comments
- When a stacked bar is plotted, the bottom patches,
p
, which correspond to the fist value in the legendlabels
, are plotted first. - Except when
i == 0
, increase the value ofcounter
by 1, based upon the length of the length ofdf
, and usecounter
to index the correct label fromlabels
.- In this case,
counter
will be0
for the first 31 patches, and then increment to1
for the 2nd 31 patches, and so on.- The counter needs to increment when all the rows (31) for each label, have been plotted.
- In this case,
- I'm going to use a different color palette for the legend, because there aren't enough colors in the current palette for all the columns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
# setup the dataframe
df = pd.read_csv('test.csv')
df.set_index('hyd_yr', inplace=True)
# new color palette
colors = sns.color_palette('husl', n_colors=len(df.columns))
# plot
ax = df.plot.barh(stacked=True, figsize=(20, 16), color=colors)
# extract the legend labels
handles, labels = ax.get_legend_handles_labels()
# annotations:
counter = 0 # counter is used to index legend labels
for i, p in enumerate(ax.patches, 0): #
if (i % len(df) == 0) & (i != 0): # reset counter to 0 based on length of df
counter += 1 # increment the counter
left, bottom, width, height = p.get_bbox().bounds
label = labels[counter] # index the correct label
if width > 0:
ax.annotate((f'{label}: {width*100:0.0f}%'), xy=(left+width/2, bottom+height/2), ha='center', va='center')
Real data in test.csv
hyd_yr,BM,HFA,HFZ,HM,HNA,HNZ,NWA,NWZ,NZ,SEA,SWA,SWZ,TB,TRM,WA,WS,WZ
1989,,,,,,,,0.0979020979020979,,,,,,,0.3006993006993007,,0.23776223776223776
1990,0.14835164835164835,,,,,,,,,,,,,,0.17582417582417584,,0.21428571428571427
1991,0.23626373626373626,0.08791208791208792,,,,,,,,,,,,,,,0.25824175824175827
1992,,,,0.18032786885245902,,,,,,,,,,,0.16393442622950818,,0.16393442622950818
1993,0.0989010989010989,,,0.12087912087912088,,,,,,,,,,,,,0.22527472527472528
1994,,,0.07142857142857142,,,,,,,0.09340659340659341,,,,,,,0.34615384615384615
1995,,,,0.1043956043956044,,,,0.0989010989010989,,,,,,,,,0.3241758241758242
1996,,0.12571428571428572,,,,,,,0.11428571428571428,,0.13142857142857142,,,,,,
1997,,,,0.08791208791208792,,,,,,,,,,,0.08791208791208792,,0.2032967032967033
1998,,,,,0.08241758241758242,,0.08791208791208792,,,,,,,,,,0.22527472527472528
1999,0.15934065934065933,,,,,,,,,,,,,0.09340659340659341,,,0.23076923076923078
2000,,,,,,,,0.11475409836065574,,,,,,,0.12021857923497267,,0.22404371584699453
2001,,,,,,,,,,,,0.11299435028248588,0.11299435028248588,,,,0.1751412429378531
2002,0.1043956043956044,,,,,,,0.17032967032967034,,,,,,,,,0.2032967032967033
2003,0.11538461538461539,0.11538461538461539,,,,,,,,,,,,,,,0.14285714285714285
2004,0.14207650273224043,,,,,,,,,,,,,0.12568306010928962,,,0.2185792349726776
2005,0.13736263736263737,,,,,,,0.13736263736263737,,,,,,0.2087912087912088,,,
2006,0.13186813186813187,,,,,,,0.15934065934065933,,,,,,0.13736263736263737,,,
2007,0.10989010989010989,,,,,,,,,,,0.16483516483516483,,,,,0.21428571428571427
2008,0.20765027322404372,,,,,,,0.08196721311475409,,,,,,,,,0.28415300546448086
2009,0.11731843575418995,,,,,,,0.12290502793296089,,,,,,0.10614525139664804,,,
2010,,,,,,,,,,,,0.10989010989010989,,,,0.12637362637362637,0.13186813186813187
2011,0.15254237288135594,,,,,,,,,,,0.0903954802259887,,,,,0.11864406779661017
2012,,,,0.10382513661202186,,,,0.18032786885245902,,,,,,,,,0.15300546448087432
2013,,,,,,0.13186813186813187,,,,,,,,0.15934065934065933,,,0.10989010989010989
2014,0.1043956043956044,,,,,,,,,,,0.23076923076923078,,,,,0.08241758241758242
2015,0.12290502793296089,,,,,,,,,,,,,0.1452513966480447,,,0.1340782122905028
2016,,,,,,,,,,,,,,0.09836065573770492,0.1366120218579235,,0.14207650273224043
2017,0.14285714285714285,,,0.14835164835164835,,,,,,,,,,,,,0.13736263736263737
2018,0.1043956043956044,,,,,,,,,,,,,0.12637362637362637,,,0.15934065934065933
2019,0.11363636363636363,,,,,,0.12121212121212122,0.12121212121212122,,,,,,,,,
Answered By - Trenton McKinney
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.