Подтвердить что ты не робот

Узлы метки снаружи с минимальным перекрытием с другими узлами/ребрами в networkx

Я пытаюсь создать граф с node ярлыками, напечатанными вне узлов. Я могу сгенерировать "смещение", как показано ниже, что решает цель. Тем не менее, иногда метки перекрываются с ребрами (что нежелательно, поскольку вокруг узлов есть много пустых пространств, где могут быть напечатаны соответствующие метки). Мне нужно обозначить эти узлы таким образом, чтобы метки не перекрывали ни одного края или, по крайней мере, пытались максимально свести к минимуму перекрытие.

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

Есть ли какая-либо функция в сети, которая может справиться с такой ситуацией. Я долго искал игры в googled без успеха.

4b9b3361

Ответ 1

Я ранее пытался что-то подобное, основная идея заключалась в том, чтобы в основном не использовать края.

Предполагая, что ребра являются прямыми, существуют два простых и похожих способа достижения этого:

  • На основании углов, что ребра a node neighbourhood делают относительно node.

  • На основе centroid узлов окрестности.

Итак, найдите углы, чтобы края отступали от node к его окрестности и пытались расположить метку AWAY от большинства ребер; ИЛИ оцените центр тяжести окрестности node и поместите метку в противоположном направлении.

Первое решение может быть немного проблематичным, прежде всего из-за того, что работает функция atan2 (которая, по сути, определяет краевые углы), но он обеспечивает некоторую гибкость с точки зрения позиционирования метки.

Второе решение является самым простым и работает следующим образом:

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 position from the layout
    x,y = pos[aNode]
    #Get the node neighbourhood
    N = G[aNode]
    #Find the centroid of the neighbourhood. The centroid is the average of the Neighbourhood node 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 '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()

Это работает, главным образом, для узлов, которые в основном находятся на периферии Графа, но сложны для узлов, которые расположены по направлению к центру графика, потому что центроид не обеспечивает надежного направления, которое позволяет избежать большинства ребер.

Вот результат для графика fdp макет...

graphviz fdp output

... и вот вывод для networkx spring layout.

networkx spring layout

Обратите внимание на близость ярлыков зеленого и черного цветов на втором рисунке. По существу, центр тяжести окрестности ddddddd относительно близок к фактическому положению node.

Для более сложного решения вы можете проверить более сложные алгоритмы, такие как тот, который используется Wordle, чтобы адаптировать исходное положение метки если он пересекает ребро.

Надеюсь, что это поможет.