【问题标题】:"Repel" annotations in matplotlib?matplotlib 中的“排斥”注释?
【发布时间】:2016-04-14 03:16:02
【问题描述】:

我最近看到了 R/ggplot2 的 this 包,它可以让一个图上有多个注释并自动调整它们的位置以最小化重叠,这样可以提高可读性。有没有什么 类似的可用于 python/matplotlib?

编辑: 我找到了Matplotlib overlapping annotations / text,它看起来很有希望,但似乎结果不如 R 包。

例子:

from matplotlib import pyplot as plt
import numpy as np
xs = np.arange(10, step=0.1)+np.random.random(100)*3
ys = np.arange(10, step=0.1)+np.random.random(100)*3
labels = np.arange(100)
plt.scatter(xs, ys)
for x, y, s in zip(xs, ys, labels):
    plt.text(x, y, s)
plt.show()

您可以看到,当数据密度很高时,即使是这么短的标签也会造成疯狂的混乱。

【问题讨论】:

  • 请展示一个示例数据集以及您要查找的结果。
  • 谢谢你的链接,我去看看。可以轻松生成示例数据集,我将更新问题,但如果我可以生成我正在寻找的内容,我就不会问这个问题:)

标签: python matplotlib plot


【解决方案1】:

[12-11-2016 再次更新了代码和第二个图,因为此后库已得到显着改进]

完全改写答案

为此,我制作了一个小型库,其工作方式与上述 ggrepel 类似:https://github.com/Phlya/adjustText

关闭对点的排斥,即使对于这个困难的例子,它也会产生不错的效果:

from matplotlib import pyplot as plt
from adjustText import adjust_text
import numpy as np

np.random.seed(2016)
xs = np.arange(10, step=0.1) + np.random.random(100) * 3
ys = np.arange(10, step=0.1) + np.random.random(100) * 3
labels = np.arange(100)

f = plt.figure()
scatter = plt.scatter(xs, ys, s=15, c='r', edgecolors='w')
texts = []
for x, y, s in zip(xs, ys, labels):
    texts.append(plt.text(x, y, s))

plt.show()

adjust_text(texts, force_points=0.2, force_text=0.2,
            expand_points=(1, 1), expand_text=(1, 1),
            arrowprops=dict(arrowstyle="-", color='black', lw=0.5))
plt.show()

【讨论】:

    【解决方案2】:

    tcaswell's answer 的基础上,您可以使用networkx 的spring_layout 排斥标签,它实现了Fruchterman Reingold force-directed layout algorithm

    import matplotlib.pyplot as plt
    import numpy as np
    import networkx as nx
    np.random.seed(2016)
    xs = np.arange(10, step=0.1)+np.random.random(100)*3
    ys = np.arange(10, step=0.1)+np.random.random(100)*3
    labels = np.arange(100)
    
    def repel_labels(ax, x, y, labels, k=0.01):
        G = nx.DiGraph()
        data_nodes = []
        init_pos = {}
        for xi, yi, label in zip(x, y, labels):
            data_str = 'data_{0}'.format(label)
            G.add_node(data_str)
            G.add_node(label)
            G.add_edge(label, data_str)
            data_nodes.append(data_str)
            init_pos[data_str] = (xi, yi)
            init_pos[label] = (xi, yi)
    
        pos = nx.spring_layout(G, pos=init_pos, fixed=data_nodes, k=k)
    
        # undo spring_layout's rescaling
        pos_after = np.vstack([pos[d] for d in data_nodes])
        pos_before = np.vstack([init_pos[d] for d in data_nodes])
        scale, shift_x = np.polyfit(pos_after[:,0], pos_before[:,0], 1)
        scale, shift_y = np.polyfit(pos_after[:,1], pos_before[:,1], 1)
        shift = np.array([shift_x, shift_y])
        for key, val in pos.iteritems():
            pos[key] = (val*scale) + shift
    
        for label, data_str in G.edges():
            ax.annotate(label,
                        xy=pos[data_str], xycoords='data',
                        xytext=pos[label], textcoords='data',
                        arrowprops=dict(arrowstyle="->",
                                        shrinkA=0, shrinkB=0,
                                        connectionstyle="arc3", 
                                        color='red'), )
        # expand limits
        all_pos = np.vstack(pos.values())
        x_span, y_span = np.ptp(all_pos, axis=0)
        mins = np.min(all_pos-x_span*0.15, 0)
        maxs = np.max(all_pos+y_span*0.15, 0)
        ax.set_xlim([mins[0], maxs[0]])
        ax.set_ylim([mins[1], maxs[1]])
    
    
    fig, ax = plt.subplots()
    ax.plot(xs, ys, 'o')
    repel_labels(ax, xs, ys, labels, k=0.0025)
    plt.show()
    

    产量

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-25
      • 1970-01-01
      • 1970-01-01
      • 2022-01-16
      • 1970-01-01
      • 2018-03-26
      • 2015-04-11
      • 1970-01-01
      相关资源
      最近更新 更多