FaCoffee
FaCoffee

Reputation: 7929

Consistent removal of nodes from grid_2d_graph

Say that I have a regular 2d grid graph G made of NxN=100x100 nodes. I create such network by calling:

N=100
G=nx.grid_2d_graph(N,N)
pos = dict( (n, n) for n in G.nodes() ) #Dictionary of all positions
labels = dict( ((i, j), i + (N-1-j) * N ) for i, j in G.nodes() )
nx.draw_networkx(G, pos=pos, labels=labels,with_labels=False, node_size=10)

and I get a result similar to that shown in the left hand side of this picture:

enter image description here

Now, say that my network experiences a disruptive event which renders a number of nodes failed (right hand side of the picture). My graph G is no longer made of the same number of nodes: graph G2 now possesses K<NxN nodes. Given this case, I want to be able to draw G2 by calling:

G2=G.remove_nodes_from(failed_nodes_dict) #Failed_node_dict holds the disrupted nodes
nx.draw_networkx(G2, pos=pos, labels=labels,with_labels=False, node_size=10)

My question. How can I do this, in order to make sure that the dictionary of positions pos is not altered? This means: if node 0 is in position (0,0) (upper left) in situation 1) and it does not fail, then it must appear in the same position in situation 2). This way, the two networks can be comparable in that the second is just a "transformation" of the first. Thanks!

Upvotes: 2

Views: 1637

Answers (3)

Imanol Luengo
Imanol Luengo

Reputation: 15909

I would extend a bit the answer of @AbdallahSobehy. However, for a more general reusability of networkx graphs, I would suggest storing position and label information inside the graph structure, rather than in a separate containers.

Starting with the reduced version of your graph (for N = 5):

N = 5
G = nx.grid_2d_graph(N,N)
pos = dict((n, n) for n in G.nodes())
labels = dict(((i, j), i + (N-1-j) * N) for i, j in G.nodes())

One can store pos and label dictionaries as graph attributes:

nx.set_node_attributes(G, 'pos', pos)
nx.set_node_attributes(G, 'labels', labels)

And use them for ploting:

nx.draw_networkx(G, pos=nx.get_node_attributes(G, 'pos'),
                 labels=nx.get_node_attributes(G, 'labels'),
                 with_labels=True, node_size=500)

enter image description here

You can later perfom any operation in the graph, such as removing some nodes:

G.remove_nodes_from([(0,0),(0,2),(2,2),(2,1)])

And replot the graph without actually needing to recalculate pos or label dictionaries, as the entries are automatically updated with the graph operations. The following line is exactly the same as the plot call above:

nx.draw_networkx(G, pos=nx.get_node_attributes(G, 'pos'),    
                 labels=nx.get_node_attributes(G, 'labels'), 
                 with_labels=True, node_size=500)

But this time, yields a different image:

enter image description here

In short: store everything you can in node/edge attributes, as they will be automatically updated with graph changes.

Upvotes: 2

Joel
Joel

Reputation: 23887

I struggled to see why you were asking this question - the code should basically work exactly as you want already. However, you have an error which may be what is causing you to ask this question.

G2= G.remove_nodes_from(failed_nodes_dict) #Failed_node_dict holds the disrupted nodes

This will make G2 be None. G.remove_nodes_from(L) removes the nodes in L from G. It doesn't create a new graph that looks like G, but without those nodes. So it doesn't return anything. Because of this G2 isn't being given any value. The graph G has had those nodes removed.

So the following should work:

N=100
G=nx.grid_2d_graph(N,N)
pos = dict( (n, n) for n in G.nodes() ) #Dictionary of all positions
labels = dict( ((i, j), i + (N-1-j) * N ) for i, j in G.nodes() )
nx.draw_networkx(G, pos=pos, labels=labels,with_labels=False, node_size=10)

#find failed nodes here

G.remove_nodes_from(failed_nodes_dict) #Failed_node_dict holds the disrupted nodes
nx.draw_networkx(G, pos=pos, labels=labels,with_labels=False, node_size=10)

pos is never changed in this code.

Upvotes: 1

Abdallah Sobehy
Abdallah Sobehy

Reputation: 3021

You can do so by relabeling nodes from (i,j) to the numbers you see on the figures from the labels dictionary. Then remove the failed nodes. Finally, adjust the pos dictionary to map positions to the new labels of nodes by simply reversing the labels dictionary. Here is what I added after your code:

nx.relabel_nodes(G,labels,False)
G.remove_nodes_from([0,4,12,18])            
pos = {y:x for x,y in labels.iteritems()}
nx.draw_networkx(G, pos=pos, with_labels=True, node_size = 300)

for an N = 5 and failed nodes [0,4,12,18], here is the results I got before (left) and after (right) failed nodes are removed. enter image description here

Upvotes: 1

Related Questions