注释自动放置 (matploylib.pyplot) - 或列出注释 [英] Annotation auto-placement (matploylib.pyplot) -or list annotations

查看:30
本文介绍了注释自动放置 (matploylib.pyplot) - 或列出注释的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有代码可以轻松地自动处理着色和绘制多个图(对我而言).我想让注释更容易:

I have code that handles coloring and plotting multiple plots automatically easily (for me). I want to make annotation easier:

目标:如果注释 xy 与前一个注释冲突,请移动 - 向上说 - 直到与其他注释没有冲突为止.

goal: If an annotation xy conflicts with a previous one, shift - say up - until there is no conflict with no other annotation.

  1. 如果有一个功能已经能够做到这一点,那将是一个梦想,我找不到.

  1. If there is a function already capable of this that would be a dream, I couldn't find one.

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

Otherwise - what's the best way to list annotations and get their bounding box in the coordinate system?

我有一个看起来像这样的自动着色代码:

I have a code for auto coloring that looks like this:

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

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

I could do something similar for annotations (change if statement and body of second loop), but 1) I don't like this piece of code 2) I'm hoping something more elegant exists.

谢谢

推荐答案

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

This is my solution, one I was trying to avoid. I saw in the old question linked in the question comments someone mentions this is an np-complete problem, but I want to point out that's doesn't really matter. I tested up to 26 annotations, and it takes a few seconds but no more. Any practical plot won't have 1000 annotations.

注意事项:

  1. 如前所述,这不是超快.具体来说,我希望我可以避免 draw(). 现在可以了,只绘制两次.
  2. 此代码允许仅添加具有特定正交(左/右/上/下)方向的任何新注释,但这可以扩展.
  3. 箭头位置依赖于窗口.这意味着确保在注释(带箭头)后窗口大小或轴不会改变.如果您调整大小,请重新注释.
  4. 不是动态的,见第 3 点.
  1. As mentioned, this isn't superfast. Specifically I wish I could avoid draw(). It's OK now, only draw twice.
  2. This code allows any new annotation/s to be added only with a specific orthogonal (left/right/up/down) direction, but this can be extended.
  3. The arrow placement is window dependent. This means make sure the window size or axes do not change after annotating (with arrows). Re-annotate if you resize.
  4. Not dynamic, see point 3.

背景:

  1. Iplot 是一个辅助类,我必须处理多图图形、处理着色、调整大小和现在的注释.
  2. pltmatplotlib.pyplot
  3. 此方法处理多个注释(或单个注释),现在可以解决冲突.
  4. 您可能已经猜到了,Iplot.axes 包含我的坐标轴图.
  1. Iplot is a helper class I have to handle multiplot figures, handling coloring, sizing and now annotating.
  2. plt is matplotlib.pyplot
  3. This methods handles multiple annotations (or single) and can now solve conflicts.
  4. As you may have guessed, Iplot.axes holds my axes figure.

编辑我删除了我的班级代码,使其更易于复制粘贴.应该为函数提供轴,并且 kwargs 接受现有的 box 关键字以考虑先前的注释,这些注释是在适当的位置编辑的.注意我使用一个类来封装它.该函数还必须返回这些框以供使用,以防这是第一次调用.

EDIT I removed my class code to make this more copy pasteable. Axes should be given to the function, and kwargs accept an existing boxes keyword to take into account previous annotations, which is edited in place. Note I use a class to encapsulate this. The function has to return the boxes for use as well, in case this is a first call.

编辑 2

一段时间后加快了速度 - 不需要绘制这么多,最好循环两次,然后在中间更新渲染器.

After a while sped this up - no need to draw so much, better to loop twice and then update the renderer in between.

代码:

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

任何改进建议将受到热烈欢迎.非常感谢!

Any suggestions to improve will be warmly welcomed. Many thanks!

这篇关于注释自动放置 (matploylib.pyplot) - 或列出注释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆