Adding labels to stacked bar chart
<p>I'm plotting a cross-tabulation of various offices within certain categories. I'd like to put together a horizontal stacked bar chart where each office and its value is labeled. </p>

<p>Here's some example code: </p>

<pre><code>df = pd.DataFrame({'office1': pd.Series([1,np.nan,np.nan], index=['catA', 'catB', 'catC']),
              'office2': pd.Series([np.nan,8,np.nan], index=['catA', 'catB', 'catC']),
              'office3': pd.Series([12,np.nan,np.nan], index=['catA', 'catB', 'catC']),
              'office4': pd.Series([np.nan,np.nan,3], index=['catA', 'catB', 'catC']),
              'office5': pd.Series([np.nan,5,np.nan], index=['catA', 'catB', 'catC']),
              'office6': pd.Series([np.nan,np.nan,7], index=['catA', 'catB', 'catC']),
              'office7': pd.Series([3,np.nan,np.nan], index=['catA', 'catB', 'catC']),
              'office8': pd.Series([np.nan,np.nan,11], index=['catA', 'catB', 'catC']),
              'office9': pd.Series([np.nan,6,np.nan], index=['catA', 'catB', 'catC']),
              })
ax = df.plot.barh(title="Office Breakdown by Category", legend=False, figsize=(10,7), stacked=True)
</code></pre>

<p>This gives me a fine starting point: </p>

<p><a href="http://i.stack.imgur.com/d4Jeu.png" rel="nofollow"><img src="http://i.stack.imgur.com/d4Jeu.png" alt="Stacked Bar Chart Example"></a></p>

<p>However, what I'd like to have is this: 
<a href="http://i.stack.imgur.com/a3FwK.png" rel="nofollow"><img src="http://i.stack.imgur.com/a3FwK.png" alt="Bar Chart with labels"></a></p>

<p>After some research, I came up with the following code that correctly lines up labels on the 'category' axis: </p>

<pre><code>def annotateBars(row, ax=ax):
    for col in row.index:
        value = row[col]
        if (str(value) != 'nan'):
            ax.text(value/2, labeltonum(row.name), col+","+str(value))

def labeltonum(label):
    if label == 'catA':
        return 0
    elif label == 'catB':
        return 1
    elif label == 'catC':
        return 2

df.apply(annotateBars, ax=ax, axis=1)
</code></pre>

<p>But this doesn't factor in the "stacking" of the bars. I've also tried iterating through the <code>patches</code> container returned by the plot command (which can let me retrieve x &amp; y positions of each rectangle), but I then lose any connection to the office labels. </p>

<p>Figured it out. If I iterate through the columns of each row of the dataframe I can build up a list of the labels I need that matches the progression of the rectangles in <code>ax.patches</code>. Solution below: </p>

<pre><code>labels = []
for j in df.columns:
    for i in df.index:
        label = str(j)+": " + str(df.loc[i][j])
        labels.append(label)

patches = ax.patches

for label, rect in zip(labels, patches):
    width = rect.get_width()
    if width &gt; 0:
        x = rect.get_x()
        y = rect.get_y()
        height = rect.get_height()
        ax.text(x + width/2., y + height/2., label, ha='center', va='center')
</code></pre>

<p>Which, when added to the code above, yields:</p>

<p><a href="http://i.stack.imgur.com/ihchS.png" rel="nofollow"><img src="http://i.stack.imgur.com/ihchS.png" alt="Properly annotated bar chart"></a></p>

<p>Now to just deal with re-arranging labels for bars that are too small...</p>

python pandas matplotlib