【问题标题】:Why does pyplot.plot() create an additional Rectangle with width=1, height=1?为什么 pyplot.plot() 会创建一个宽度 = 1、高度 = 1 的附加矩形?
【发布时间】:2017-12-14 04:09:33
【问题描述】:

我正在从 DataFrame 创建一个简单的条形图。 (Series 和 DataFrame 上的 plot 方法只是 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')

这是生成的图

现在我想访问各个栏。我注意到的是,还有一个额外的条(矩形),宽度=1,高度=1

rects = [rect for rect in ax.get_children() if isinstance(rect, mpl.patches.Rectangle)]
for r in rects:
   print(r)

输出:

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)

我希望这里只有三个矩形。第四个的目的是什么?

【问题讨论】:

    标签: python pandas matplotlib plot


    【解决方案1】:

    你不想为了得到那些感兴趣的轴而弄乱所有的轴子。如果坐标区中只有条形图,ax.patches 会为您提供坐标区中的矩形。

    关于酒吧的标签,链接的文章可能不是最佳选择。它主张手动计算标签的距离,这并不是真正有用。相反,您只需使用参数textcoords="offset points"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()
    

    最后请注意,使用图中的形状来创建注释可能仍然不是最佳选择。相反,为什么不使用数据本身?

    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()
    

    这会产生与上面相同的图。

    【讨论】:

      【解决方案2】:

      第四个矩形是轴子图的边界框。
      这是 Pyplot 处理边界框的方式的产物,它并不特定于 Pandas。例如,使用常规 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)
      

      仍然产生四个矩形:

      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)
      

      Pyplot tight layout docs 中有一行引用了这个额外的 Rectangle(以及为什么它的坐标是 (0,0),(1,1)。它引用了一个 rect 参数:

      ...它指定子图将适合的边界框。坐标必须是标准化图形坐标,默认为 (0, 0, 1, 1)。

      可能有一个更正式的 Matplotlib 文档部分更彻底地描述了这个架构,但我发现那些文档很难导航,这是我能想到的最好的。

      【讨论】:

      • 感谢您的澄清。所以,我想如果我想访问绘图的条形,我将不得不手动删除边界框矩形。背景:我想使用这里描述的功能composition.al/blog/2015/11/29/…
      • 您可以在链接到的autolabel() 函数中的height = rect.get_height() 之后使用if height > 1: 之类的内容忽略它。
      • 您还想从这个答案中寻找什么?
      • 不,只需要先找到接受答案按钮 =) 谢谢
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-29
      • 2020-11-21
      相关资源
      最近更新 更多