Plot bipartite graph using networkx in Python [duplicate] - python

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()

Related

Networkx - problem while working big data

I am trying to triangulate a large amount of massive data using Delaunay to scipy.spatial for triangulation and networkx to get the node adjacency relations. My code works very well on small data sets but when I try to introduce volumes of about 2 miollion points I always get the following error:
raise NetworkXError(f"The node {n} is not in the graph.") from e
NetworkXError: The node 1 is not in the graph.
It seems like my graph store the first node and nothing more. When I did my research I found that networkx is well adapted to massive data
Here is my code :
import numpy as np
import networkx as nx
import scipy.spatial
points = np.genfromtxt('las1.xyz', delimiter = ';')
xy= points[:,0:2]
z= points[:,2]
delTri = scipy.spatial.Delaunay(xy)
edges = set()
for n in range(delTri.nsimplex):
edge = sorted([delTri.vertices[n,0], delTri.vertices[n,1]])
edges.add((edge[0], edge[1]))
edge = sorted([delTri.vertices[n,0], delTri.vertices[n,2]])
edges.add((edge[0], edge[1]))
edge = sorted([delTri.vertices[n,1], delTri.vertices[n,2]])
edges.add((edge[0], edge[1]))
pts_neigh = {}
graph = nx.Graph(list(edges))
for i in range(len(xy)):
pts_neigh[i] = list(graph.neighbors(i))
I still get the edges list from my networkx graph but it seems like it fails at the level of constructing the nodes.
I will be so grateful for your help.
Although it's possible to instantiate graph with specific data, the syntax can be a bit complex. An easier option is to explicitly add edges from a list:
graph = nx.Graph()
graph.add_edges_from(list(edges))

Plotting isolated nodes in NetworkX

import networkx as nx
import numpy as np
from scipy.sparse import coo_matrix #coordinate sparse matrices
A = np.zeros([4,4])
A[0,1] = A[1,2] = 1
S = coo_matrix(A)
edges = np.r_[[S.row], [S.col]].T
G = nx.Graph()
G.add_edges_from(edges)
nx.draw(G)
When I run that script, I get this:
But there are four nodes. How can I get the isolated fourth node to show?
By only adding the edges to the graph, networkx has no way of knowing about the additional vertices; all it's doing is adding the vertices of each edge that you're providing. If, instead, you explicitly add all vertices, then you're good to go:
G = nx.Graph()
G.add_nodes_from(range(len(A)))
G.add_edges_from(edges)
nx.draw(G)

Add edge-weights to plot output in networkx from adjacency matrix

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?

Draw a scale-free network in a lattice

I want to generate graph form an adjacency list but I am not happy with how the nodes are positioned. I want them to be positioned according to a pre-defined scheme which resembles a regular grid with arbitrary coordinates x and y, while still maintaining the scale-free features. Let me give an example: A barabasi-albert network with node 1 located at x_1 = 0.6 and y_1 = 0.5, node 2 located at x_2 = -0.5 and y_2 = 1 ... and so on. I have a list of coordinates of each node.
Have a look at the pos parameter of draw_networkx_XXX functions here.
It can be used like this:
import networkx as nx
import matplotlib.pyplot as plt
from random import randint,seed;
seed(1)
nodes = list(range(5))
edges = [ (nodes[i-1],nodes[i]) for i in range(1,len(nodes)) ]
# here we can set the coordinates to our liking
positions = { node:(randint(0,9),randint(0,9)) for node in nodes }
G=nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
nx.draw_networkx(G,pos=positions, with_labels=False, node_size=100)
plt.show()
[Edit]
Here's how we can build the graph from the adjancency list and assign real values to node positions.
import networkx as nx
import matplotlib.pyplot as plt
from random import randint,seed
from pprint import pprint
seed(0)
edges = [ (randint(0,5),randint(0,5)) for i in range(5) ]
G=nx.Graph()
# nodes added automatically with add_edges_from
G.add_edges_from(edges)
# here we can set positions to our liking
positions = { node: (round((5-randint(0,9))/7.0,2)
, round((5-randint(0,9))/7.0,2)) for node in G.nodes }
pprint({ "edges:": edges, "nodes:":list(G.nodes), "positions:":positions }, width=100)
nx.draw_networkx(G, pos = positions, with_labels=False, node_size=100)
plt.show()
Using positions from a csv file is straightforward.
The pos parameter is really supposed to be a dict with node names as keys (I edited the first snippet to reflect that).
So, if we have a csv file with node names and positions, we just build a dict from it and supply the dict for pos.

NetworkX Random Geometric Graph Implementation using K-D Trees

So it is clear with NetworkX that they use an algorithm in n^2 time to generate a random geometric graph. They say there is a faster algorithm possible with the use of K-D Trees. My question is how would one go about attempting to implement the K-D Tree version of this algorithm? I am not familiar with this data structure, nor would I call myself a python expert. Just trying to figure this out. All help is appreciated, thanks!
def random_geometric_graph(n, radius, dim=2, pos=None):
G=nx.Graph()
G.name="Random Geometric Graph"
G.add_nodes_from(range(n))
if pos is None:
# random positions
for n in G:
G.node[n]['pos']=[random.random() for i in range(0,dim)]
else:
nx.set_node_attributes(G,'pos',pos)
# connect nodes within "radius" of each other
# n^2 algorithm, could use a k-d tree implementation
nodes = G.nodes(data=True)
while nodes:
u,du = nodes.pop()
pu = du['pos']
for v,dv in nodes:
pv = dv['pos']
d = sum(((a-b)**2 for a,b in zip(pu,pv)))
if d <= radius**2:
G.add_edge(u,v)
return G
Here is a way that uses the scipy KD-tree implementation mentioned by #tcaswell above.
import numpy as np
from scipy import spatial
import networkx as nx
import matplotlib.pyplot as plt
nnodes = 100
r = 0.15
positions = np.random.rand(nnodes,2)
kdtree = spatial.KDTree(positions)
pairs = kdtree.query_pairs(r)
G = nx.Graph()
G.add_nodes_from(range(nnodes))
G.add_edges_from(list(pairs))
pos = dict(zip(range(nnodes),positions))
nx.draw(G,pos)
plt.show()

Categories