外部标记节点,与networkx中的其他节点/边缘重叠最小

时间:2012-08-14 04:57:12

标签: python label nodes networkx

我正在尝试创建一个在节点外打印节点标签的图表。我能够生成如下所示的“偏移”来解决目的。但是,有时标签与边缘重叠(这是不合需要的,因为在节点周围有很多空白区域可以打印相应的标签)。我需要以这样的方式标记这些节点,使得标签不与任何边缘重叠,或者至少尽可能地尽量减少重叠。

import networkx as nx
from networkx.utils import is_list_of_ints, flatten
import matplotlib.pyplot as plt

G=nx.Graph()

G = nx.complete_graph(5)
mapping = {0:'aaaaaaa',1:'bbbbbbb',2:'ccccccc', 3:'dddddddd', 4:'eeeeeeeee'}
G = nx.relabel_nodes(G,mapping)

plt.figure(figsize=(10,10), facecolor="w", frameon=False)
pos = nx.graphviz_layout(G, prog="fdp") #calculate position (x,y) coordinates
nx.draw_networkx_nodes(G,pos,node_size=1200,node_shape='o',node_color='0.75')
nx.draw_networkx_edges(G,pos, width=2,edge_color='b')


#for labeling outside the node
offset =10
pos_labels = {}
keys = pos.keys()
for key in keys:
    x, y = pos[key]
    pos_labels[key] = (x, y+offset)
nx.draw_networkx_labels(G,pos=pos_labels,fontsize=2)
plt.show()

networkx中是否有任何可以处理此类情况的功能。我google了很长时间没有成功。

2 个答案:

答案 0 :(得分:2)

我之前尝试过类似的主要想法,主要是避开边缘。

假设边缘是直线,有两种简单且类似的方法可以实现这一点:

  1. 基于节点neighbourhood的边缘相对于节点本身的角度。

  2. 根据邻居节点的centroid

  3. 因此,找到边缘从节点向其邻域形式偏离的角度,并尝试从大多数边缘定位标签AWAY;或者估计节点邻域的质心,并沿着相反的方向定位标签。

    第一个解决方案可能有点问题,主要是因为atan2函数的运行方式(基本上决定了边角),但它确实在定位标签方面提供了一些灵活性。

    第二种解决方案是最简单的,其工作原理如下:

    <div id="image"><!--
        --><a href="http://www.google.ca"><img id="google" src="http://placehold.it/250x228" alt="google" style="width:250px;height:228px;" /></a><!--
        --><a href="http://www.intel.ca/content/www/ca/en/homepage.html"><img id="intel" src="http://placehold.it/250x100" alt="Intel Logo" style="width:250px;height:100px;" /></a><!--
    --></div>
    

    这主要适用于主要位于图形外围的节点,但对于位于图形中心的节点具有挑战性,因为质心不会提供避免大部分边缘的可靠方向。

    以下是graphviz的fdp布局...

    的输出

    graphviz fdp output

    ...这里是networkx'spring layout的输出。

    networkx spring layout

    请注意第二个图上绿色和黑色标签的接近程度。基本上,ddddddd邻域的质心相对接近节点的实际位置。

    对于更复杂的解决方案,您可能需要检查更复杂的算法,例如the one that is used by Wordle,以便在标签与边缘相交时调整标签的初始位置。

    希望这有帮助。

答案 1 :(得分:0)

@A_A 概述的方法建立在良好的直觉之上,并且是不错的初步近似值。然而,除了@A_A 已经提到的问题之外,这两种方法还有一些额外的问题。

  1. 如果一个节点(欧几里得)附近的所有边也属于该节点,则两种方法都只会减少标签边重叠。但是,如果图很大或很密集,则节点附近的大多数边可能属于其他节点,这两种方法都没有考虑。

  2. 尽管这两种方法通常会减少小而稀疏图中的标签边重叠,但两种方法都无法解决标签节点和标签标签重叠问题。

有一种概念上简单的方法也可以解决标签节点和标签标签重叠的问题:在每个被标记的节点周围画一个圆圈。在每个圆上,找到离其他所有东西(节点、边、其他标签)最远的点。 这确保圆上的这个位置周围有最空的画布,因此是一个很好的位置贴上标签。

这可以通过以下方式完成:

  1. 用一系列沿边缘密集采样的点来近似每个边缘。在实践中,10-20 点似乎足够好,但即使是 100-1000 点在计算上也很容易处理。确保包括边的起点和终点,即节点位置。

  2. 对于每个标签,计算沿相应节点周围的圆采样的第二组点。同样,35 分(每 10 度 1 分)通常绰绰有余,但使用更多的分(比如 100)并没有实质性的危害。

  3. 对于每个圆,在圆上找到其最近的欧几里得邻居距离最大的点(同时排除同一圆上的点)。将标签放在那里。

可以进一步细化步骤 3 以使用最近的两个邻居的最大平均距离。这解决了结点问题,当节点位于图的外围时可能会发生这种情况,因此大部分圆的最近邻居是被标记的节点。

从数字的角度来看,所有这些听起来可能很可怕。但是,通过使用 KD 树可以非常有效地计算最近邻距离,如下所示(使用 100 个点来近似每个边和圆)。

enter image description here

这种方法是在 netgraph 中实现的,这是一个用于可视化网络的 Python 库(我是作者)。该库与大多数常见的图形数据格式完全兼容,包括 networkxigraph Graph 对象,因此制作漂亮的图形应该简单快捷。至少是这个想法。

重现动画的代码(不包括鼠标移动):

#!/usr/bin/env python
import matplotlib.pyplot as plt
import networkx as nx
from netgraph import InteractiveGraph # pip install netgraph

g = InteractiveGraph(nx.complete_graph(10), node_size=2, edge_width=0.5,
                     node_labels=dict(zip(range(10), 'abcdefghij')), node_label_offset=0.05,
                     node_label_fontdict=dict(size=20, fontweight='bold'))
plt.show()