Issue
I am currently trying to display relations between objects of a generic Node class using NetworkX. As it is possible for two nodes to have multiple edges between them, I am trying to implement this using a MultiGraph. The Node class I made contains an attribute called "caption", which I want to display ontop of the node. Each node also has the attribute "id", which is a number unique to the node. However, multiple nodes having the same caption causes an issue. In this example, I want Node Y and Z to each have a connection to new nodes called "A" and "B". Note that there are two Nodes with the name "A" and two with the name "B". I envision that to look somewhat like this:
The following code produces an image where the nodes called A and B are treated as being only one node each:
import networkx as nx
import matplotlib.pyplot as plt
# define Node class
class Node:
def __init__(self, caption, id, parent_id, parent_connections, children):
self.caption = caption
self.id = id
self.parent_id = parent_id
self.parent_connections = parent_connections
self.children = children
G = nx.MultiGraph()
# create all nodes for the example
node_x = Node(caption="X", id=1, parent_id=None, parent_connections=[], children=[])
node_y = Node(caption="Y", id=2, parent_id=1, parent_connections=["first connection"], children=[])
node_z = Node(caption="Z", id=3, parent_id=1, parent_connections=["first connection"], children=[])
node_a_y = Node(caption="A", id=4, parent_id=2, parent_connections=["first connection", "second connection", "third connection"], children=[])
node_b_y = Node(caption="B", id=5, parent_id=2, parent_connections=["first connection"], children=[])
node_a_z = Node(caption="A", id=6, parent_id=3, parent_connections=["first connection"], children=[])
node_b_z = Node(caption="B", id=7, parent_id=3, parent_connections=["first connection"], children=[])
all_nodes = [node_x, node_y, node_z, node_a_y, node_b_y, node_a_z, node_b_z]
# fill the children lists with every node's children
for node in all_nodes:
for other_node in all_nodes:
if other_node is not node and other_node.parent_id == node.id:
node.children.append(other_node)
# add the nodes and edges to the MultiGraph G
for node in all_nodes:
G.add_node(node.caption)
for child in node.children:
for i in range(0, len(child.parent_connections)):
G.add_edge(node.caption, child.caption, length=10)
# draw the Graph
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos, node_color="r", node_size=100, alpha=1)
ax = plt.gca()
for e in G.edges:
ax.annotate(e[1],
xy=pos[e[0]], xycoords="data",
xytext=pos[e[1]], textcoords="data",
arrowprops=dict(arrowstyle="-", color="0.5",
shrinkA=5, shrinkB=5,
patchA=None, patchB=None,
connectionstyle="arc3,rad=rrr".replace("rrr",str(0.3*e[2])),
),
)
plt.axis('off')
plt.show()
Furthermore, adding the node objects directly to G's nodes and edges instead of just adding the captions creates this result:
this seems to be what I am looking for, but unfortunately the objects themselves are being used as a caption instead of the caption attribute. Is there a way of making networkx use a certain attribute of the given object as a caption? If there isn't, what else could I do to reach my goal?
Solution
Nodes need to have distinct (and hashable) IDs. By default, networkx uses these ID as the labels of the nodes. However, you can specify other labels using the labels
attribute. The labels
values can have duplicates (unlike the node IDs).
nx.draw(nx.Graph([(0, 1)], labels={0 : 'a', 1 : 'a'}); plt.show()
See also: nx.draw_networkx_labels
.
Regarding the second part of your question:
but unfortunately the objects themselves are being used as a caption instead of the caption attribute
To be precise, the __repr__
method of your Node
class is used for the node labels.
Is there a way of making networkx use a certain attribute of the given object as a caption?
You can override the default __repr__
method to return the caption attribute instead.
class Node:
def __init__(self, ...):
...
def __repr__(self):
return self.caption
Answered By - Paul Brodersen
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.