I\'m trying to \"robustly\" center the data labels in a stacked bar chart. A simple code and the result are given below. As you can see, the data labels aren\'t really cente
pandas.DataFrame
is the easiest way to plot a stacked bar plot.matplotlib.axes.Axes
or a numpy.ndarray
of them..patches
method unpacks a list of matplotlib.patches.Rectangle objects, one for each of the sections of the stacked bar.
.Rectangle
has methods for extracting the various values that define the rectangle..Rectangle
is in order from left to right, and bottom to top, so all the .Rectangle
objects, for each level, appear in order, when iterating through .patches
.label_text = f'{height}'
, so any additional text can be added as needed, such as label_text = f'{height}%'
import pandas as pd
import matplotlib.pyplot as plt
A = [45, 17, 47]
B = [91, 70, 72]
C = [68, 43, 13]
# pandas dataframe
df = pd.DataFrame(data={'A': A, 'B': B, 'C': C})
df.index = ['C1', 'C2', 'C3']
A B C
C1 45 91 68
C2 17 70 43
C3 47 72 13
plt.style.use('ggplot')
ax = df.plot(stacked=True, kind='bar', figsize=(12, 8), rot='horizontal')
# .patches is everything inside of the chart
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 height of the bar is the data value and can be used as the label
label_text = f'{height}' # f'{height:.2f}' to format decimal values
# ax.text(x, y, text)
label_x = x + width / 2
label_y = y + height / 2
# plot only when height is greater than specified value
if height > 0:
ax.text(label_x, label_y, label_text, ha='center', va='center', fontsize=8)
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0.)
ax.set_ylabel("Count", fontsize=18)
ax.set_xlabel("Class", fontsize=18)
plt.show()
kind='barh'
label_text = f'{width}'
Why you wrote va="bottom"
? You have to use va="center"
.