Issue
I solved my own question after a long and failed search, so I'm posting the question here and the answer immediately below.
The goal: plot percentages but annotate raw counts. The problem: you can annotate the bars with your plotted data (in my case, percentages) by iterating over the axes bar object and calling get_height() to get the text. However if you want to annotate something else, you need to iterate through some separate annotation data at the same time and supply it as annotation text instead. My first solution failed because the separate annotation data, despite being ordered, was assigned to the bars completely out of order (I'd love it if anyone can tell me why):
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
label_freqs = {'Red': 8, 'Orange': 2, 'Yellow': 4, 'Green': 7, 'Blue': 1, 'Indigo': 6, 'Violet': 5}
df = pd.DataFrame(columns=('Colour', 'Frequency', 'Percentage'))
total = sum(label_freqs.values())
df['Colour'] = label_freqs.keys()
df['Frequency'] = [int(val) for val in label_freqs.values()]
df['Percentage'] = [round((int(val)/total)*100, ndigits=2) for val in label_freqs.values()]
df = df.sort_values(by='Frequency', ascending=False)
Colour Frequency Percentage
0 Red 8 24.24
3 Green 7 21.21
5 Indigo 6 18.18
6 Violet 5 15.15
2 Yellow 4 12.12
1 Orange 2 6.06
4 Blue 1 3.03
def autolabel(my_bar, raw_freqs):
"""Attach a text label above each bar in *my_bar*, displaying its height."""
i = 0
for point in my_bar:
height = point.get_height()
ax.annotate('{}'.format(raw_freqs[i]),
xy=(point.get_x() + point.get_width() / 2, height),
xytext=(0, 3), # 3 points vertical offset
textcoords="offset points",
ha='center', va='bottom', rotation=90)
i += 1
The solution I found is to zip the axes bar object and the annotation data together and then iterate over it. See the answer, below.
Solution
Here is the solution:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
fig, ax = plt.subplots()
plt.style.use('seaborn-darkgrid')
x_pos = np.arange(len(df))
ax_bar = ax.bar(x_pos, df['Percentage'], alpha=0.2)
ax.set_title('Colour Frequencies', fontsize=12, fontweight=0)
ax.set_xticks(x_pos)
ax.set_xticklabels(df['Colour'])
for tick in ax.get_xticklabels():
tick.set_rotation(90)
ax.set_ylabel("Frequency in Percent")
def autolabel(my_bar, raw_freqs):
"""Attach a text label above each bar in *my_bar*, displaying its height."""
for point, freq in zip(my_bar, raw_freqs):
height = point.get_height()
ax.annotate('{}'.format(freq),
xy=(point.get_x() + point.get_width() / 2, height),
xytext=(0, 3), # 3 points vertical offset
textcoords="offset points",
ha='center', va='bottom', rotation=90)
autolabel(ax_bar, df['Frequency'])
plt.tight_layout()
plt.show()
plt.close()
Answered By - KMunro
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.