I have a node set
N=[1,2,....n]
I can define 2^(nC2) graphs on this node set. I want to enumerate each of them in nondecreasing order with respect to the number of edges. Is there an efficient way to do it in python networkx? Assume undirected graphs, and by enumerating graphs i basically mean enumerating the adjacency matrices.
This can be achieved using the below code. Basically, we use itertools to generate lists representing all possible graphs, sort them by how many edges they contain, generate a dictionary of lists representing that graph, and then return a list of networkx graphs corresponding to those dicts of lists.
Code:
from math import factorial as f
import networkx as nx
import itertools
def nCr(n,r):
return f(n) // f(r) // f(n-r)
def get_all_graphs(n):
rows = sorted(itertools.product(range(2), repeat=nCr(n,2)), key= lambda x: sum(x))
indices = [sum(range(n-1, n-i-1, -1)) for i in range(n)] + [sum(range(n))]
graphs = [{node: [j+node+1 for j, edge in enumerate(row[indices[node] : indices[node+1]]) if edge == 1] for node in range(n)} for row in rows]
return [nx.from_dict_of_lists(x) for x in graphs]
Example:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(nrows=2, ncols=4)
graphs = get_all_graphs(3)
for i, row in enumerate(ax):
for j, col in enumerate(row):
nx.draw(graphs[i*4+j], with_labels=True, ax=col)
plt.tight_layout()
plt.show()
Output:
Or with the above example adapted for n=4:
Related
I'm generating a random graph and drawing it from the adjacency matrix. I need to be able to add the edge weights.
I looked at Add edge-weights to plot output in networkx and that seems to work fine and is exactly what I'm looking for in the display, but it only works when adding edges individually.
I'm using:
nx.from_numpy_matrix(G, create_using = nx.DiGraph())
And according to the documentation, if the nonsymmetric adjacency matrix has only integer entries (which it does), the entries will be interpreted as weighted edges joining the vertices (without creating parallel edges). So when looking at Add edge-weights to plot output in networkx, they grab the node attributes, grab the label attributes, and draw the edge labels. But I'm unable to grab the attributes. Does anyone know how to display these edges while still using this adjacency matrix?
Thanks in advance!
from random import random
import numpy
import networkx as nx
import matplotlib.pyplot as plt
#here's how I'm generating my random matrix
def CreateRandMatrix( numnodes = int):
def RandomHelper():
x = random()
if x < .70:
return(0)
elif .7 <= x and x <.82:
return(1)
elif .82 <= x and x <.94:
return(2)
else:
return(3)
randomatrix = numpy.matrix([[RandomHelper() for x in range(numnodes)] for y in range(numnodes)])
for i in range(len(randomatrix)):
randomatrix[i,i]=0
return randomatrix
#this generate the graph I want to display edge weights on
def Draw(n = int):
MatrixtoDraw = CreateRandMatrix(n)
G = nx.from_numpy_matrix(MatrixtoDraw, create_using = nx.DiGraph())
nx.draw_spring(G, title="RandMatrix",with_labels=True)
plt.show()
This my attempt at following Add edge-weights to plot output in networkx.
def Draw2(n = int):
MatrixtoDraw = CreateRandMatrix(n)
G = nx.from_numpy_matrix(MatrixtoDraw, create_using = nx.DiGraph())
nx.draw_spring(G, title="RandMatrix",with_labels=True)
pos=nx.get_node_attributes(G,'pos')
labels = nx.get_edge_attributes(G,'weight')
nx.draw_networkx_edge_labels(G,pos,edge_labels=labels)
plt.show()
If I run each line individually on idle I get
>>> nx.get_node_attributes(G,'pos')
{}
>>> nx.get_node_attributes(G,'weight')
{}
Why are they not being grabbed from the graph information generated by the adjacency matrix?
I am trying to build a hierarchical directed network where some nodes can branch into others, while others do not. The values in the inner dictionary (i.e. the integers) are to keep track of leaves in the tree-like structure. I've created a naive way to turn this particular nested dictionary graph_data into a directed graph but it is only specific to 3 layers. Below shows the hierarchy:
How can I create a nested function that adds edges to the directed graph for any number of levels? For example, if there was a level-3 or a level-4 this would not work and I would have to expand it out each time. Do I need to use a while loop?
import numpy as np
from collections import *
import networkx as nx
%matplotlib inline
# Hierarchical data
graph_data = {"root": {"level-0.A":0,
"level-0.B":{"level-1.B.1":2,
"level-1.B.2": {"level-2.B.2.1":3, "level-2.B.2.2":1}}}}
# Empty directed graph
G = nx.DiGraph()
# Helper functions
is_dict = lambda x: type(x) in {dict, OrderedDict, defaultdict}
# Iterate through the layers
for root, level_0 in graph_data.items():
if len(level_0) > 0:
for level_0_node, level_1 in level_0.items():
G.add_edge(root, level_0_node)
if is_dict(level_1):
for level_1_node, level_2 in level_1.items():
G.add_edge(level_0_node, level_1_node)
if is_dict(level_2):
for level_2_node, level_3 in level_2.items():
G.add_edge(level_1_node, level_2_node)
np.random.seed(8)
nx.draw(G, with_labels=True)
Use a queue to hold the details, e.g.:
from collections import Mapping
graph_data = {"root": {"level-0.A":0,
"level-0.B":{"level-1.B.1":2,
"level-1.B.2": {"level-2.B.2.1":3, "level-2.B.2.2":1}}}}
# Empty directed graph
G = nx.DiGraph()
# Iterate through the layers
q = list(graph_data.items())
while q:
v, d = q.pop()
for nv, nd in d.items():
G.add_edge(v, nv)
if isinstance(nd, Mapping):
q.append((nv, nd))
np.random.seed(8)
nx.draw(G, with_labels=True)
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:
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):
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!
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()
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() )
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
I am working on creating a graph method for analyzing images using pixels as nodes in Python. Using networkx as graph support(documentation here: https://networkx.github.io/documentation/latest/index.html ) Take this as an example:
new=np.arange(256)
g=nx.Graph()
for x in new:
g.add_node(x)
h=g.order()
print h
As expected, 256 nodes will be created.
Now, I would like to create node:attribute pairs based on another array, namely:
newarray=np.arange(256)
for x in new:
g.add_node(x)
nx.set_node_attributes(g, 'value' newarray[x])
With the addition of this line, I was hoping that the first node of newarray would be assigned to the first node of g. However, rather, all values of g will be assigned the last value of newarray. Namely, 256. How can I add attribute pairs for each node, element by element?
You need to pass in a dictionary as the third parameter for set_node_attribute, one that's aligned with the graph. See if this code does what you need:
import numpy as np
import networkx as nx
array1 = np.arange(256)
array2 = np.arange(256) * 10
g = nx.Graph()
valdict = {}
for x in array1:
g.add_node(x)
valdict[x] = array2[x]
nx.set_node_attributes(g, 'value', valdict)
for i in array1:
print g.nodes()[i], g.node[i]['value']
This question already has answers here:
Bipartite graph in NetworkX
(4 answers)
Closed 7 years ago.
I have an n1-by-n2 bi-adjacency matrix A of a bipartite graph. The matrix A is a scipy.sparse csc matrix. I would like to plot the bipartite graph using A in networkx. Assume that the nodes are colored according to their class labels called node_class. I could do the following:
import networkx as nx
G = nx.from_numpy_matrix(A)
graph_pos = nx.fruchterman_reingold_layout(G)
degree = nx.degree(G)
nx.draw(G, node_color = node_class, with_labels = False, node_size = [v * 35 for v in degree.values()])
The above code works fine for a square dense adjacency matrix. However not for a non-square bi-adjacency matrix A. The error is:
'Adjacency matrix is not square.'
Moreover the matrix A I have is a scipy.sparse matrix` because it is very large and have lots of zeros. So I would want to avoid making an (n1+n2)-by-(n1+n2) adjacency matrix by stacking A and adding zeros.
I checked the documentation of NetworkX for bipartite graphs, it does not mention how to plot bi-partite graph using bi-adjacency matrix, or create a graph using bi-adjacency sparse matrix. If someone could tell me how to plot the bipartite graph, that would be great!
I don't believe there is a NetworkX function that creates a graph from a biadjacency matrix, so you'll have to write your own. (However, they do have a bipartite module you should check out.)
Here's one way to define a function that takes a sparse biadjacency matrix and converts it to a NetworkX graph (see the comments for explanation).
# Input: M scipy.sparse.csc_matrix
# Output: NetworkX Graph
def nx_graph_from_biadjacency_matrix(M):
# Give names to the nodes in the two node sets
U = [ "u{}".format(i) for i in range(M.shape[0]) ]
V = [ "v{}".format(i) for i in range(M.shape[1]) ]
# Create the graph and add each set of nodes
G = nx.Graph()
G.add_nodes_from(U, bipartite=0)
G.add_nodes_from(V, bipartite=1)
# Find the non-zero indices in the biadjacency matrix to connect
# those nodes
G.add_edges_from([ (U[i], V[j]) for i, j in zip(*M.nonzero()) ])
return G
See an example use case below, where I use nx.complete_bipartite_graph to generate a complete graph:
import networkx as nx, numpy as np
from networkx.algorithms import bipartite
from scipy.sparse import csc_matrix
import matplotlib.pyplot as plt
RB = nx.complete_bipartite_graph(3, 2)
A = csc_matrix(bipartite.biadjacency_matrix(RB, row_order=bipartite.sets(RB)[0]))
G = nx_graph_from_biadjacency_matrix(A)
nx.draw_circular(G, node_color = "red", with_labels = True)
plt.show()
And here's the output graph:
Here is a simple example:
import networkx as nx
import matplotlib.pyplot as plt
from networkx.algorithms import matching
%matplotlib inline
ls=[
[0,0,0,1,1],
[1,0,0,0,0],
[1,0,1,0,0],
[0,1,1,0,0],
[1,0,0,0,0]
]
g = nx.Graph()
a=['a'+str(i) for i in range(len(ls))]
b=['b'+str(j) for j in range(len(ls[0]))]
g.add_nodes_from(a,bipartite=0)
g.add_nodes_from(b,bipartite=1)
for i in range(len(ls)):
for j in range(len(ls[i])):
if ls[i][j] != 0:
g.add_edge(a[i], b[j])
pos_a={}
x=0.100
const=0.100
y=1.0
for i in range(len(a)):
pos_a[a[i]]=[x,y-i*const]
xb=0.500
pos_b={}
for i in range(len(b)):
pos_b[b[i]]=[xb,y-i*const]
nx.draw_networkx_nodes(g,pos_a,nodelist=a,node_color='r',node_size=300,alpha=0.8)
nx.draw_networkx_nodes(g,pos_b,nodelist=b,node_color='b',node_size=300,alpha=0.8)
# edges
pos={}
pos.update(pos_a)
pos.update(pos_b)
#nx.draw_networkx_edges(g,pos,edgelist=nx.edges(g),width=1,alpha=0.8,edge_color='g')
nx.draw_networkx_labels(g,pos,font_size=10,font_family='sans-serif')
m=matching.maximal_matching(g)
nx.draw_networkx_edges(g,pos,edgelist=m,width=1,alpha=0.8,edge_color='k')
plt.show()