注释自动放置(matploylib.pyplot) - 或列表注释

时间:2016-11-22 07:13:39

标签: python matplotlib annotations

我有代码可以自动处理着色和绘制多个绘图(对我来说)。我想让注释更容易:

目标:如果注释xy与前一个注释发生冲突,请移位 - 说 - 直到与其他注释没有冲突。

  1. 如果有一个能够实现梦想的功能,我就找不到了。

  2. 否则 - 列出注释并在坐标系中获取边界框的最佳方法是什么?

  3. 我有一个自动着色的代码,如下所示:

    if chain:
        children = []
        for child in Iplot.axes.get_children():
            if (type(child) is not matplotlib.collections.PathCollection and
                type(child) is not matplotlib.lines.Line2D):
                continue
            children.append(child)
        col_step = 1.0/(len(children)+len(args))
        for child in children:
            child.set_color([Iplot.col,0,1-Iplot.col])
            Iplot.col += col_step
    

    我可以为注释做一些类似的事情(改变第二个循环的语句和主体),但是1)我不喜欢这段代码2)我希望存在更优雅的东西。

    由于

1 个答案:

答案 0 :(得分:2)

这是我的解决方案,我试图避免。我在问题评论中链接的旧问题中看到有人提到这是一个完整的问题,但我想指出这并不重要。我测试了多达26个注释,需要几秒钟但不会更多。任何实用的情节都不会有1000个注释。

注意事项:

  1. 如上所述,这不是超快的。具体来说,我希望我能避免画画()。现在好了,只抽两次。
  2. 此代码允许仅使用特定的正交(左/右/上/下)方向添加任何新注释,但这可以扩展。
  3. 箭头展示位置取决于窗口。这意味着在注释后确保窗口大小或轴不会改变(带箭头)。如果你调整大小,请重新注释。
  4. 不动态,见第3点。
  5. 背景:

    1. Iplot是一个助手类,我必须处理多色图,处理着色,大小调整和现在注释。
    2. pltmatplotlib.pyplot
    3. 此方法处理多个注释(或单个注释),现在可以解决冲突。
    4. 正如您可能已经猜到的那样,Iplot.axes拥有我的斧头。
    5. 修改 我删除了我的类代码,使其更易于复制。轴应该被赋予函数,并且kwargs接受现有的box关键字以考虑先前的注释,这些注释是在适当的位置编辑的。注意我使用一个类来封装它。该函数必须返回框以供使用,以防这是第一次调用。

      编辑2

      过了一会儿加速了 - 不需要画那么多,最好循环两次然后更新渲染器。

      代码:

      def annotate(axes,boxes,labels,data,**kwargs):
          #slide should be relevant edge of bbox - e.g. (0,0) for left, (0,1) for bottom ...
          try: slide = kwargs.pop("slide")
          except KeyError: slide = None
          try: 
              xytexts = kwargs.pop("xytexts")
              xytext  = xytexts
          except KeyError: 
              xytext = (0,2)
              xytexts = None
          try: boxes = kwargs.pop("boxes")
          except KeyError: boxes = list()
          pixel_diff = 1
                                                                                        newlabs = []              
          for i in range(len(labels)):
              try: 
                  len(xytexts[i])
                  xytext = xytexts[i]
              except TypeError: pass
      
              a = axes.annotate(labels[i],xy=data[i],textcoords='offset pixels',
                                          xytext=xytext,**kwargs)
              newlabs.append(a)
          plt.draw()
          for i in range(len(labels)):
              cbox = a.get_window_extent()
              if slide is not None:
                  direct  = int((slide[0] - 0.5)*2)
                  current = -direct*float("inf")
                  arrow = False
                  while True:
                      overlaps = False
                      count = 0
                      for box in boxes:
                          if cbox.overlaps(box):
                              if direct*box.get_points()[slide] > direct*current:
                                  overlaps = True
                                  current =  box.get_points()[slide] 
                                  shift   = direct*(current - cbox.get_points()[1-slide[0],slide[1]])
                      if not overlaps: break
                      arrow = True
                      position = array(a.get_position())
                      position[slide[1]] += shift * direct * pixel_diff
                      a.set_position(position)
                      plt.draw()
                      cbox = a.get_window_extent()
                      x,y =  axes.transData.inverted().transform(cbox)[0]
                  if arrow:
                      axes.arrow(x,y,data[i][0]-x,data[i][1]-y,head_length=0,head_width=0)
              boxes.append(cbox)
          plt.draw()
          return boxes
      

      欢迎任何改进建议。非常感谢!