问题描述
我有一些代码可以自动轻松地处理颜色和绘制多个图(对我来说).我想使注释更容易:
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.
-
如果已经有一个梦想实现的功能,我找不到.
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完全问题,但我想指出的是这并不重要.我测试了多达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.
注意事项:
- 现在可以,只需绘制两次即可.
- 此代码允许仅在特定的正交(左/右/上/下)方向上添加任何新注释,但这可以扩展.
- 箭头的位置取决于窗口.这意味着请确保在注释后(带有箭头)窗口大小或轴不变.如果您调整大小,请重新标注.
- 不是动态的,请参阅第3点.
- It's OK now, only draw twice.
- 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.
- 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.
- Not dynamic, see point 3.
背景:
-
Iplot
是我必须处理多图图形,处理着色,调整大小和现在进行注释的助手类. -
plt
是matplotlib.pyplot
- 此方法可处理多个注释(或单个注释),现在可以解决冲突.
- 您可能已经猜到了,
Iplot.axes
持有我的坐标轴图.
Iplot
is a helper class I have to handle multiplot figures, handling coloring, sizing and now annotating.plt
ismatplotlib.pyplot
- This methods handles multiple annotations (or single) and can now solve conflicts.
- As you may have guessed,
Iplot.axes
holds my axes figure.
编辑我删除了我的课程代码,以使其更易于粘贴.应该为函数指定轴,并且kwargs接受现有的box关键字以考虑先前的注释,该注释已在适当位置进行了编辑.注意我使用一个类来对此进行封装.如果是第一次调用,该函数还必须返回要使用的框.
EDITI 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)-或列出注释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!