I have a MultiGraph which can have multiple edges between any two nodes:
g = MultiGraph()
g.add_nodes_from([1,2,3,4,5,6])
g.add_edges_from([(1,2), (2,1), (1,2), (3,4), (5,6), (6,5), (3,5), (3, 5), (3, 5), (3, 5)])
How can I find all edges with multiplicity larger than 2 in this graph? I can do something like this:
for s in itertools.combinations(g.nodes(), 2):
e = g[s[0]].get(s[1], {})
if len(e) > 2:
print(s[0], s[1])
but it is too inefficient, so I'm looking for a better solution
for u in G.nodes():
for neighbor in G.neighbors(u):
if G.number_of_edges(u,neighbor)>2:
print (u,neighbor)
Note - each such edge will be printed twice.
[x for x in tmp.edges if tmp.number_of_edges(x[0], x[1]) > 1]
Related
I have a Graph (G) where nodes are split into two distinct subgraphs (H, I). The nodes within each subgraph are interconnected. What I'm looking for is a list of edges that are connections between the subgraphs and only those in a way thats somewhat scaleable to big graphs.
Setup:
import networkx as nx
G = nx.path_graph(10)
H = nx.subgraph(G, [0,1,2,3,4])
I = nx.subgraph(G, [5,6,7,8,9])
G.add_edge(1,7)
G.add_edge(2,9)
Output I want:
[(4,5),(1,7),(2,9)]
You can simply loop over the edges and check whether they belong to the different graphs.
This should be O(n) are graphs behave like sets:
out = [e for e in G.edges
if e[0] in H and e[1] in I
or e[0] in I and e[1] in H]
output:
[(1, 7), (2, 9), (4, 5)]
I have a face-vertex mesh like the following image has.
Face-Vertex Mesh
I have 'face_list' and 'vertex_list' data sets, but not sure how to efficiently calculate a list of edges.
Compute the normal vectors of the faces. If the normal vectors of 2 adjacent faces point in different directions, the two vertices shared by the face form an edge.
The normal vectors of a face can be computed with the Cross product. For a face with the vertices A, B, C, the unit normal vector is:
N = normalized(cross(B-A, C-A))
The normal vectors of the faces can be compared with the Dot product. 2 normal vectors N1 and N2 are equal directed if:
equally_directed = abs(dot(N1, N2)) == 1
Use a library for the vector arithmetic. For example OpenGL Mathematics (GLM) library for Python or NumPy.
Minimal example:
import glm, math
vertices = [(-1,-1,-1), ( 1,-1,-1), ( 1, 1,-1), (-1, 1,-1), (-1,-1, 1), ( 1,-1, 1), ( 1, 1, 1), (-1, 1, 1)]
faces = [(0,1,2), (0,2,3), (5,4,7), (5,7,6), (4,0,3), (4,3,7), (1,5,6), (1,6,2), (4,5,1), (4,1,0), (3,2,6), (3,6,7)]
v = [glm.vec3(vertex) for vertex in vertices]
nv_list = [glm.normalize(glm.cross(v[i1]-v[i0], v[i2]-v[i0])) for i0,i1,i2 in faces]
edge_threshold = 0.99
edges = []
for i, f1 in enumerate(faces):
for j, f2 in enumerate(faces[i+1:]):
edge_candidates = [(f1[0], f1[1]), (f1[1], f1[2]), (f1[2], f1[0])]
for ei0, ei1 in edge_candidates:
if ei0 in f2 and ei1 in f2:
cos_nv = math.fabs(glm.dot(nv_list[i], nv_list[j+i+1]))
if abs(cos_nv) < edge_threshold:
edges.append((ei0, ei1))
print(len(edges))
print(edges)
Output:
12
[(1, 2), (0, 1), (3, 0), (2, 3), (4, 7), (5, 4), (6, 5), (7, 6), (4, 0), (3, 7), (1, 5), (6, 2)]
I am trying to found the longest way in networkx from node A to node A using only 10 other nodes in fully connected graph as shown below. Is it possible?
G = nx.Graph()
for i in range(len(hashlist)):
for j in range(len(hashlist)):
if i!=j:
connection=(i,j)
size=hashlist[i]-hashlist[j]
G.add_edge(i, j, weight=size)
This will find the longest cycle in a directed graph.
import networkx as nx
G = nx.Graph([(0, 0), (0, 1), (0, 2), (1, 2), (2, 0), (2, 1), (2, 2)]).to_directed()
a = sorted(list(nx.simple_cycles(G)), key = lambda s: len(s))
print(a[-1])
The same approach will work for weighted graph.
import networkx as nx
G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
G.add_weighted_edges_from([(0, 1, 3.0), (1, 2, 7.5), (2,3, 5), (1,3, 2.4)])
G = G.to_directed()
a = sorted(list(nx.simple_cycles(G)), key = lambda s: len(s))
print(a[-1])
I have a graph where my nodes can have multiple edges between them in both directions and I want to set the width between the nodes based on the sum of all edges between them.
import networkx as nx
nodes = [0,1]
edges = [(0,1),(1,0)]
G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
weights = [2,3]
nx.draw(G, width = weights)
I would like to have the width between 0 and 1 set to 5 as that is the summed weight.
First you need to create a MultiDiGraph and add all possible edges to it. This is because it supports multiple directed egdes between the same set of nodes including self-loops.
import networkx as nx
nodes = [0, 1, 2, 3, 4, 5]
edges = [(0,1), (1,0), (1, 0),(0, 1), (2, 3), (2, 3), (2, 3), (2, 3),
(4, 1), (4, 1), (4, 1), (4, 1), (4, 1), (4, 1), (4, 5), (5, 0)]
G = nx.MultiDiGraph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
Next, create a dictionary containing counts of each edges
from collections import Counter
width_dict = Counter(G.edges())
edge_width = [ (u, v, {'width': value})
for ((u, v), value) in width_dict.items()]
Now create a new DiGraph from the edge_width dictionary created above
G_new = nx.DiGraph()
G_new.add_edges_from(edge_width)
Plotting using thickened edges
This is an extension of answer mentioned here.
edges = G_new.edges()
weights = [G_new[u][v]['width'] for u,v in edges]
nx.draw(G_new, edges=edges, width=weights)
Add Edge labels
See this answer for more info.
pos = nx.spring_layout(G_new)
nx.draw(G_new, pos)
edge_labels=dict([((u,v,),d['width'])
for u,v,d in G_new.edges(data=True)])
nx.draw_networkx_edges(G_new, pos=pos)
nx.draw_networkx_edge_labels(G_new, pos, edge_labels=edge_labels,
label_pos=0.25, font_size=10)
You can also view this Google Colab Notebook with working code.
References
https://stackoverflow.com/a/25651827/8160718
https://stackoverflow.com/a/22862610/8160718
Drawing networkX edges
MultiDiGraph in NetworkX
Count occurrences of List items
I have this graph:
%matplotlib inline
import networkx as nx
G = nx.Graph()
G.add_edge(1, 2)
G.add_edge(2, 3)
G.add_edge(3, 4)
G.add_edge(3, 5)
G.add_edge(4, 6)
G.add_edge(5, 6)
G.add_edge(3, 7)
G.add_edge(7, 6)
G.add_edge(6, 8)
G.add_edge(8, 9)
nx.draw(G, pos=nx.spring_layout(G), with_labels=True)
Is it possible to get the subgraph between the nodes 3 and 6 without using nx.subgraph(G, [3,4,5,6,7]). I mean, what if I know that there is this subgraph, but I don't know e.g. about the 5?
My answer is very similar to back2basics, but more directly finds the nodes between the two. If there is a path from source to target, that path will be found by nx.all_simple_paths(G, source=source, target=target) which returns a generator for the paths.
import networkx as nx
G = nx.Graph()
G.add_edges_from([(1, 2), (2, 3), (3, 4), (3, 5), (4, 6), (5, 6), (3, 7), (7, 6), (6, 8), (8, 9)])
paths_between_generator = nx.all_simple_paths(G,source=3,target=6)
nodes_between_set = {node for path in paths_between_generator for node in path}
SG = G.subgraph(nodes_between_set)
the nodes_between_set = ... uses a "set generator". It is equivalent to
nodes_between_set = set()
for path in paths_beween_generator:
for node in path:
nodes_between_set.add(node)
The first 3 lines help made the list that you need to make the subset.
import networkx as nx
c_score = nx.algorithms.betweenness_centrality_subset(G,(3,), (6,))
nodes_between = [x for x in c_score if c_score[x]!=0.0]
nodes_between.extend((3,6)) #add on the ends
SG = G.subgraph(nodes_between)
nx.draw(SG, pos=nx.spring_layout(SG), with_labels=True)
One caveat: The subgraph points are defined as being in a path from point 3 to point 6
This works on the principle that
any node in the new subgraph is reachable from source or destination, and
any node on the path by definition has at least one predecessor and one successor (for a directed graph) or two neighbors (for an undirected graph).
So, first we find the subgraph of nodes that we can reach, and then recursively remove nodes without at least one predecessor and one successor until there is only the existing subgraph.
import networkx as nx
def subgraph_from_connections(G, source, target, directed = None):
included_nodes = [x for x in G.node if nx.has_path(G, source, x) and nx.has_path(G, x, target)]
G2 = G.subgraph(included_nodes)
# If this is a undirected graph, we only need to know if it only has 1 neighbor
# If this is a directed graph, then it needs at least 1 predecessor and at least 1 successor
if directed == True or (directed is None and type(G) == nx.classes.digraph.DiGraph):
removals = [x for x in G2.node if len(G2.predecessors(x)) == 0 or len(G2.successors(x)) == 0]
while len(removals) > 0:
G2.remove_nodes_from(removals)
removals = [x for x in G.node if len(G2.predecessors(x)) == 0 or len(G2.successors(x)) == 0]
else:
removals = [x for x in G2.node if len(G2.neighbors(x)) < 2]
while len(removals) > 0:
G2.remove_nodes_from(removals)
removals = [x for x in G2.node if len(G2.neighbors(x)) < 2]
return G2
Not extensively tested, but it worked for the few cases outlined here, and it includes 10/11 when those were included from Joel's test. The algorithm is fast enough - 130 ms for my 1000/10 node random test from before (maybe I shouldn't have deleted that after all).