I'm trying to "robustly" center the data labels in a stacked bar chart. A simple code example and the result are given below. As you can see, the data labels aren't really centered in all rectangles. What am I missing?
import numpy as np import matplotlib.pyplot as plt A = [45, 17, 47] B = [91, 70, 72] fig = plt.figure(facecolor="white") ax = fig.add_subplot(1, 1, 1) bar_width = 0.5 bar_l = np.arange(1, 4) tick_pos = [i + (bar_width / 2) for i in bar_l] ax1 = ax.bar(bar_l, A, width=bar_width, label="A", color="green") ax2 = ax.bar(bar_l, B, bottom=A, width=bar_width, label="B", color="blue") ax.set_ylabel("Count", fontsize=18) ax.set_xlabel("Class", fontsize=18) ax.legend(loc="best") plt.xticks(tick_pos, ["C1", "C2", "C3"], fontsize=16) plt.yticks(fontsize=16) for r1, r2 in zip(ax1, ax2): h1 = r1.get_height() h2 = r2.get_height() plt.text(r1.get_x() + r1.get_width() / 2., h1 / 2., "%d" % h1, ha="center", va="bottom", color="white", fontsize=16, fontweight="bold") plt.text(r2.get_x() + r2.get_width() / 2., h1 + h2 / 2., "%d" % h2, ha="center", va="bottom", color="white", fontsize=16, fontweight="bold") plt.show()
Under Chart Tools, on the Design tab, in the Data group, click Select Data. In the Select Data Source dialog box, in the Legend Entries (Series) box, click the data series that you want to change the order of. Click the Move Up or Move Down arrows to move the data series to the position that you want.
In a bar chart you can choose to show the bars grouped or stacked. In a grouped bar chart values from various categories are compared. A stacked bar chart shows the ratio of individual data to the whole.
pandas.DataFrame
is the easiest way to plot a stacked bar plot.pandas.DataFrame.plot.bar(stacked=True)
, or pandas.DataFrame.plot(kind='bar', stacked=True)
, is the easiest way to plot a stacked bar plot. matplotlib.axes.Axes
or a numpy.ndarray
of them.seaborn
is just a high-level API for matplotlib
, these solutions also work with seaborn
plots, as shown in How to annotate a seaborn barplot with the aggregated value.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
matplotlib v3.4.2
matplotlib.pyplot.bar_label
.bar_label()
pandas v1.2.4
, which is using matplotlib
as the plot engine.labels
for .bar_label()
.ax.bar_label(c, fmt='%0.0f', label_type='center')
will change the number format to show no decimal places, if needed.ax = df.plot(kind='bar', stacked=True, figsize=(8, 6), rot=0, xlabel='Class', ylabel='Count') for c in ax.containers: # Optional: if the segment is small or 0, customize the labels labels = [v.get_height() if v.get_height() > 0 else '' for v in c] # remove the labels parameter if it's not needed for customized labels ax.bar_label(c, labels=labels, label_type='center')
matplotlib v3.4.2
.bar_label
examples.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}%'
label_text = f'{height:0.0f}'
will display numbers with no decimal places.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}'
if width > 0:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With