简体   繁体   中英

Why does pyplot.plot() create an additional Rectangle with width=1, height=1?

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.

The fourth Rectangle is the bounding box for the Axis subplot.
This is an artifact of the way Pyplot handles bounding boxes, it's not specific to Pandas. For example, plotting with regular Pyplot:

f, ax = plt.subplots()
ax.bar(range(3), df.Population.values)
rects = [rect for rect in ax.get_children() if isinstance(rect, mpl.patches.Rectangle)]
for r in rects:
    print(r)

Still results in four Rectangles:

Rectangle(-0.4,0;0.8x3.42635e+06)
Rectangle(0.6,0;0.8x1.26039e+06)
Rectangle(1.6,0;0.8x1.73912e+06)
Rectangle(0,0;1x1)

There's a line in the Pyplot tight layout docs which refers to this extra Rectangle (and also why its coordinates are (0,0),(1,1) . It refers to a rect parameter:

...which specifies the bounding box that the subplots will be fit inside. The coordinates must be in normalized figure coordinates and the default is (0, 0, 1, 1).

There's probably a more official section of the Matplotlib documentation that describes this architecture more thoroughly, but I find those docs difficult to navigate, this is the best I could come up with.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM