I'm creating a simple bar plot from a DataFrame. (The plot method on Series and DataFrame is just a simple wrapper around pyplot.plot)
import pandas as pd
import matplotlib as mpl
df = pd.DataFrame({'City': ['Berlin', 'Munich', 'Hamburg'],
'Population': [3426354, 1260391, 1739117]})
df = df.set_index('City')
ax = df.plot(kind='bar')
This is the generated plot

Now I want to access the individual bars. And what I've noticed is that there is an additional bar (Rectangle) with width=1, height=1
rects = [rect for rect in ax.get_children() if isinstance(rect, mpl.patches.Rectangle)]
for r in rects:
print(r)
output:
Rectangle(xy=(-0.25, 0), width=0.5, height=3.42635e+06, angle=0)
Rectangle(xy=(0.75, 0), width=0.5, height=1.26039e+06, angle=0)
Rectangle(xy=(1.75, 0), width=0.5, height=1.73912e+06, angle=0)
Rectangle(xy=(0, 0), width=1, height=1, angle=0)
I would expect only three rectangles here. What is the purpose of the fourth?
You would not want to mess with all the children of the axes to get those of interest. If there are only bar plots in the axes, ax.patches gives you the rectangles in the axes.
Concerning the labeling of the bars, the linked article may not be the best choice. It argues to calculate the distance of the label manually, which is not really useful. Instead you would just offset the annotation by some points compared to the bar top, using the argument textcoords="offset points" to plt.annotation.
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame({'City': ['Berlin', 'Munich', 'Hamburg'],
'Population': [3426354, 1260391, 1739117]})
df = df.set_index('City')
ax = df.plot(kind='bar')
def autolabel(rects, ax):
for rect in rects:
x = rect.get_x() + rect.get_width()/2.
y = rect.get_height()
ax.annotate("{}".format(y), (x,y), xytext=(0,5), textcoords="offset points",
ha='center', va='bottom')
autolabel(ax.patches,ax)
ax.margins(y=0.1)
plt.show()

Finally note that using the shapes in the plot to create the annotations may still not be the optimal choice. Instead why not using the data itself?
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame({'City': ['Berlin', 'Munich', 'Hamburg'],
'Population': [3426354, 1260391, 1739117]})
ax = df.plot(x = "City", y="Population", kind='bar')
def autolabel(s, ax=None, name=""):
x = s.name
y = s[name]
ax.annotate("{}".format(y), (x,y), xytext=(0,5), textcoords="offset points",
ha='center', va='bottom')
df.apply(autolabel, axis=1, ax=ax, name="Population")
ax.margins(y=0.1)
plt.show()
This produces the same plot as above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With