- Use
matplotlib.pyplot.bar_label
twice
- The annotation value is being determined based on whether the label is at the center of the bar or the edge of the bar.
- The other answer uses
x[0].
because there is only one group of stacked bars, but that won't work if there's more than one group on the x-axis.
- See this answer for more details and examples with
.bar_label
.
- Reshaping the dataframe should be a separate step from plotting
pandas.DataFrame.plot
uses matplotlib
as the default plotting backend, and has a number of parameters like rot
, xlabel
, ylabel
, and figsize
, for customizing the plot.
- Tested in
python 3.10
, pandas 1.3.4
, matplotlib 3.5.0
df = pd.DataFrame(data=dat, columns=names_col)
dft = df.set_index('Count').T
axe = dft.plot(kind='bar', stacked=True, figsize=(16,9), rot=0)
for x in axe.containers:
axe.bar_label(x, label_type='edge', weight='bold')
axe.bar_label(x, label_type='center', weight='bold', color='white')

- Here's a more thorough example with multiple groups
- The other answer does not place the middle annotations for the second group of bars.
# test data
data = {'Matching': [56935, 17610], 'Mismatching': [100587, 13794], 'Test': [33139, 23567]}
df = pd.DataFrame(data=data, index=['Freq', 'Freq2'])
axe = df.plot(kind='bar', stacked=True, figsize=(16,9), rot=0)
for x in axe.containers:
axe.bar_label(x, label_type='edge', weight='bold')
axe.bar_label(x, label_type='center', weight='bold', color='white')

Add only the total to the top of the bars
- Add a new colon for the sum of the rows, to use for annotations
df['tot'] = df.sum(axis=1)
display(df)
Matching Mismatching Test tot
Freq 56935 100587 33139 190661
Freq2 17610 13794 23567 54971
# plot
axe = df.iloc[:, :3].plot(kind='bar', stacked=True, figsize=(16,9), rot=0)
# annotate
for x in axe.containers:
axe.bar_label(x, label_type='center', weight='bold', color='white')
# resuse x from the for loop, the last x is the top set of bar patches
axe.bar_label(x, labels=df['tot'], label_type='edge', weight='bold')
