Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stacked Bar Chart with Centered Labels

Tags:

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() 

like image 449
Bruno Avatar asked Dec 23 '16 06:12

Bruno


People also ask

How do I rearrange a stacked bar chart in Excel?

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.

What is the difference between a grouped and a stacked bar chart?

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.


1 Answers

  • The following method is more succinct, and easily scales.
  • Putting the data into a pandas.DataFrame is the easiest way to plot a stacked bar plot.
  • Using pandas.DataFrame.plot.bar(stacked=True), or pandas.DataFrame.plot(kind='bar', stacked=True), is the easiest way to plot a stacked bar plot.
    • This method returns a matplotlib.axes.Axes or a numpy.ndarray of them.
  • Since 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.

Imports & Test DataFrame

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 

Updated for matplotlib v3.4.2

  • Use matplotlib.pyplot.bar_label
    • Will automatically center the values in the bar.
    • See this answer for additional details about .bar_label()
  • See the matplotlib: Bar Label Demo page for additional formatting options.
  • Tested with pandas v1.2.4, which is using matplotlib as the plot engine.
  • If some sections of the bar plot will be zero, see my answer, which shows how to customize the 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') 

enter image description here

Annotation Resources - from matplotlib v3.4.2

  • Adding value labels on a matplotlib bar chart - Contains a list of many .bar_label examples
  • How to annotate each segment of a stacked bar chart
  • How to plot and annotate multiple data columns in a seaborn barplot
  • How to annotate a seaborn barplot with the aggregated value
  • stack bar plot in matplotlib and add label to each section
  • How to add multiple annotations to a barplot
  • How to plot and annotate a grouped bar chart

Original Answer

  • Using the .patches method unpacks a list of matplotlib.patches.Rectangle objects, one for each of the sections of the stacked bar.
    • Each .Rectangle has methods for extracting the various values that define the rectangle.
    • Each .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.
  • The labels are made using an f-string, 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.

Plot

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() 

enter image description here

  • To plot a horizontal bar:
    • kind='barh'
    • label_text = f'{width}'
    • if width > 0:
  • Attribution: jsoma/chart.py
like image 88
Trenton McKinney Avatar answered Sep 22 '22 03:09

Trenton McKinney