Merge and interact between two graphs in networkx - python

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)

Related

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

How to iterate through edges in a graph if they are not in a pre-selected list

I am filtering a subset of edges so I can iterate through them. In this case, I am excluding the "end edges", which are the final edges along a chain:
import networkx as nx
graph = nx.Graph()
graph.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 4)])
end_nodes = [n for n in graph.nodes if nx.degree(graph, n) == 1]
end_edges = graph.edges(end_nodes)
print(f"end edges: {end_edges}")
for edge in graph.edges:
if edge not in end_edges:
print(f"edge {edge} is not an end edge.")
else:
print(f"edge {edge} is an end edge.")
However, when you run this code, you get the following output:
end edges: [(0, 1), (4, 3)]
edge (0, 1) is an end edge.
edge (1, 2) is an end edge.
edge (2, 3) is an end edge.
edge (3, 4) is an end edge.
Edges (1, 2) and (2, 3) are not in end_edges, yet it returns False when the conditional edge not in end_edges is checked (seeming to imply that it is in fact included, when it seems to not be).
What is going on, and how can I filter this properly?
Python version is 3.7, NetworkX is 2.4.
You can convert end_nodes to a set of edges and keep the edges unordered.
>>> graph = nx.Graph()
>>> graph.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 4)])
>>> end_nodes = [n for n in graph.nodes if nx.degree(graph, n) == 1]
>>> end_edges = set(map(frozenset, graph.edges(end_nodes)))
>>> end_edges
{frozenset({3, 4}), frozenset({0, 1})}
>>> for edge in graph.edges:
... print(edge, frozenset(edge) in end_edges)
...
(0, 1) True
(1, 2) False
(2, 3) False
(3, 4) True
import networkx as nx
graph = nx.Graph()
graph.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 4)])
end_nodes = [n for n in graph.nodes if nx.degree(graph, n) == 1]
end_edges = graph.edges(end_nodes)
print(f"end edges: {end_edges}")
for edge in graph.edges:
if edge not in list(end_edges):
print(f"edge {edge} is not an end edge.")
else:
print(f"edge {edge} is an end edge.")
This should return what you ask for.

Graph isomorphism with constraints on the edges using networkx

I would like to define my own isomorphism of two graphs. I want to check if two graphs are isomorphic given that each edge has some attribute --- basically the order of placing each edge. I wonder if one can use the method:
networkx.is_isomorphic(G1,G2, edge_match=some_callable)
somehow by defining function some_callable().
For example, the following graphs are isomorphic, because you can relabel the nodes to obtain one from another.
Namely, relabel [2<->3].
But, the following graphs are not isomorphic.
There is no way to obtain one from another by re-labeling the nodes.
Here you go. This is exactly what the edge_match option is for doing. I'll create 3 graphs the first two are isomorphic (even though the weights have different names --- I've set the comparison function to account for that). The third is not isomorphic.
import networkx as nx
G1 = nx.Graph()
G1.add_weighted_edges_from([(0,1,0), (0,2,1), (0,3,2)], weight = 'aardvark')
G2 = nx.Graph()
G2.add_weighted_edges_from([(0,1,0), (0,2,2), (0,3,1)], weight = 'baboon')
G3 = nx.Graph()
G3.add_weighted_edges_from([(0,1,0), (0,2,2), (0,3,2)], weight = 'baboon')
def comparison(D1, D2):
#for an edge u,v in first graph and x,y in second graph
#this tests if the attribute 'aardvark' of edge u,v is the
#same as the attribute 'baboon' of edge x,y.
return D1['aardvark'] == D2['baboon']
nx.is_isomorphic(G1, G2, edge_match = comparison)
> True
nx.is_isomorphic(G1, G3, edge_match = comparison)
> False
Here answer the problem specifically in the question, with the very same graphs. Note that I'm using the networkx.MultiGraph and consider some 'ordering' in placing those edges.
import networkx as nx
G1,G2,G3,G4=nx.MultiGraph(),nx.MultiGraph(),nx.MultiGraph(),nx.MultiGraph()
G1.add_weighted_edges_from([(0, 1, 0), (0, 2, 1), (0, 3, 2)], weight='ordering')
G2.add_weighted_edges_from([(0, 1, 0), (0, 3, 1), (0, 2, 2)], weight='ordering')
G3.add_weighted_edges_from([(0, 1, 0), (0, 1, 1), (2, 3, 2)], weight='ordering')
G4.add_weighted_edges_from([(0, 1, 0), (2, 3, 1), (0, 1, 2)], weight='ordering')
def comparison(D1,D2):
return D1[0]['ordering'] == D2[0]['ordering']
nx.is_isomorphic(G1,G2, edge_match=comparison)
>True
nx.is_isomorphic(G3,G4, edge_match=comparison)
>False

NetworkX convert MultiGraph to simple Graph by averaging parallel length

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})])

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