- It seems the
dict
must be the output of a DataFrame, which isn't shown in the OP.
- Use
.bar_label
to add custom labels to the top of each bar. How to add value labels on a bar chart has a thorough explanation of the method.
- Use the bar height (
h
), country
, and container label
, with Boolean indexing on df
, to get the correct 'percents'
to annotate the bars.
- The answers to this question do not demonstrate how to get the custom values for the bars.
- Do not assign all lines to
ax
, as they do not return a matplotlib.axes
. This overwrites ax
, so ax.bar_label
won't work.
plt.xlabel
, plt.ylabel
, and plt.title
return matplotlib.text.Text
.
- With
ax = plt.show()
, type(ax) → NoneType
because plt.show
doesn't return anything.
- Tested in
python 3.11
, pandas 1.5.3
, matplotlib 3.7.1
, seaborn 0.12.2
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# create the dataframe
data = {'Country Name': {0: 'United States', 1: 'United States', 2: 'United States', 3: 'United States', 4: 'Russian Federation', 5: 'Russian Federation', 6: 'Russian Federation', 7: 'Russian Federation', 8: 'Japan', 9: 'Japan', 10: 'Japan', 11: 'Japan', 12: 'Germany', 13: 'Germany', 14: 'Germany', 15: 'Germany', 16: 'France', 17: 'France', 18: 'France', 19: 'France'},
'Indicator Name': {0: 'Population, total', 1: 'Population, ages 0-14, total', 2: 'Population, ages 15-64, total', 3: 'Population, ages 65+, total', 4: 'Population, total', 5: 'Population, ages 0-14, total', 6: 'Population, ages 15-64, total', 7: 'Population, ages 65+, total', 8: 'Population, total', 9: 'Population, ages 0-14, total', 10: 'Population, ages 15-64, total', 11: 'Population, ages 65+, total', 12: 'Population, total', 13: 'Population, ages 0-14, total', 14: 'Population, ages 15-64, total', 15: 'Population, ages 65+, total', 16: 'Population, total', 17: 'Population, ages 0-14, total', 18: 'Population, ages 15-64, total', 19: 'Population, ages 65+, total'},
'Valeur': {0: 320896618.0, 1: 61653419.0, 2: 212262832.0, 3: 46980367.0, 4: 144096870.0, 5: 24255306.0, 6: 100404879.0, 7: 19436685.0, 8: 127141000.0, 9: 16517168.0, 10: 77547638.0, 11: 33076194.0, 12: 81686611.0, 13: 10716271.0, 14: 53720119.0, 15: 17250221.0, 16: 66624068.0, 17: 12168975.0, 18: 41837530.0, 19: 12617563.0}}
df = pd.DataFrame(data)
# add the list of percent values to the dataframe, which assumes they're in the correct order (as they appear to be)
df['percents'] = ['100%', '19%', '66%', '15%', '100%', '17%', '70%', '13%', '100%', '13%', '61%', '26%', '100%', '13%', '66%', '21%', '100%', '18%', '63%', '19%']
# create the plot
fig, ax = plt.subplots(figsize=(10, 5))
sns.barplot(data=df, x='Country Name', y='Valeur', hue='Indicator Name', palette="dark", ax=ax)
# customize the plot labels and title
plt.xlabel('')
plt.ylabel('Nombre de personnes', size=15)
plt.title('Etude de la répartition de la population', size=20)
# list of countries for selecting data from df
countries = [v.get_text() for v in ax.get_xticklabels()] # countries = df['Country Name'].unique() also works
# iterate through the axes bar containers
for c in ax.containers:
# get the label of the current containers
label = c.get_label()
# use the height of the bar, country, and label name to get the corresponding percent for the container
labels = [df.loc[df['Indicator Name'].eq(label) &
df['Country Name'].eq(country) &
df['Valeur'].eq(h) , 'percents'].iloc[0] if (h := v.get_height()) else '' for (v, country) in zip(c, countries)]
# add the bar label with the custom labels
ax.bar_label(c, labels=labels)

df
Country Name Indicator Name Valeur percents
0 United States Population, total 320896618.0 100%
1 United States Population, ages 0-14, total 61653419.0 19%
2 United States Population, ages 15-64, total 212262832.0 66%
3 United States Population, ages 65+, total 46980367.0 15%
4 Russian Federation Population, total 144096870.0 100%
5 Russian Federation Population, ages 0-14, total 24255306.0 17%
6 Russian Federation Population, ages 15-64, total 100404879.0 70%
7 Russian Federation Population, ages 65+, total 19436685.0 13%
8 Japan Population, total 127141000.0 100%
9 Japan Population, ages 0-14, total 16517168.0 13%
10 Japan Population, ages 15-64, total 77547638.0 61%
11 Japan Population, ages 65+, total 33076194.0 26%
12 Germany Population, total 81686611.0 100%
13 Germany Population, ages 0-14, total 10716271.0 13%
14 Germany Population, ages 15-64, total 53720119.0 66%
15 Germany Population, ages 65+, total 17250221.0 21%
16 France Population, total 66624068.0 100%
17 France Population, ages 0-14, total 12168975.0 18%
18 France Population, ages 15-64, total 41837530.0 63%
19 France Population, ages 65+, total 12617563.0 19%