7

I need to generate a regular graph (also known as lattice network) which has 100x100 nodes. I started off with drawing a 10x10 graph with the following code:

import numpy
from numpy import *
import networkx as nx
from networkx import *
import matplotlib.pyplot as plt

G=nx.grid_2d_graph(10,10)        
nx.draw(G)

plt.axis('off')
plt.show()

but what I get is this:

enter image description here

Is there any way of getting rid of this sort of rotation effect the output has? My final network must look like a chess table, just like this (please ignore the lables):

enter image description here

Also, I need to give each node its ID, ranging from 0 to 9999 (in the case of the 100x100 network). Any idea will be much appreciated!

Community
  • 1
  • 1
FaCoffee
  • 7,609
  • 28
  • 99
  • 174

2 Answers2

10

By default, networkx.draw uses a spring layout. Instead, you can provide your own positions with parameter pos. This is actually really simple, since the labels of nodes given networkx.grid_2d_graph actually are a (row, column) tuple:

>>> G=nx.grid_2d_graph(2,2)
[(0, 1), (1, 0), (0, 0), (1, 1)]

Thus you can use a node's name as its position. So you just need to create a dictionary mapping nodes to themselves, and pass that as the position.

pos = dict( (n, n) for n in G.nodes() )

However, since you also want to add node labels, you should use networkx.draw_networkx, which takes a dictionary of custom labels as an optional parameter. You'll need a dictionary mapping nodes to their new labels. Since NetworkX gives each node the label (row, column) by default, we can just label each node with row * 10 + column:

labels = dict( ((i, j), i * 10 + j) for i, j in G.nodes() )

Putting it all together, you get the following code which yields the graph below:

import networkx as nx
import matplotlib.pyplot as plt

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

plt.axis('off')
plt.show()

plot

EDIT

Using the suggestion from @AbdallahSobehy, we can label the nodes from left to right and top to bottom.

labels = dict( ((i, j), i + (N-1-j) * 10 ) for i, j in G.nodes() )

better-labeled-plot

mdml
  • 22,442
  • 8
  • 58
  • 66
  • Wow, this is straight to the point! One consideration: since I am to test the response of this network to a specific kind of *loading*, with my loading being a 2D array of values, I would need to alter the node locations. This means that my 0th node will be in the upper left corner, the 9th in the upper right, and so on and so forth, until I reach the 99th node (in my 100x100 network). The 100th node will, accordingly, be placed as the first one in the second row. – FaCoffee Oct 16 '15 at 13:33
  • 1
    @FrancescoCastellani: it shouldn't be hard to change the order of the labels. But I don't understand your labeling scheme. Why would the first node in the second row be labeled with "100"? – mdml Oct 16 '15 at 13:36
  • 2
    That is a very good answer, just a small change to follow the labeling scheme in the question would be changing the labels dict to: labels = dict( ((i, j), (i) + (N-1-j)*10) for i, j in G.nodes() ) @FrancescoCastellani – Abdallah Sobehy Oct 16 '15 at 13:37
  • In a 100x100 lattice, if you start counting from 0 in the upper left corner, you will have the 99th node as the last of the first row, and this is 100 nodes. Then, the 101st node (which will in reality bear an ID of 100) will be the first of the second row. I made a mistake as I should have said "the 101st node". This "scheme" is governed by the type of output I get when I compute the 2D array, which has the 0th cell in the upper left corner. – FaCoffee Oct 16 '15 at 13:39
  • Since I have a number of different networks to create and test, with all of them having the nodes in the *same* locations, it is convenient for me to create an *assignation list* in which each node is given the four surrounding cells. If the nodes remain in the same positions, this will only have to be done once, and knowing that "Node 0" is **always** fed by "Cell 0" will make things easier for me. – FaCoffee Oct 16 '15 at 13:42
  • 1
    @FrancescoCastellani: ah okay, sorry I thought you were talking about the 10x10 lattice. See my update answered using Abdallah Sobehy's suggestion. – mdml Oct 16 '15 at 13:42
  • @mdml How come that when I try to silence the ID labels with `labels=None`, I get the node coordinates printed instead? What if I would like to have no labels at all? – FaCoffee Oct 16 '15 at 14:21
  • 1
    @FrancescoCastellani In the draw_networkx function you need to indicate the labels should not be drawn: nx.draw_networkx(G, pos=pos, labels=labels, with_labels=False) – Abdallah Sobehy Oct 16 '15 at 14:37
  • @AbdallahSobehy sorry for the trivial question but I need to understand. You wrote `labels = dict( ((i, j), i + (N-1-j) * 10 ) for i, j in G.nodes() )` to create the labels. My question: can I refer to a particular node in G by simply "calling" its label? Which is to say: does the node labeled as "0" correspond to the first node in the upper left corner? Thanks! – FaCoffee Oct 16 '15 at 19:13
  • 1
    No question is trivial as long as you are learning @FrancescoCastellani :) I have posted an answer to be able to express my self clearly, please check it. Also, point (2) has something important for a consistent drawing of the graph so read it carefully. – Abdallah Sobehy Oct 16 '15 at 21:01
3

Clarifications to support @mdml answer (All what is said here is to be referenced to the answer of @mdml)

1- Node keys using nx.grid_2d_graph

The keys given to nodes is done implicitly giving each node a key of (i,j) describing the row and column. To access a node at (0,0) -> G[(0,0)]

2- Labels used for drawing

The labels specified for drawing should be done as follows to abide by the numbering scheme in the question:

labels = dict( ((i, j), i + (N-1-j) * N ) for i, j in G.nodes() ) 

please notice it should be N not 10, so that it is more general as if you changed N the labels will not be the ones you expect. Also, these labels are only for drawing so they have nothing to do with accessing the node.

3- Linking keys to labels

accessing node -> G[(0,0)] refers to node 90 in the drawn graph (Lower left corner in general), G[(1,0)] is the node to the right (91), while G[(0,1)] is the node labelled (80) so take care of this convention because it might not be obvious.

4- To give nodes ID that is equivalent to the ones on the graph

You can use the labels dictionary to add an attribute called id to each node which holds the integer that you see in the drawn figure:

for (i,j) in labels:
    G.node[(i,j)]['id'] = labels[(i,j)]

I created a simple graph with N=2, and I used the lines at points 2 and 3 and I printed out the Id's as follows:

for i in xrange(N):
    for j in xrange(N):
        print 'Node ID at: (%d, %d) = %d'  %(i,j,G.node[(i,j)]['id'])
plt.axis('off')
plt.show()

Result:

Node ID at: (0, 0) = 2
Node ID at: (0, 1) = 0
Node ID at: (1, 0) = 3
Node ID at: (1, 1) = 1

enter image description here

Abdallah Sobehy
  • 2,881
  • 1
  • 15
  • 28
  • 2
    @mdml I allowed myself to reference your answer to make some clarifications and add some suggestions for Francesco (I hope you are okay with that). Your review would be valuable in case I missed something. – Abdallah Sobehy Oct 17 '15 at 08:03
  • 1
    :D your appreciation is enough. But anyway, it is only logical to accept mdml's answer because this one is just an elaboration. And if there is anything unclear, do not hesitate to ask. – Abdallah Sobehy Oct 17 '15 at 10:29
  • @AbdallahSobehy sorry for asking again. Is there any way for me to obtain an `edgelist` once I have already generated my graph with `nx.grid_2d_graph`? Since Python is able to draw this graph I assume it must have the `edgelist` information stored somewhere. I need it to compute and plot the degree distribution of this specific network. Thanks! – FaCoffee Oct 19 '15 at 08:30
  • 1
    Feel free to ask, to get edge list you can use G.edges() or G.edges_iter() Check the documentation here https://networkx.github.io/documentation/latest/reference/generated/networkx.Graph.edges.html – Abdallah Sobehy Oct 19 '15 at 09:28
  • @AbdallahSobehy can we continue this in chat for a moment? I would like to avoid a long discussion... thanks! – FaCoffee Oct 20 '15 at 10:50
  • @AbdallahSobehy it looks like the `labels` dict goes beyond the boundaries of the 2D array. In fact, if I reproduce the 2x2 array and ask for `edgelist=G.edges()`, the result is a list with 8 elements instead of 4. I get this: `In[8]: edgelist Out[9]: [((0, 1), (0, 0)), ((0, 1), (1, 1)), ((0, 1), 'id'), ((1, 0), (0, 0)), ((1, 0), (1, 1)), ((1, 0), 'id'), ((0, 0), 'id'), ((1, 1), 'id')]` – FaCoffee Oct 20 '15 at 11:27
  • To support this, I tried `In[10] node_degree` and I got `Out[11]: [((0, 1), 3), ((1, 0), 3), ((0, 0), 3), ((1, 1), 3)]`. All nodes appear to have 3 links, although the scheme only allows for 2. What do I have to change in the `labels` dict to remain within the boundaries? – FaCoffee Oct 20 '15 at 11:32
  • 1
    I apologize I made a mistake in my code. Actually, instead of adding a new attribute 'id' to nodes I create a node called id and connect the nodes to it. So, the correction would be in point 4 where the addition of the attribute should be like this: `G.node[i,j]['id'] = labels [(i,j)]` – Abdallah Sobehy Oct 20 '15 at 12:18
  • 1
    Also printing the attribute, you should write `G.node[(i,j)]['id']`. I made those edits to the answer accordingly. I am open for chatting also, if you want to discuss any issue. Finally, a small hint in point 4 you can remove `(i,j)` and use `n` or any letter just to make the code look better. – Abdallah Sobehy Oct 20 '15 at 12:27
  • It works fine now! The only problem I have is, since I am going to work with 10000 nodes, it is VERY likely that I will get confused by the way functions like `edgelist=G.edges()` and `node_degree=nx.degree(G).items()` display the results. In both cases the output is a `list` which shows the node coordinates. I would like them to show the node IDs instead, as per defined in the `labels` dict. – FaCoffee Oct 20 '15 at 12:57
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/92866/discussion-between-francesco-castellani-and-abdallah-sobehy). – FaCoffee Oct 20 '15 at 13:08