Joining two networkx graphs on a single edge - python

I need a graph with N clusters, that somewhat represents the structure of social networks. I planned to go about this by creating N graphs with barabasi albert structure, and then connecting them by a single node.
import networkx as nx
a = nx.barabasi_albert_graph(10,2)
b = nx.barabasi_albert_graph(10,2)
nx.draw(a)
nx.draw(b)
what I want is them connected like this:
But I cannot see or find any simple way of doing this, are there any networkX functionality that can do just this?

Joining two graphs by edge is really simple:
import matplotlib.pyplot as plt
import networkx as nx
a = nx.barabasi_albert_graph(10,2)
b = nx.barabasi_albert_graph(10,2)
c = nx.union(a,b, rename=('a-', 'b-'))
c.add_edge('a-0', 'b-0')
nx.draw_networkx(c,with_labels=True,node_size=500)
plt.show()
And if you want to merge graphs on a common node (this is stated in your question contrary to the title), you can do this:
import matplotlib.pyplot as plt
import networkx as nx
a = nx.barabasi_albert_graph(10,2)
b = nx.barabasi_albert_graph(10,2)
a= nx.relabel_nodes(a, { n: str(n) if n==0 else 'a-'+str(n) for n in a.nodes })
b= nx.relabel_nodes(b, { n: str(n) if n==0 else 'b-'+str(n) for n in b.nodes })
c = nx.compose(a,b)
nx.draw_networkx(c,with_labels=True,node_size=500)
plt.show()

Related

Combine edges when node degree is n in networkx

I have an undirected graph as follows:
import networkx as nx
import matplotlib.pyplot as plt
l = [('1','2'),('2','3'),('3','4'),('3','5'),('1','6'),('6','7'),('6','8'),('9','8')]
G=nx.Graph()
G.add_edges_from(l)
nx.draw_networkx(G,with_labels=True)
plt.show()
I want to combine edges when node satisfies degree=n(like 2). I need remove node 1,2 and 8,and connect 3-6 and 6-9 in my example. So I expect the results to be as follows.
How can I do it? Thanks in advaence
import networkx as nx
import matplotlib.pyplot as plt
l = [('1','2'),('2','3'),('3','4'),('3','5'),('1','6'),('6','7'),('6','8'),('9','8')]
G=nx.Graph()
G.add_edges_from(l)
# Select all nodes with only 2 neighbors
nodes_to_remove = [n for n in G.nodes if len(list(G.neighbors(n))) == 2]
# For each of those nodes
for node in nodes_to_remove:
# We add an edge between neighbors (len == 2 so it is correct)
G.add_edge(*G.neighbors(node))
# And delete the node
G.remove_node(node)
nx.draw(G,with_labels=True)

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)

Plot bipartite graph using networkx in Python [duplicate]

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

Graph traversal with Networkx (Python)

I'm playing a bit with Networkx to manage a graph of dependencies.
Let's say I have this Graph which each letter represent a server
>>> G = nx.Graph()
>>> G.add_edge("A","B")
>>> G.add_edge("A","H")
>>> G.add_edge("H","C")
>>> G.add_edge("B","C")
>>> G.add_edge("B","D")
A
/ \
H B
/ / \
C C D
So here we can see that before starting A we need to start H and B and to start H we need to start C and then to start B wee need to start C and D
By fiddling a bit with Networkx I found that I can get that by doing a dfs traversal
print nx.dfs_successors(G,"A")
{A:[H,B], H:[C], B:[D] }
But I have a problem with that method. As you can see when there is two same letter in the tree, Networkx only chose to put one of them in the final structure (which is correct) But I need to have the complete structure
How can I force Networkx to add in the structure B:[D,C] ??
I want to precise that by doing
>>> nx.dfs_successors(G,"B")
{'B': ['C', 'D']}
So everything is "Internally" correct, it's just the dfs_successors that displays it not in the way I wish.
Thank you
Taking your code, your graph doesn't come out as you'd expect. If you do:
import pylab as p
import networkx as nx
G = nx.Graph()
G.add_edge("A","B")
G.add_edge("A","H")
G.add_edge("H","C")
G.add_edge("B","C")
G.add_edge("B","D")
nx.draw(G)
p.show()
you will see your graph as:
This is due to the logic of G.add_edge("A", "B"):
If G has no node of id "A", add it.
If G has no node of id "B", add it.
Connect "A" to "B" with a new edge.
Thus, you only create five nodes, not six as in your picture.
Edit
Networkx can take any hashable as value for a node, and in the graph it uses str(node) to label each circle. So we can simply define our own Node class (which you maybe want to call Server?) and give it the desired behavior.
import pylab as p
import networkx as nx
class Node(object):
nodes = []
def __init__(self, label):
self._label = label
def __str__(self):
return self._label
nodes = [Node(l) for l in ["A","B","C","C","D","H"]]
edges = [(0,1),(0,5),(5,2),(1,3),(1,4)]
G = nx.Graph()
for i,j in edges:
G.add_edge(nodes[i], nodes[j])
nx.draw(G)
p.show()
gives us
and so what you wanted.
I think what you are looking for is a topological sort https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.dag.topological_sort.html
This only works if you have a DAG (directed acyclic graph).
If so you can draw the tree you want too - like this:
import uuid
import networkx as nx
import matplotlib.pyplot as plt
G = nx.DiGraph()
G.add_edge("A","B")
G.add_edge("A","H")
G.add_edge("H","C")
G.add_edge("B","C")
G.add_edge("B","D")
order = nx.topological_sort(G)
print "topological sort"
print order
# build tree
start = order[0]
nodes = [order[0]] # start with first node in topological order
labels = {}
print "edges"
tree = nx.Graph()
while nodes:
source = nodes.pop()
labels[source] = source
for target in G.neighbors(source):
if target in tree:
t = uuid.uuid1() # new unique id
else:
t = target
labels[t] = target
tree.add_edge(source,t)
print source,target,source,t
nodes.append(target)
nx.draw(tree,labels=labels)
plt.show()
The drawing uses a label mapping to map the ids of the node to the original labels.

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