I am trying to plot a stacked bar plot where I have my data plotted as shown in the figure below. The labels are the exact values for each container. I would like to put these in percentage values of the total value of each bar. So far, I failed to do so. Your help would be appreciated.
Here is the code for labels:
for i, rect in enumerate(ax.patches):
# Find where everything is located
height = rect.get_height()
width = rect.get_width()
x = rect.get_x()
y = rect.get_y()
label_text = f"{height:.02f}"
label_x = x + width / 2
label_y = y + height / 2
ax.text(
label_x,
label_y,
label_text,
ha="center",
va="center",
fontsize=4,
weight="bold",
)
Given the following toy dataframe:
import pandas as pd
from matplotlib import pyplot as plt
df = pd.DataFrame(
{
"A": {2019: 125, 2020: 124, 2021: 50, 2022: 63},
"B": {2019: 129, 2020: 40, 2021: 85, 2022: 47},
"C": {2019: 126, 2020: 95, 2021: 51, 2022: 44},
"D": {2019: 99, 2020: 120, 2021: 106, 2022: 117,},
}
)
print(df)
# Output
A B C D
2019 125 129 126 99
2020 124 40 95 120
2021 50 85 51 106
2022 63 47 44 117
Here is one way to do it:
# Setup figure
fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(7, 4))
# Add bars
ax.bar(df.index, df["A"], label="A")
ax.bar(df.index, df["B"], bottom=df["A"], label="B")
ax.bar(df.index, df["C"], bottom=df["A"] + df["B"], label="C")
ax.bar(df.index, df["D"], bottom=df["A"] + df["B"] + df["C"], label="D")
# Add percentages as labels
for idx in df.index:
start = 0
for col in df.columns:
y = df.loc[idx, col]
value = df.loc[idx, col]
total = df.loc[idx, :].sum()
ax.text(
x=idx,
y=start + y / 2,
s=f"{round(100 * value / total, 1)}%",
fontsize=10,
ha="center",
color="w",
)
start += y
# Add other useful informations
plt.xticks(df.index, df.index)
ax.legend()
plt.show()
Which outputs: