2025, Nov 07 21:01
Как отобразить метки признаков узлов в NetworkX из torch_geometric
Показываем, как отрисовать в NetworkX метки узлов из torch_geometric: словарь labels, конвертация тензоров в строки и применение draw_networkx с примерами
Отрисовка признаков узлов на графике NetworkX кажется обманчиво простой, однако многие замечают, что стандартная визуализация показывает только индексы узлов. Если ваши данные приходят из torch_geometric и вы ожидаете увидеть на узлах значения вроде -1, 0 и 1, эти метки нужно явно передать процедуре рисования.
Как воспроизвести проблему
Ниже приведён небольшой пример, который строит крошечный граф и пытается его визуализировать. Отрисовка проходит успешно, но на узлах не печатаются их признаки — видны только числовые идентификаторы.
import torch
from torch_geometric.data import Data
edges_idx = torch.tensor(
[
[0, 1, 1, 2],
[1, 0, 2, 1]
],
dtype=torch.long
)
# по одному скалярному признаку на узел
data_feats = torch.tensor([[-1], [0], [1]], dtype=torch.float)
graph_data = Data(x=data_feats, edge_index=edges_idx)
show_graph = True
if show_graph:
import networkx as nx
import matplotlib.pyplot as plt
edge_pairs = edges_idx.t().tolist()
graph_obj = nx.Graph()
graph_obj.add_edges_from(edge_pairs)
graph_obj.add_nodes_from(range(data_feats.size(0)))
coords = nx.spring_layout(graph_obj, seed=1)
plt.figure(figsize=(6, 6))
nx.draw_networkx(
graph_obj,
coords,
with_labels=True,
node_color='lightblue',
edge_color='gray',
node_size=800
)
plt.title("Visualization of the Social Network Graph")
plt.axis('off')
plt.show()
Что происходит на самом деле
NetworkX ничего не знает о ваших признаках из torch_geometric, пока вы их явно не передадите. Узлы в этом графе создаются как простые целые числа, поэтому with_labels=True приводит к отображению именно этих чисел. Чтобы увидеть значения признаков, сопоставьте идентификаторы узлов нужному тексту и передайте это соответствие через labels=...
Решение: передайте словарь labels
Создайте словарь, который мапит id узла к нужной строке. Если признаки хранятся в тензорах, сначала преобразуйте их в питоновские объекты. Использование .int() помогает избежать строк вроде [1.0], если вы предпочитаете [1]. Затем укажите labels=... в draw_networkx или нарисуйте метки отдельно через draw_networkx_labels.
import torch
from torch_geometric.data import Data
edges_idx = torch.tensor(
[
[0, 1, 1, 2],
[1, 0, 2, 1]
],
dtype=torch.long
)
data_feats = torch.tensor([[-1], [0], [1]], dtype=torch.float)
graph_data = Data(x=data_feats, edge_index=edges_idx)
# Формируем метки: {node_id: текст_признака}
# Используем .int(), чтобы избежать меток вида [1.0]
label_map = dict(enumerate(data_feats.int().tolist()))
print(label_map)
show_graph = True
if show_graph:
import networkx as nx
import matplotlib.pyplot as plt
edge_pairs = edges_idx.t().tolist()
graph_obj = nx.Graph()
graph_obj.add_edges_from(edge_pairs)
graph_obj.add_nodes_from(range(data_feats.size(0)))
coords = nx.spring_layout(graph_obj, seed=1)
plt.figure(figsize=(6, 6))
# Вариант A: передать labels напрямую
nx.draw_networkx(
graph_obj,
coords,
node_color='lightblue',
edge_color='gray',
node_size=800,
with_labels=True,
labels=label_map
)
# Вариант B: рисовать метки отдельно
# nx.draw_networkx(
# graph_obj,
# coords,
# node_color='lightblue',
# edge_color='gray',
# node_size=800,
# with_labels=False
# )
# nx.draw_networkx_labels(graph_obj, coords, labels=label_map)
plt.title("Visualization of the Social Network Graph")
plt.axis('off')
plt.show()
Форматируем метки так, как нужно
Поскольку метки — это обычные строки, вы можете оформить их так, как удобно для вашей визуализации. Если хотите добавить поясняющий текст или несколько строк, переводите значения в строки и используйте символы перевода строки. Это также упрощает вставку LaTeX, если ваша конфигурация Matplotlib его поддерживает.
label_map = {idx: f"x = {item}\ny = $2^{{ {item[0]} }}$" for idx, item in enumerate(data_feats.int().tolist())}
Можно оставить всё минимальным и просто привести значения признаков к строкам как есть.
label_map = {idx: str(item) for idx, item in enumerate(data_feats.int().tolist())}
Смежные преобразования
Если ваши данные уже лежат в объекте torch_geometric Data, вы можете напрямую построить граф NetworkX. При отрисовке метки всё равно нужно передавать отдельно.
import torch_geometric
nx_graph = torch_geometric.utils.to_networkx(graph_data, to_undirected=True)
Почему это важно
Графы часто несут богатую поузловую информацию. Если визуализация скрывает этот контекст, сложнее быстро проверять конвейеры данных и отлаживать входы модели. Явное передавание отображаемых меток сохраняет разделение ответственности: структура графа остаётся чистой, а слой визуализации решает, какой текст показывать.
Главные выводы
При построении графиков в NetworkX идентификаторы узлов — это не то же самое, что признаки узлов. Чтобы вывести признаки на рисунке, соберите словарь от id узла к тексту метки и передайте его в labels=... или используйте draw_networkx_labels. Сначала конвертируйте тензоры в питоновские типы, при необходимости приводите к int, чтобы убрать десятичные точки, и форматируйте строки для многострочных или расширенных меток. Если вы преобразуете граф из torch_geometric в NetworkX, принцип тот же: метки нужно создавать и передавать явно.
Статья основана на вопросе на StackOverflow от Mathieu Krisztian и ответе пользователя furas.