kab*_*nus 4 python text matplotlib plot-annotations
我有代码可以轻松地自动处理着色和绘制多个图(对我来说)。我想让注释变得更容易:
目标:如果注释 xy 与前一个注释冲突,请向上移动 - 直到与其他注释不发生冲突。
如果有一个功能已经能够做到这一点那将是一个梦想,但我找不到。
否则 - 列出注释并在坐标系中获取其边界框的最佳方法是什么?
我有一个自动着色代码,如下所示:
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
Run Code Online (Sandbox Code Playgroud)
我可以对注释做类似的事情(更改 if 语句和第二个循环的主体),但是 1)我不喜欢这段代码 2)我希望存在更优雅的东西。
这是我的解决方案,也是我试图避免的解决方案。我在问题评论中链接的旧问题中看到有人提到这是一个 np 完全问题,但我想指出这并不重要。我测试了多达 26 个注释,需要几秒钟,但不会再多了。任何实际情节都不会有 1000 个注释。
注意事项:
背景:
Iplot是一个辅助类,我必须处理多图图形、处理着色、调整大小和现在注释。plt是matplotlib.pyplotIplot.axes拿着我的轴图。编辑 我删除了我的类代码以使其更易于复制粘贴。应该为函数提供轴,并且 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
Run Code Online (Sandbox Code Playgroud)
任何改进建议都将受到热烈欢迎。非常感谢!