16

Class point is defined as (there are also some methods, atributes, and stuff in it, but this is minimal part):

class point():
    def ___init___(self, x, y):
        self.x = x
        self.y = y

So, I saw this question, but when I tried applying it, it returns an error:

G = nx.Graph()
p = point(0,0)
G.add_node(0, p)

NetworkXError: The attr_dict argument must be a dictionary.

If i use

G = nx.Graph()
p = point(0,0)
G.add_node(0, data = p)

I don't get an error, but when i try to access the x-coordinate, it turns out it didn't save it as a point.

G[0].x

returns: AttributeError: 'dict' object has no attribute 'x'

doing

G = nx.Graph()
G.add_node(0, data = point(0,0))
G[0]

returns: {}

which means it still saves it as a dictionary.

I saw I can make my points hashable, and use these objects as nodes, so i added atribute id, since points are going to move. I added this to the class, and __repr__ for nice drawing of the graphs:

def __hash__(self):
    return self.id_n
def __cmp__(self, p):
    if self.id_n < p.id_n: return -1
    elif self.id_n == p.id_n: return 0
    else: return 1
def __eq__(self, p):
    if p.id_n == self.id_n: return True
    else: return False
def __repr__(self):
    return str(self.id_n) 

but that is a bit wierd, since I don't understand how to select a node then, by

G[<what should i put here?>]

So, question is, what is a proper way to do this?

I hoped to be able to use something like

G[node_id].some_method(some_args)

3 Answers 3

12

edit - in the below, replace G.node[] with G.nodes[] - in version 2.4 G.node was deprecated.

You're looking at G[0]. But that's not what you want. G[0] contains the information about neighbors of node 0 and the attributes of the edges, but nothing about the attributes of node 0.

class point():
    def __init__(self, x, y):
        self.x = x
        self.y = y

import networkx as nx
G = nx.Graph()
p0 = point(0,0)
p1 = point(1,1)

G.add_node(0, data=p0)
G.add_node(1, data=p1)
G.add_edge(0,1, weight=4)
G[0]
> AtlasView({1: {'weight': 4}})  #in networkx 1.x this is actually a dict. In 2.x it is an "AtlasView"

For networkx there is an expectation that a node may have a lot of data associated with it. In your case, you only have a single piece of data, namely the point. But you could have also assigned a color, a weight, a time, an age, etc. So networkx is going to store all the attributes in another dictionary, but that dictionary is accessed through G.node[0] rather than G[0].

G.node[0]
> {'data': <__main__.point at 0x11056da20>}
G.node[0]['data'].x
> 0

Notice that data in your input becomes the string 'data'.

It might be better to enter the nodes like G.add_node(0, x=0, y=0) and then you can access the entries as G.node[0]['x'].

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you, now i undestand it. I didn't know that I have to use 'data'. I will simply use the separate list with the data.
Use G.nodes[] instead of G.node[]. G.node[] is deprecated from NetworkX 2.4
The answer is strange. If I want to store actual instance in node I cannot do it and need to covert it to the attributes and back?
0

You have added a node and as such, you can examine the nodes which is a set-like view. Quoting from the docs:

These are set-like views of the nodes, edges, neighbors (adjacencies), and degrees of nodes in a graph. They offer a continually updated read-only view into the graph structure.

Do for example:

mynodes = list(G.nodes())
print(mynodes)

You should be now able to also do:

mynode = mynodes[0]  # because we have converted the set-like view to a list

See the tutorial: https://networkx.github.io/documentation/stable/tutorial.html

3 Comments

Q: Does changing mynode change the G[0]? E: also, it feels a bit dirty.
G[0] as indicated above, doesn't give you access to the node. In any case mynodes is now a list which is not bound to your graph, so changing mynodes[0] won't change anything in the graph. It has been pulled from a read-only view.
So this is basically the same as having an index-to-object dictionary on the side. Do you think this is the best way to have objects as nodes?
0

I think I understand your problem.

You want to use instances of a class you have defined as nodes, and be able to easily access instance attributes or methods.

When you create a graph in networkx with something like:

G = nx.DiGraph() # create graph
G.add_nodes_from([node0, node1, node2]) # add 3 nodes
G.nodes[node0]["node_attribute0"] = "value0"
G.add_edges_from([(node0, node1), (node0, node2)]) # add 2 edges
G[node0][node1]["edge_attribute0"] = "value1" # add one attribute to edge
G[node0][node1]["edge_attribute1"] = 10 # add another attribute to same edge

networkx creates nested python dictionaries, in the case of a DiGraph for example, 3 nested dictionaries like this:

'''
   G = {
        node0 : {
                 node1 : {
                          "edge_attribute0" : "value1",
                          "edge_attribute1" : 10
                         },
                 node2 : {}
                 },
        node1 : {},
        node2 : {}
        }
'''

That is why you access each with successive [key]

G[node0]                    # = output dict of dict, with neighbours on top layer
G[node0][node1]             # = output dict with edge attributes
G[node0][node1][attribute0] # = output value

Now, node attributes are not stored anywhere on those nested dicts. They are instead on a separate nested dict which you access via G.nodes:

'''
    G.nodes = {
               node0 : {
                        "node_attribute0" : "value0"
                       }
               }
'''

You can access this other dictionary of dictionaries by using G.nodes like this:

G.nodes[node0]["node_attribure0"]  # = value assigned to key

As you can see, there is no way en either of the 2 nests of dictionaries to access the nodes as objects themselves, since in all cases they are just dictionary "keys".

The workaround, is to store the node object itself as a node attribute, for which you could use "self" as convenient key:

for node in G: # iterate over all nodes in G
    G.nodes[node]["self"] = node # create new node attribute = node

Now, if node0, node1, node2 where all instances of a class you defined before, with, lets say a variable called "variable" and a method called "method", you can now access those by using:

G.nodes[node0]["self"].variable
G.nodes[node0]["self"].method()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.