NetworkX convert MultiGraph to simple Graph by averaging parallel length - python

There is one solution about converting MultiGraph to Simple Graph by using Maehler's code
import networkx as nx
G = nx.MultiGraph()
G.add_nodes_from([1,2,3])
G.add_edges_from([(1, 2), (1, 2), (1, 3), (2, 3), (2, 3)])
G2 = nx.Graph(G)
and another one using Aslak and Aric's code by summing the weight
import networkx as nx
# weighted MultiGraph
M = nx.MultiGraph()
M.add_edge(1,2,weight=7)
M.add_edge(1,2,weight=19)
M.add_edge(2,3,weight=42)
# create weighted graph from M
G = nx.Graph()
for u,v,data in M.edges_iter(data=True):
w = data['weight'] if 'weight' in data else 1.0
if G.has_edge(u,v):
G[u][v]['weight'] += w
else:
G.add_edge(u, v, weight=w)
print G.edges(data=True)
# [(1, 2, {'weight': 26}), (2, 3, {'weight': 42})]
Wondering how to average the parallel edges' weight?

Here is an approach similar to those that uses the statistics package to compute the mean of the edge weight.
import networkx as nx
from statistics import mean
# weighted MultiGraph
M = nx.MultiGraph()
M.add_edge(1,2,weight=7)
M.add_edge(1,2,weight=20)
M.add_edge(2,3,weight=42)
M.add_edge(2,3)
# create weighted graph G from M
G = nx.Graph()
for u,v,data in M.edges(data=True):
if not G.has_edge(u,v):
# set weight to 1 if no weight is given for edge in M
weight = mean(d.get('weight',1) for d in M.get_edge_data(u,v).values())
G.add_edge(u, v, weight=weight)
print(G.edges(data=True))
OUTPUT (networkx-2.0-dev)
EdgeView([(1, 2, {'weight': 13.5}), (2, 3, {'weight': 21.5})])

Related

Merge and interact between two graphs in networkx

There is way for merge two graphs G and H side by side or even interact between them after computing layout? I need same spring look for single and merged graph.
G = nx.Graph() pos = nx.spring_layout(G)
nx.draw_networkx(G, pos, edge_color=colors, **options, node_color=colors_nodes, width=widths)
H = nx.Graph()
pos2 = nx.spring_layout(H)
nx.draw_networkx(H, pos2, edge_color=colors, **options, node_color=colors_nodes, width=widths)
The final look should be like this Graph
I believe you would be looking for the disjoint_union function or the union depending upon your use case and the nature of your graphs' nodes being distinct integers or not
Example would be:
G = nx.Graph([(0, 1), (0, 2), (1, 2)])
H = nx.Graph([(0, 1), (0, 3), (1, 3), (1, 2)])
I = nx.union(G, H, rename=("G", "H"))
J = nx.Graph(I)
nx.draw_networkx(J, pos, edge_color=colors, **options, node_color=colors_nodes, width=widths)

how to add subgraph with (new nodes, new edges) to an existed graph in python

I'm trying to add new nodes (red dots) with new edges (green lines) to be places diagonaly
new color and positions to this grid graph
import networkx as nx
import matplotlib.pyplot as plt
G = nx.grid_graph(dim=[5, 5])
nodes = list(G.nodes)
edges = list(G.edges)
p = []
for i in range(0, 5):
for j in range(0, 5):
p.append([i, j])
for i in range(0, len(nodes)):
G.nodes[nodes[i]]['pos'] = p[i]
pos = {}
for i in range(0, len(nodes)):
pos[nodes[i]] = p[i]
nx.draw(G, pos)
plt.show()
Sorry, your question is not clear for me, but you can create list of edges and then use G.add_edges_from() to add nodes & edges to your initial graph
Probably you should define algorithm to find coordinates / labels of new nodes, then construct edges and add by G.add_edges_from()
G = nx.grid_graph(dim=[5, 5])
new_edges = [((0.5, 1.5), (0.5, 2.5)),
((0.5, 1.5), (0, 2)),
((0.5, 2.5), (0, 2))]
G.add_edges_from(new_edges)
nx.draw(G, pos={n:n for n in G.nodes()})
plt.show()
EDITED
I modified code:
nodes are renamed and have 'coord' and 'color' attributes
graph are drawn wrt color of node
Probably there is a better way to solve this problem cause code looks massive)
G = nx.grid_graph(dim=[5, 5])
# rename nodes & set coord and color (0 - blue, 1 - red) attributes to nodes
nx.set_node_attributes(G, {n: {"coord": n, "color": 0} for n in G.nodes()})
nx.relabel_nodes(G, mapping={coord:label
for label,coord in zip(range(len(G)), G.nodes())}, copy=False)
# we are GIVEN list nodes & edges to add
new_nodes = [(label, {'coord': coord, 'color': 1})
for label, coord in zip([26, 27], [(0.5, 1.5), (0.5, 2.5)])]
new_edges = [(2, 26), (2, 27), (26, 27)]
G.add_nodes_from(new_nodes)
G.add_edges_from(new_edges)
color_map = {0: '#7d99f5', 1: '#f57d7d'}
nx.draw_networkx(G, pos={n:G.nodes[n]['coord'] for n in G.nodes()},
with_labels=True,
node_color=[color_map[n[1]['color']]
for n in G.nodes(data=True)])
plt.show()

Calculate the Laplacian matrix of a graph object in NetworkX

I am writing my own function that calculates the Laplacian matrix for any directed graph, and am struggling with filling the diagonal entries of the resulting matrix. The following equation is what I use to calculate entries of the Laplacian matrix, where e_ij represents an edge from node i to node j.
I am creating graph objects with NetworkX (https://networkx.org/). I know NetworkX has its own Laplacian function for directed graphs, but I want to be 100% sure I am using a function that carries out the correct computation for my purposes. The code I have developed thus far is shown below, for the following example graph:
# Create a simple example of a directed weighted graph
G = nx.DiGraph()
G.add_nodes_from([1, 2, 3])
G.add_weighted_edges_from([(1, 2, 1), (1, 3, 1), (2, 1, 1), (2, 3, 1), (3, 1, 1), (3, 2, 1)])
# Put node, edge, and weight information into Python lists
node_list = []
for item in G.nodes():
node_list.append(item)
edge_list = []
weight_list = []
for item in G.edges():
weight_list.append(G.get_edge_data(item[0],item[1])['weight'])
item = (item[0]-1,item[1]-1)
edge_list.append(item)
print(edge_list)
> [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
# Fill in the non-diagonal entries of the Laplacian
num_nodes = len(node_list)
num_edges = len(edge_list)
J = np.zeros(shape = (num_nodes,num_nodes))
for x in range(num_edges):
i = edge_list[x][0]
j = edge_list[x][1]
J[i,j] = weight_list[x]
I am struggling to figure out how to fill in the diagonal entries. edge_list is a list of tuples. To perform the computation in the above equation for L(G), I need to loop through the second entries of each tuple, store the first entry into a temporary list, sum over all the elements of that temporary list, and finally store the negative of the sum in the correct diagonal entry of L(G).
Any suggestions would be greatly appreciated, especially if there are steps above that can be done more efficiently or elegantly.
I adjusted networkx.laplacian_matrix function for undirected graphs a little bit
import networkx as nx
import scipy.sparse
G = nx.DiGraph()
G.add_nodes_from([1, 2, 3])
G.add_weighted_edges_from([(1, 2, 1), (1, 3, 1), (2, 1, 1), (2, 3, 1), (3, 1, 1), (3, 2, 1)])
nodelist = list(G)
A = nx.to_scipy_sparse_matrix(G, nodelist=nodelist, weight="weight", format="csr")
n, m = A.shape
diags = A.sum(axis=0) # 1 = outdegree, 0 = indegree
D = scipy.sparse.spdiags(diags.flatten(), [0], m, n, format="csr")
print((A - D).todense())
# [[-2 1 1]
# [ 1 -2 1]
# [ 1 1 -2]]
I will deviate a little from your method, since I prefer to work with Numpy if possible :P.
In the following snippet, I generate test data for a network of n=10 nodes; that is, I generate an array of tuples V to populate with random nodes, and also a (n,n) array A with the values of the edges between nodes. Hopefully the code is somewhat self-explanatory and is correct (let me know otherwise):
from random import sample
import numpy as np
# Number and list of nodes
n = 10
nodes = list(np.arange(n)) # random.sample needs list
# Test array of linked nodes
# V[i] is a tuple with all nodes the i-node connects to.
V = np.zeros(n, dtype = tuple)
for i in range(n):
nv = np.random.randint(5) # Random number of edges from node i
# To avoid self-loops (do not know if it is your case - comment out if necessary)
itself = True
while itself:
cnodes = sample(nodes, nv) # samples nv elements from the nodes list w/o repetition
itself = i in cnodes
V[i] = cnodes
# Test matrix of weighted edges (from i-node to j-node)
A = np.zeros((n,n))
for i in range(n):
for j in range(n):
if j in V[i]:
A[i,j] = np.random.random()*5
# Laplacian of network
J = np.copy(A) # This already sets the non-diagonal elements
for i in range(n):
J[i,i] = - np.sum(A[:,i]) - A[i,i]
Thank you all for your suggestions! I agree that numpy is the way to go. As a rudimentary solution that I will optimize later, this is what I came up with:
def Laplacian_all(edge_list,weight_list,num_nodes,num_edges):
J = np.zeros(shape = (num_nodes,num_nodes))
for x in range(num_edges):
i = edge_list[x][0]
j = edge_list[x][1]
J[i,j] = weight_list[x]
for i in range(num_nodes):
temp = []
for x in range(num_edges):
if i == edge_list[x][1]:
temp.append(weight_list[x])
temp_sum = -1*sum(temp)
J[i,i] = temp_sum
return J
I have yet to test this on different graphs, but this was what I was hoping to figure out for my immediate purposes.

Generating graph from a batch of adjacency matrices

I am trying to train a network for generating adjacency matrix for graphs. In the training process, for a single graph I use
import networkx as nx
import numpy as np
adj = np.asarray([[0,1,0,0],[1,0,1,0],[0,0,0,1], [0,0,1,0]])
G = nx.from_numpy_matrix(adj)
for transforming adjacency to graph. However, while training the network, I need to do this with a batch of matrices and it seems that networkx cannot do this. Is there a package that can handle the following:
import networkx as nx
import numpy as np
adjs = []
adjs.append(np.asarray([[0,1,0,0],[1,0,1,0],[0,0,0,1], [0,0,1,0]]))
adjs.append(np.asarray([[0,1,0,1],[1,0,0,0],[0,0,0,1], [1,0,1,0]]))
adjs = np.asarray(adjs)
G = nx.from_numpy_matrix(adjs)
You can add a map over the nx.from_numpy_matrix function to apply it over all the adjacency matrices in the adjs list. Something like this
import networkx as nx
import numpy as np
adjs = []
adjs.append(np.asarray([[0,1,0,0],[1,0,1,0],[0,0,0,1], [0,0,1,0]]))
adjs.append(np.asarray([[0,1,0,1],[1,0,0,0],[0,0,0,1], [1,0,1,0]]))
adjs = np.asarray(adjs)
graph_list = list(map(lambda adj_matrix:nx.from_numpy_matrix(adj_matrix), adjs))
Now, graph_list is simply a list of NetworkX graphs.
for graph in graph_list:
print("Printing information for Graph at index:", idx)
print(graph.nodes())
print(graph.edges())
# Output:
# Printing information for Graph at index: 0
# [0, 1, 2, 3]
# [(0, 1), (1, 2), (2, 3)]
# Printing information for Graph at index: 1
# [0, 1, 2, 3]
# [(0, 1), (0, 3), (2, 3)]
You can view the code here as well.
Reference:
Python Map Tutorial

Find edges in a cycle networkx python

I would like to make an algorithm to find if an edge belongs to a cycle, in an undirected graph, using networkx in Python.
I am thinking to use cycle_basis and get all the cycles in the graph.
My problem is that cycle_basis returns a list of nodes. How can I convert them to edges?
You can construct the edges from the cycle by connecting adjacent nodes.
In [1]: import networkx as nx
In [2]: G = nx.Graph()
In [3]: G.add_cycle([1,2,3,4])
In [4]: G.add_cycle([10,20,30])
In [5]: basis = nx.cycle_basis(G)
In [6]: basis
Out[6]: [[2, 3, 4, 1], [20, 30, 10]]
In [7]: edges = [zip(nodes,(nodes[1:]+nodes[:1])) for nodes in basis]
In [8]: edges
Out[8]: [[(2, 3), (3, 4), (4, 1), (1, 2)], [(20, 30), (30, 10), (10, 20)]]
Here is my take at it, using just lambda functions (I love lambda functions!):
import networkx as nx
G = nx.Graph()
G.add_cycle([1,2,3,4])
G.add_cycle([10,20,30])
G.add_edge(1,10)
in_path = lambda e, path: (e[0], e[1]) in path or (e[1], e[0]) in path
cycle_to_path = lambda path: list(zip(path+path[:1], path[1:] + path[:1]))
in_a_cycle = lambda e, cycle: in_path(e, cycle_to_path(cycle))
in_any_cycle = lambda e, g: any(in_a_cycle(e, c) for c in nx.cycle_basis(g))
for edge in G.edges():
print(edge, 'in a cycle:', in_any_cycle(edge, G))
in case you don't find a nice solution, here's an ugly one.
with edges() you can get a list of edges that are adjacent to nodes in a cycle. unfortunately, this includes edges adjacent to nodes outside the cycle
you can now filter the list of edges by removing those which connect nodes that are not part of the cycle.
please keep us posted if you find a less wasteful solution.
With the help of Aric, and a little trick to check both directions, I finally did this that looks ok.
import networkx as nx
G = nx.Graph()
G.add_cycle([1,2,3,4])
G.add_cycle([10,20,30])
G.add_edge(1,10)
def edge_in_cycle(edge, graph):
u, v = edge
basis = nx.cycle_basis(graph)
edges = [zip(nodes,(nodes[1:]+nodes[:1])) for nodes in basis]
found = False
for cycle in edges:
if (u, v) in cycle or (v, u) in cycle:
found = True
return found
for edge in G.edges():
print edge, 'in a cycle:', edge_in_cycle(edge, G)
output:
(1, 2) in a cycle: True
(1, 4) in a cycle: True
(1, 10) in a cycle: False
(2, 3) in a cycle: True
(3, 4) in a cycle: True
(10, 20) in a cycle: True
(10, 30) in a cycle: True
(20, 30) in a cycle: True
You can directly obtain the edges in a cycle with the find_cycle method. If you want to test if an edge belongs to a cycle, you should check if both of its vertices are part of the same cycle.
Using the example in the answers above:
import networkx as nx
G = nx.Graph()
G.add_cycle([1,2,3,4])
G.add_cycle([10,20,30])
G.add_edge(1,10)
nx.find_cycle(G, 1) # [(1, 2), (2, 3), (3, 4), (4, 1)]
nx.find_cycle(G, 10) # [(10, 20), (20, 30), (30, 10)]
On the other hand, the edge (2, 3) (or (3, 2) as your graph is undirected) is part of a cycle defined first:
nx.find_cycle(G, 2) # [(2, 1), (1, 4), (4, 3), (3, 2)]

Categories