2025, Nov 05 23:00
Show torch_geometric node features as labels on NetworkX graphs by passing a labels dictionary
Show torch_geometric node features as labels in NetworkX plots. Build a node_id-to-text map and pass it via the labels parameter or draw_networkx_labels.
Rendering node features on a NetworkX plot looks deceptively simple, yet many discover that the default visualization only shows node indices. If your data originates in torch_geometric and you expect values like -1, 0, and 1 to be printed on the nodes, you have to explicitly pass those labels to the drawing routine.
Reproducing the issue
The following snippet builds a tiny graph and attempts to visualize it. The drawing succeeds, but the node features are not printed on the nodes, only their numeric ids are shown.
import torch
from torch_geometric.data import Data
edges_idx = torch.tensor(
[
[0, 1, 1, 2],
[1, 0, 2, 1]
],
dtype=torch.long
)
# one scalar feature per node
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()
What’s actually happening
NetworkX doesn’t know anything about your torch_geometric features unless you provide them. Nodes in this graph are created as simple integers, so with_labels=True results in displaying those integers. To see your feature values, you must map node ids to the desired text and pass that mapping in labels=...
The fix: pass a labels dictionary
Create a dictionary that maps each node id to the text you want drawn. If your features are tensors, convert them to a Python structure first. Using .int() avoids strings like [1.0] when you prefer [1]. Then provide labels=... to draw_networkx, or draw them separately via 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)
# Build labels: {node_id: feature_as_text}
# Using .int() to avoid labels like [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))
# Option A: pass labels directly
nx.draw_networkx(
graph_obj,
coords,
node_color='lightblue',
edge_color='gray',
node_size=800,
with_labels=True,
labels=label_map
)
# Option B: draw labels separately
# 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()
Formatting labels the way you want
Since labels are plain strings, you can format them however it suits your visualization. If you want to embed extra text or multiple lines, convert to string and use newline characters. This also makes it easy to stitch in LaTeX if your Matplotlib setup supports it.
label_map = {idx: f"x = {item}\ny = $2^{{ {item[0]} }}$" for idx, item in enumerate(data_feats.int().tolist())}
You can also keep it minimal and just stringify the feature values as-is.
label_map = {idx: str(item) for idx, item in enumerate(data_feats.int().tolist())}
Related conversions
If your data already lives in a torch_geometric Data object, you can build a NetworkX graph directly. You still need to supply labels separately when drawing.
import torch_geometric
nx_graph = torch_geometric.utils.to_networkx(graph_data, to_undirected=True)
Why this matters
Graphs often carry rich per-node information. When the visualization hides this context, you lose the ability to quickly validate data pipelines and debug model inputs. Passing an explicit labels mapping keeps the separation of concerns intact: the graph structure remains clean, and the rendering layer decides what text to display.
Takeaways
When plotting with NetworkX, node ids are not the same as node features. If you want features on the plot, build a dictionary from node id to label text and feed it to labels=... or draw_networkx_labels. Convert tensor data to Python types first, cast to int if you don’t want decimal points, and format strings for multi-line or enriched labels as needed. If you convert from torch_geometric to a NetworkX graph, the same principle applies: create and pass labels explicitly.
The article is based on a question from StackOverflow by Mathieu Krisztian and an answer by furas.