Python: networkx: Как сделать автоматическое расширение размера узла в соответствии с меткой

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

# show tree
import matplotlib.pyplot as plt
import networkx

nodes, edges, labels = gp.graph(bests[0])
graph = networkx.Graph()
graph.add_nodes_from(nodes)
graph.add_edges_from(edges)
pos = networkx.graphviz_layout(graph, prog="dot")

plt.figure(figsize=(7,7))
networkx.draw_networkx_nodes(graph, pos, node_size=900, node_color="w")
networkx.draw_networkx_edges(graph, pos)
networkx.draw_networkx_labels(graph, pos, labels)
plt.axis("off")
plt.show()

4 ответа

Решение

Нет простого способа сделать это с помощью matplotlib и networkx (конечно, это возможно при достаточном количестве кода). Graphviz действительно отлично работает с метками, и легко написать файлы точечного формата из networkx для обработки в Graphviz.
Также взгляните на https://github.com/chebee7i/nxpd который может сделать именно то, что вам нужно.

Аргумент node_size принимает как скалярные, так и векторные значения. В то время как скаляр делает все узлы одинаковыми размерами, вектор помогает вам указать отдельные значения в списке, который будет использоваться для каждого узла. Если идентификаторы вашего узла являются строками, то следующая стратегия работает довольно хорошо.

Просто измените аргумент размера на список в networkx.draw_networkx_nodes, основанный на длине каждого идентификатора узла. Выберите the_base_size соответствующим образом.

networkx.draw_networkx_nodes (graph, pos, node_size = [len (v) * the_base_size для v в graph.nodes()], node_color="w")

Вы можете адаптировать это к случаю, когда вы можете обрабатывать этикетки тоже.

*** Однако я не уверен, будет ли сохранено однозначное соответствие, пока он выбирает размеры узлов из списка на основе размеров меток. Делитесь своими результатами. Я лично использовал его для идентификаторов строк и он работает хорошо.

Я недавно наткнулся на эту проблему. Даже через 5 лет прямого пути к этому нет. Однако после многих проб и ошибок я наконец нашел разумный обходной путь. Вы можете удалить узлы и просто использовать метки с цветом фона.

pos = nx.spring_layout(graph) # Get positions of your nodes
nx.draw_networkx_nodes(graph, pos=pos, label=labels, bbox=dict(fc="r"))

Здесь метки - это словарь с узлом в качестве ключа и именем узла в качестве значения.

Еще один отлаженный способ достижения этого, особенно когда вы хотите настроить свойства каждой метки (например, цвет, форму, фон и т. Д.), Лучший способ добавить метки самостоятельно, используя plt.text().

pos = nx.spring_layout(graph) # Get positions of your nodes
for key in pos:
    x, y = pos[key]
    plt.text(x,y,key,fc="r", ha="center", va="center")

Мне понравилось решение @mathfux, потому что оно правильно позиционирует стрелки в ориентированных графах. Но для решения упомянутой проблемы соответствия требуются некоторые настройки (списки позиций и цветов указаны в порядке компоновки, а не в порядке узлов); также для обработки индексирования списков с нуля. Я также обнаружил, что размеры работают по квадратичному закону, а не линейно. Вот улучшенная версия с цветами, использующая Kamada Kwai, а не Spring, поэтому макет не меняется каждый раз.

      import pandas as pd
import matplotlib.pyplot as plt
import networkx as nx

G = nx.DiGraph()
G.add_edges_from([(0,1), (1, 1), (1, 7), (2, 1), (2, 2), (2, 3), 
                  (2, 6), (3, 5), (4, 3), (5, 4), (5, 8),
                  (5, 9), (6, 4), (7, 2), (7, 6), (8, 7)])
labelList="zero one twotwotwo three four five six seven eighteighteight nine".split(' ')

positions=nx.kamada_kawai_layout(G)
plt.figure(figsize =(9, 9))
nx.draw_networkx(G, 
                 node_color =['C{}'.format(i) for i in positions], 
                 pos=positions, 
                labels={idx: val for idx, val in enumerate(labelList)},
                node_size=[len(labelList[i])**2 * 60 for i in positions]
                )

Другие вопросы по тегам