1
d = {'X':[1,2,3,4],'A': [50,40,20,60], '% of Total in A':[29.4,23.5,11.8,35.3] , 'B': [25,10,5,15], '% in A' :[50,25,25,25]}
df = pd.DataFrame(d)
ax = df.plot(x='X',y="A", kind="bar")
df.plot(x='X', y="B", kind="bar", ax=ax,color='C2')

   X   A  % of Total in A   B  % in A
0  1  50             29.4  25      50
1  2  40             23.5  10      25
2  3  20             11.8   5      25
3  4  60             35.3  15      25

I have the above dataframe and I know how to draw a stacked bar plot based on two columns A and B. enter image description here

How do I add value labels on top of the bar such as for X=0, I want to label 50 (29.4% of the total) above the blue bar, and 25 (50% in group) above the green bar within the blue bar.

Any help is appreciated.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Iwishworldpeace
  • 478
  • 4
  • 12

2 Answers2

5

The first bars are stored in ax.containers[0], the second in ax.containers[1]. You can call ax.bar_label(...) using these containers together with a list of the corresponding labels.

By the way, you are missing x= in the second bar plot.

from matplotlib import pyplot as plt
import pandas as pd

d = {'X': [1, 2, 3, 4], 'A': [50, 40, 20, 60], '% of Total in A': [29.4, 23.5, 11.8, 35.3], 'B': [25, 10, 5, 15], '% in A': [50, 25, 25, 25]}
df = pd.DataFrame(d)
ax = df.plot(x='X', y="A", kind="bar")
df.plot(x='X', y="B", kind="bar", color='C2', ax=ax)

ax.bar_label(ax.containers[0], labels=df['% of Total in A'])
ax.bar_label(ax.containers[1], labels=df['% in A'], color='white')
plt.show()

pandas bar plot with labels

To further accentuate that B is a part of A, you could give them the same color, and hatch B. For example:

ax = df.plot(x='X', y="A", kind="bar", color='dodgerblue')
df.plot(x='X', y="B", kind="bar", facecolor='dodgerblue', hatch='xx', rot=0, ax=ax)

ax.bar_label(ax.containers[0], labels=[f'{p} %' for p in df['% of Total in A']])
ax.bar_label(ax.containers[1], labels=[f'{p} %' for p in df['% in A']], color='white')
for spine in ['top', 'right']:
    ax.spines[spine].set_visible(False)

hatching B

JohanC
  • 71,591
  • 8
  • 33
  • 66
2
ax = df.plot(kind='bar', x='X', y=['A', 'B'], stacked=True, rot=0, color=['tab:blue', 'tab:green'])
ax.legend(bbox_to_anchor=(1, 1.02), loc='upper left')

# specify the columns to uses for alternate labels, in order based on the order of y=
cols = ['% of Total in A', '% in A']

for c, col in zip(ax.containers, cols):
    
    labels = df[col]

    # Use the alternate column for the labels instead of the bar height (or width of horizontal bars)
    labels = [f'{v}%' for v in labels]
    
    # remove the labels parameter if it's not needed for customized labels
    ax.bar_label(c, labels=labels, label_type='edge')

ax.margins(y=0.1)

enter image description here

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158