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

2022-01-25 00:00:00 python networkx label nodes

问题描述

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

将networkx导入为nx从 networkx.utils 导入 is_list_of_ints,展平将 matplotlib.pyplot 导入为 pltG=nx.Graph()G = nx.complete_graph(5)映射= {0:'aaaaaaa',1:'bbbbbbb',2:'ccccccc',3:'dddddddd',4:'eeeeeeeeee'}G = nx.relabel_nodes(G,mapping)plt.figure(figsize=(10,10), facecolor="w", frameon=False)pos = nx.graphviz_layout(G, prog="fdp") #计算位置(x,y)坐标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')#用于节点外的标签偏移量=10pos_labels = {}键 = pos.keys()对于键中的键:x, y = pos[键]pos_labels[key] = (x, y+offset)nx.draw_networkx_labels(G,pos=pos_labels,fontsize=2)plt.show()

networkx 中是否有任何功能可以处理这种情况.我google了很久没有成功.

解决方案

我之前尝试过类似的东西,主要思想是尽量避免边缘.

假设边是直线,有两种简单相似的方法可以实现:

  1. 基于节点的 .

    请注意第二个图中绿色和黑色标签的距离.本质上,dddddddd的邻域的质心与节点的实际位置比较接近.

    对于更复杂的解决方案,您可能需要检查更复杂的算法,例如 Wordle 使用的那个,以便在标签与边相交时调整标签的初始位置.

    希望这会有所帮助.

    I am trying to create a graph with node labels printed outside of nodes. I am able to generate 'offset' as shown below that solve the purpose. However, Sometimes the labels overlaps with edges (Which is undesirable as there are lots of empty spaces around nodes where the corresponding labels can be printed). I need to label these nodes in such a way that the labels does not overlap any edge or at least try to minimize overlap as much as possible.

    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()
    

    Is there any function in networkx that can deal with such situation. I googled for long with no success.

    解决方案

    I have previously attempted something similar with the main idea being to keep out of the way of the edges mostly.

    Assuming that the edges are straight lines, there are two simple and similar ways to achieve this:

    1. On the basis of the angles that the edges of a node's neighbourhood are making with respect to the node itself.

    2. On the basis of the centroid of the neighbourhood's nodes.

    So, find the angles that the edges departing from a node towards its neighbourhood form and try to position the label AWAY from the majority of the edges; OR estimate the centroid of a node's neighbourhood and position the label along the opposite direction.

    The first solution can be a little bit problematic, primarily because of the way that the atan2 function operates (which essentially determines the edge angles) but it does provide some flexibility in terms of positioning the label.

    The second solution is the simplest and works as follows:

    import networkx as nx
    import matplotlib.pyplot as plt
    
    #Build the graph
    #Please note, the code here is as per the original post
    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)
    #Get a graph layout
    pos = nx.graphviz_layout(G, prog="fdp") #calculate position (x,y) coordinates
    #Here is an alternative layout, please see below.
    #pos = nx.layout.spring_layout(G)
    nx.draw_networkx_nodes(G,pos,node_size=1200,node_shape='^',node_color='0.75')
    nx.draw_networkx_edges(G,pos, width=2,edge_color='r')
    #Show the original position of the labels using a Green colour.
    nx.draw_networkx_labels(G,pos,font_color='g')
    
    #Please note, the code below uses the original idea of re-calculating a dictionary of adjusted label positions per node.
    label_ratio = 1.0/8.0
    pos_labels = {} 
    #For each node in the Graph
    for aNode in G.nodes():
        #Get the node's position from the layout
        x,y = pos[aNode]
        #Get the node's neighbourhood
        N = G[aNode]
        #Find the centroid of the neighbourhood. The centroid is the average of the Neighbourhood's node's x and y coordinates respectively.
        #Please note: This could be optimised further
        cx = sum(map(lambda x:pos[x][0], N)) / len(pos)
        cy = sum(map(lambda x:pos[x][1], N)) / len(pos)
        #Get the centroid's 'direction' or 'slope'. That is, the direction TOWARDS the centroid FROM aNode.
        slopeY = (y-cy)
        slopeX = (x-cx)
        #Position the label at some distance along this line. Here, the label is positioned at about 1/8th of the distance.
        pos_labels[aNode] = (x+slopeX*label_ratio, y+slopeY*label_ratio)
    
    #Finally, redraw the labels at their new position.
    nx.draw_networkx_labels(G,pos=pos_labels,fontsize=2)
    #Show the figure
    plt.show()
    

    This works, mostly, for nodes that are largely in the periphery of the Graph but is challenging for nodes that are positioned towards the centre of the graph because the centroid will not provide a reliable direction that avoids the majority of the edges.

    Here is the output for graphviz's fdp layout...

    ...and here is the output for networkx' spring layout.

    Please note the proximity of the green and black coloured labels on the second figure. Essentially, the centroid of ddddddd's neighbourhood is relatively close to the node's actual position.

    For a more complex solution, you might want to check more complex algorithms such as the one that is used by Wordle in order to adapt the initial position of the label if it intersects an edge.

    Hope this helps.

相关文章