- 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]
, orpandas.DataFrame.plot[kind='bar', stacked=True]
, is the easiest way to plot a stacked bar plot.- This method returns a
matplotlib.axes.Axes
or anumpy.ndarray
of them.
- This method returns a
- Since
seaborn
is just a high-level API formatplotlib
, these solutions also work withseaborn
plots, as shown in How to annotate a seaborn barplot with the aggregated value. - For horizontal stacked bars, see Horizontal stacked bar plot and add labels to each section
- Tested in
python 3.10
,pandas 1.4.2
,matplotlib 3.5.1
,seaborn 0.11.2
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
, which will automatically center the values in the bar. - See
How to add value labels on a bar chart for additional details and examples with
.bar_label
. - Tested with
pandas v1.2.4
, which is usingmatplotlib
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']
Seaborn Options
seaborn
is a high-level api formatplotlib
- The
seaborn.barplot
api doesn't have an option for stacking, but it "can" be implemented withsns.histplot
, orsns.displot
.
Seaborn DataFrame Format
# create the data frame
df = pd.DataFrame[data={'A': A, 'B': B, 'C': C, 'cat': ['C1', 'C2', 'C3']}]
A B C cat
0 45 91 68 C1
1 17 70 43 C2
2 47 72 13 C3
# convert the dataframe to a long form
df = df.melt[id_vars='cat']
cat variable value
0 C1 A 45
1 C2 A 17
2 C3 A 47
3 C1 B 91
4 C2 B 70
5 C3 B 72
6 C1 C 68
7 C2 C 43
8 C3 C 13
axes-level plot
# plot
ax = sns.histplot[data=df, x='cat', hue='variable', weights='value', discrete=True, multiple='stack']
# iterate through each container
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']
figure-level plot
# plot
g = sns.displot[data=df, x='cat', hue='variable', weights='value', discrete=True, multiple='stack']
# iterate through each axes
for ax in g.axes.flat:
# iterate through each container
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']
Original Answer
- Using the
.patches
method unpacks a list ofmatplotlib.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
.
- Each
- The labels are made using an f-string,
label_text = f'{height}'
, so any additional text can be added as needed, such aslabel_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[]
- To plot a horizontal bar:
kind='barh'
label_text = f'{width}'
if width > 0:
- Attribution: jsoma/chart.py
How do you show values on a stacked bar chart in Python?
Use matplotlib. pyplot. bar_label , which will automatically center the values in the bar.
How do you show the value of a stacked bar chart?
The Keys to Adding Totals to the Stacked Bar Chart Above:.
Highlight your data including the “Spacing” column but not including the “Grand Total” column. ... .
On the “Insert” menu select a “2-D Stacked Bar Chart”.
Select “Switch Row/Column” as necessary so the “Spacing” values are not listed as an option on the Y axis..
How do you show data in a bar graph in Python?
Call matplotlib. pyplot. barh[x, height] with x as a list of bar names and height as a list of bar values to create a bar chart. Use the syntax “for index, value in enumerate[iterable]” with iterable as the list of bar values to access each index, value pair in iterable.