Skip to content

Instantly share code, notes, and snippets.

@jsoma
Last active July 27, 2021 10:04
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jsoma/c61e56819e4ae315ad5d194a630ccb23 to your computer and use it in GitHub Desktop.
Save jsoma/c61e56819e4ae315ad5d194a630ccb23 to your computer and use it in GitHub Desktop.
A perfect easy beautiful simple way to label a stacked bar chart in Python using pandas/matplotlib. Put this in your Jupyter notebook!
#
# WAIT!!!
# WAIT!!!
# You probably don't need this any more! Newer versions of matplotlib
# can do this with built-in magic. See these StackOverflow answers:
#
# https://stackoverflow.com/questions/41296313/stacked-bar-chart-with-centered-labels/60895640#60895640
# https://stackoverflow.com/questions/28931224/adding-value-labels-on-a-matplotlib-bar-chart/67561982#67561982
#
# Big thanks to @trenton3983 for keeping us all up to date!
#
%matplotlib inline
import pandas as pd
df = pd.DataFrame({
'cat': [3, 5, 4, 3, 3, 23, 4],
'dog': [3, 23, 4, 3, 5, 4, 3],
'mouse': [3, 23, 4, 3, 5, 4, 3]
})
# Save the chart that's drawn
ax = df.plot(stacked=True, kind='barh', figsize=(10, 5))
# .patches is everything inside of the chart, lines and
# rectangles and circles and stuff. In this case we only
# have rectangles!
for rect in ax.patches:
# Find where everything is located
height = rect.get_height()
width = rect.get_width()
x = rect.get_x()
y = rect.get_y()
# The width of the bar is also not pixels, it's the
# number of animals. So we can use it as the label!
label_text = width
# ax.text(x, y, text)
label_x = x + width / 2
label_y = y + height / 2
ax.text(label_x, label_y, label_text, ha='center', va='center')
@trenton3983
Copy link

  • This is great, thank you for the effort.
  • I think removing padding, and changing ha to center, makes it easier.
    # ax.text(x, y, text)
    label_x = x + width / 2
    label_y = y + height / 2
    
    # only plot labels if the width is greater than 0 (or some other value)
    if width > 0: 
        ax.text(label_x, label_y, label_text, ha='center', va='center')

@jsoma
Copy link
Author

jsoma commented Oct 5, 2020

You're so right! Updated!

@sarah-dodamead
Copy link

Hi! This was so helpful for me! I was wondering if you guys had advice on getting the label_text = to equal a different column in the data frame?Such that each data label is a cell in the same row as each rectangle but a different column.

I have tried the following and a few other random code attempts with no luck. Any advice?

ra_list = dataset['RA'].tolist()
#label_text = ra_list

My full code is here:

The following code to create a dataframe and remove duplicated rows is always executed and acts as a preamble for your script:

dataset = pandas.DataFrame(Industrial Opps.OpportunityName, MP Count, REQ_DATE, RA)

dataset = dataset.drop_duplicates()

Paste or type your script code here:

import matplotlib.pyplot as plt
ax=dataset.groupby(['REQ_DATE','Industrial Opps.OpportunityName']).sum().unstack().plot(kind='bar', y='MP Count', stacked=True, figsize=(20, 10))
ax.set_xlabel("Demand Date", labelpad=20, weight='bold', size=25)
ax.set_ylabel("MP Count", labelpad=20, weight='bold', size=25)

.patches is everything inside of the chart, lines and

rectangles and circles and stuff. In this case we only

have rectangles!

for rect in ax.patches:
# Find where everything is located
height = rect.get_height()
width = rect.get_width()
x = rect.get_x()
y = rect.get_y()

# The width of the bar is also not pixels, it's the
# number of animals. So we can use it as the label!
#label_text = height
label_text = 'RA'
#ra_list = dataset['RA'].tolist()
#label_text = ra_list

# ax.text(x, y, text)
label_x = x + width / 2
label_y = y + height / 2
#ax.text(label_x, label_y, label_text, ha='center', va='center')
    # only plot labels if the width is greater than 0 (or some other value)
if height > 0: 
    ax.text(label_x, label_y, label_text, ha='center', va='center')

plt.show()

@trenton3983
Copy link

Hi! This was so helpful for me! I was wondering if you guys had advice on getting the label_text = to equal a different column in the data frame?Such that each data label is a cell in the same row as each rectangle but a different column.

  • You will need to find a way to select the data from the other column, if the index is the same as the column plotted, then you can enumerate the loop, and use i to select the data from the other column.
for i, rect in enumerate(ax.patches):
    desired_value = df.iloc[i, desire_col_idx]

@trenton3983
Copy link

You're so right! Updated!

  • Hey Jonathan, this was the go to answer for many annotation questions. However, it's position is now usurped, thanks to an update in matplotlib v3.4.2.
  • See the update here and here.
  • Thanks again for your original great solution.

@jsoma
Copy link
Author

jsoma commented May 17, 2021

Praise be to the matplotlib gods! Thanks a zillion for posting this, just updated the original!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment