我在图中有一个节点,它充当一种“临时连接器”节点。我想删除该节点并更新图中的边,以便其所有直接前辈都指向其直接后继。
在networkx
中是否有内置功能可以执行此操作,还是需要推出自己的解决方案?
示例:
我有一个图1 > 2 > 3
。我想删除节点2
,最后得到图1 > 3
。
这是我目前的操作方式:
In [230]: g = nx.DiGraph()
In [231]: g.add_edges_from([(1,2),(2,3)])
In [232]: g.edges()
Out[232]: [(1, 2), (2, 3)]
In [233]: predecessors = g.predecessors(2)
In [234]: successors = g.successors(2)
In [235]: new_edges = [(p,s) for p in predecessors for s in successors]
In [236]: new_edges
Out[236]: [(1, 3)]
In [237]: g.remove_node(2)
In [238]: g.add_edges_from(new_edges)
In [239]: g.nodes()
Out[239]: [1, 3]
In [240]: g.edges()
Out[240]: [(1, 3)]
答案 0 :(得分:0)
我不知道我的解决方案是否比Ryan所建议的要好,但是我在这里发布了一个函数,因为它试图从不同的角度解决这个问题。他们的关键是,在graph 1 > 2 > 3
中,节点2
的度数为2(即,有2个边与其连接)。一般而言,从提出的问题的意义上简化图将意味着除去所有具有2级的此类节点。下面的函数正是这样做的。
def simplifyGraph(G):
''' Loop over the graph until all nodes of degree 2 have been removed and their incident edges fused '''
g = G.copy()
while any(degree==2 for _, degree in g.degree):
g0 = g.copy() #<- simply changing g itself would cause error `dictionary changed size during iteration`
for node, degree in g.degree():
if degree==2:
if g.is_directed(): #<-for directed graphs
a0,b0 = list(g.in_edges(node))[0]
a1,b1 = list(g.out_edges(node))[0]
else:
edges = g.edges(node)
edges = list(edges.__iter__())
a0,b0 = edges[0]
a1,b1 = edges[1]
e0 = a0 if a0!=node else b0
e1 = a1 if a1!=node else b1
g0.remove_node(node)
g0.add_edge(e0, e1)
g = g0
return g
一个例子:
>>> G = nx.DiGraph()
>>> G.add_edges_from([(1,2),(2,3)])
>>> list(G.edges)
[(1, 2), (2, 3)]
>>> g = simplifyGraph(G)
>>> list(g.edges)
[(1, 3)]
答案 1 :(得分:0)
我尝试在一个巨大的复杂 MultiGraph 上使用 mjvaak 的答案,但它不起作用,因为它使用不再存在的节点创建边。我通过简单地从 g0 中取边来修复它。
所以我改变了:
edges = g.edges(node)
用于:
edges = g0.edges(node)
固定代码如下:
def simplifyGraph(G):
''' Loop over the graph until all nodes of degree 2 have been removed and their incident edges fused '''
g = G.copy()
while any(degree==2 for _, degree in g.degree):
g0 = g.copy() #<- simply changing g itself would cause error `dictionary changed size during iteration`
for node, degree in g.degree():
if degree==2:
if g.is_directed(): #<-for directed graphs
a0,b0 = list(g0.in_edges(node))[0]
a1,b1 = list(g0.out_edges(node))[0]
else:
edges = g0.edges(node)
edges = list(edges.__iter__())
a0,b0 = edges[0]
a1,b1 = edges[1]
e0 = a0 if a0!=node else b0
e1 = a1 if a1!=node else b1
g0.remove_node(node)
g0.add_edge(e0, e1)
g = g0
return g
感谢 mjkvaak 的解决方案!对于更大的图表,它只需要稍作修改。