【问题标题】:Non-overlapping scatter plot labels using matplotlib使用 matplotlib 的非重叠散点图标签
【发布时间】:2014-10-20 09:34:25
【问题描述】:

我有一个带有多个点的散点图。每个点都有一个与之关联的字符串(长度不同),我想提供一个标签,但我不能全部适合它们。所以我想从最重要到最不重要迭代我的数据点,并且在每种情况下,只有当它不会与现有标签重叠时才应用标签。字符串的长度不同。其中一位评论者提到解决背包问题以找到最佳解决方案。在我的例子中,贪心算法(总是标记最重要的剩余点,可以标记而不重叠)将是一个好的开始,可能就足够了。

这是一个玩具示例。我可以让 Python 在不重叠的情况下只标记尽可能多的点吗?

import matplotlib.pylab as plt, numpy as np

npoints = 100
xs = np.random.rand(npoints)
ys = np.random.rand(npoints)

plt.scatter(xs, ys)

labels = iter(dir(np))
for x, y, in zip(xs, ys):
    # Ideally I'd condition the next line on whether or not the new label would overlap with an existing one
    plt.annotate(labels.next(), xy = (x, y))
plt.show()

【问题讨论】:

  • 简而言之,不,这不是内置的(无论如何,找到最佳标签集让我觉得是背包问题的一种变体......)。您可能会跟踪您添加的所有文本,然后检查 bbox 是否重叠,但是文本对象在绘制之前不知道它们有多大,因此这可能会变得非常昂贵。
  • Here 您可能会找到实现此类自动标签放置所需的一切。但这不是微不足道的。
  • @tcaswell,你说绘制需要绘制文本框以找出它们有多大会“昂贵”。你是指计算时间吗?我现在有代码可以标记所有只需一两秒钟即可运行的点。即使是我最棘手的用例也只有几千分。
  • 足够的重新计算时间。文本渲染是瓶颈之一,但我正在考虑尝试进行动画/实时绘图,就人力时间而言,它仍然非常快。在我打字之前我应该​​再想一想。

标签: python matplotlib scatter-plot


【解决方案1】:

你可以先把所有的注解都画出来,然后用掩码数组检查重叠,用set_visible()隐藏。这是一个例子:

import numpy as np
import pylab as pl
import random
import string
import math
random.seed(0)
np.random.seed(0)
n = 100
labels = ["".join(random.sample(string.ascii_letters, random.randint(4, 10))) for _ in range(n)]
x, y = np.random.randn(2, n)

fig, ax = pl.subplots()

ax.scatter(x, y)

ann = []
for i in range(n):
    ann.append(ax.annotate(labels[i], xy = (x[i], y[i])))

mask = np.zeros(fig.canvas.get_width_height(), bool)

fig.canvas.draw()

for a in ann:
    bbox = a.get_window_extent()
    x0 = int(bbox.x0)
    x1 = int(math.ceil(bbox.x1))
    y0 = int(bbox.y0)
    y1 = int(math.ceil(bbox.y1))

    s = np.s_[x0:x1+1, y0:y1+1]
    if np.any(mask[s]):
        a.set_visible(False)
    else:
        mask[s] = True

输出:

【讨论】:

  • 这太好了,谢谢!!其他相关方的注意事项:这通过 Spyder 使用 IPython 开箱即用,但为了让它在 PyCharm 中工作,我必须在 fig.canvas.draw() 上方添加行 pl.tight_layout()。我从this answer 那里得到了提示。
【解决方案2】:

作为附加说明:为了使我的代码正常工作,我必须在 get_window_extent() 方法中添加额外的 renderer=fig.canvas.get_renderer() 参数,而不是默认的 get_window_extent(renderer=None)。我认为这个附加参数规范的必要性取决于操作系统。 https://github.com/matplotlib/matplotlib/issues/10874

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-08
    • 2013-10-17
    • 2017-08-24
    • 2021-10-06
    • 2016-09-04
    • 1970-01-01
    • 2018-07-25
    相关资源
    最近更新 更多