1

I've been struggling to add a simple thing as value labels to a Pandas dataframe bar plot. I've looked at over 20 threads (with these three being the most helpful - How can I display text over columns in a bar chart in matplotlib?, matplotlib advanced bar plot and Python pandas / matplotlib annotating labels above bar chart columns and nothing is working.

My data is not at all complex. The dataframe structure is:

+------+----------+----------+----------+----------+-------+
| Year | Product1 | Product2 | Product3 | Product4 | Total |
+------+----------+----------+----------+----------+-------+
| 2005 |      123 |      123 |      123 |      123 |   492 |
| 2006 |      111 |      111 |      111 |      111 |   444 |
+------+----------+----------+----------+----------+-------+

with year being the index for the dataframe.

The representation I'm looking for is simple. A stacked bar chart of all the products, with only the value label for 'Total' being displayed at the top of the stacked column (I don't want to represent 'Total' in the chart).

The code that I've currently is:

fig,ax = plt.subplots()
ax =df.ix[:,df.columns.difference(['Total'])].plot.bar(stacked=True, colormap='coolwarm',figsize=(14,12),ax=ax)
ax.set_ylabel("Total sales", fontsize=14)
ax.set_xlabel("Year", fontsize=14)
ax.legend(loc='best', fancybox=True, framealpha=0.5)
for i, label in enumerate(list(df.index)):
    score = df.ix[label]['Total']
    ax.annotate(str(score), (i - 0.2, score))
fig = ax.get_figure()
fig.savefig('sumplot.png',dpi=100,bbox='Tight')

What I'm getting right now are values that are way off in the sky. I think this is because the height is being determined by the 'Total'+the values of all the other columns? Is there anyway to modify this so that the height is just the height of 'Total'? Fiddling with the value of score in the ax.annotate snippet doesnt help because in there is wide variation in the data values (the data structure above is just representative - not actual data)

Community
  • 1
  • 1
Scubed
  • 116
  • 5
  • for me it is exactly as you describe it: `the height is just the height of 'Total'`. Where would you like to have annotations? – MaxU - stand with Ukraine Mar 16 '16 at 22:09
  • Ok, what is happening in my chart is that the annotations are happening at a height = height of total + height of all the products in the stacked column. I just want the annotations to happen at the height of the total – Scubed Mar 17 '16 at 06:33
  • This is now much simpler with the new built-in `bar_label` method: https://stackoverflow.com/a/68107610/13138364 – tdy Feb 25 '22 at 03:43

1 Answers1

0

I ran the following code, and didn't modify much. All I did was set the year as index of the dataframe, and update th .ix to the .loc method call.

Your problem might have been caused by not plotting against the year, but against a numerical index, which would then include the year as a value to be plotted on top of the data you are interested in.

If I understand your question correctly the following code produces your desired results. I will ad a picture of my plot for you to verify.

Plot of data

import matplotlib.pyplot as plt
import pandas as pd

data = {
    "Year": [2005, 2006],
    "Product1": [123, 111],
    "Product2": [123, 111],
    "Product3": [123, 111],
    "Product4": [123, 111],
    "Total": [492, 444]
}

df = pd.DataFrame(data).set_index("Year", drop=True)

fig,ax = plt.subplots()
ax =df.loc[:,df.columns.difference(['Total'])].plot.bar(stacked=True, colormap='coolwarm',figsize=(14,12),ax=ax)
ax.set_ylabel("Total sales", fontsize=14)
ax.set_xlabel("Year", fontsize=14)
ax.legend(loc='best', fancybox=True, framealpha=0.5)
for i, label in enumerate(list(df.index)):
    score = df.loc[label]['Total']
    ax.annotate(str(score), (i - 0.2, score))
fig = ax.get_figure()
fig.savefig('sumplot.png',dpi=100,bbox='Tight')
Jordy
  • 1
  • 2