[英]How to match subgraphs with edge attributes in networkx correctly?
我是子圖匹配的新手,我發現networkx
有一種非常方便的方法來做到這一點。 但是我不確定如何在具有邊緣屬性的圖上正確匹配子圖。
例如,假設我有以下具有邊緣屬性的圖表:
import networkx as nx
import networkx.algorithms.isomorphism as iso
G = nx.MultiDiGraph()
G.add_edge(1, 2, label="A")
G.add_edge(1, 2, label="D")
G.add_edge(2, 1, label="B")
G.add_edge(2, 3, label="C")
我正在嘗試找到具有以下結構的子圖:
SG = nx.MultiDiGraph()
SG.add_edge(5, 6)
SG.add_edge(6, 7)
請注意,此子圖沒有邊屬性,因此我試圖找到具有適合節點結構的任何邊的子圖(在本例中為長度為 2 的簡單路徑)。 我發現這 2 個 SO 問題( 一、 二)指導我嘗試以下 2 種方法:
方法一
gm = iso.MultiDiGraphMatcher(G, SG, edge_match=iso.categorical_edge_match("label", None))
print(gm.subgraph_is_monomorphic())
for subgraph_x in gm.subgraph_monomorphisms_iter():
print(subgraph_x)
這打印:
True
{1: 5, 2: 6, 3: 7}
但是,我假設這將打印兩個不同的子圖,因為節點 1 和 2 之間有 2 條不同的邊。意思是,這兩個子圖應該是((1,2) with edge label "A", (2,3) with edge label "C"), and ((1,2) with edge label "D", (2,3) with edge label "C")
。 我也找不到 output 邊緣標簽對應於 output 節點的方法,這將有助於了解此輸出的路徑。
方法二
SG = nx.MultiDiGraph()
SG.add_edge(5, 6, label="A")
SG.add_edge(6, 7, label="C")
gm = iso.MultiDiGraphMatcher(G, SG, edge_match=lambda x, y: x[0]['label'] == y[0]['label'])
print(gm.subgraph_is_monomorphic())
for subgraph_x in gm.subgraph_monomorphisms_iter():
print(subgraph_x)
這打印
True
{1: 5, 2: 6, 3: 7}
在這里,我在子圖中指定了我想要的邊,並且 output 是有意義的,因為只有 1 個具有該結構的子圖。 例如,如果我將SG.add_edge(6, 7, label="C")
更改為SG.add_edge(6, 7, label="D")
,則會打印False
。
但是,對於我的用例,我需要第一種方法來工作,因為我的輸入是一個沒有邊緣屬性的子圖。
我還嘗試使用沒有邊緣屬性的圖並在subgraph_isomorphisms_iter
和subgraph_monomorphisms_iter
之間進行更改:
G = nx.MultiDiGraph([(1,2), (1,3), (1,4), (1,4), (4,5), (5,6), (1,7), (7,8)])
subgraph = nx.MultiDiGraph([(10,20), (10,30), (10,40), (40,50)])
subgraphs = []
GM = iso.MultiDiGraphMatcher(G,subgraph,edge_match=iso.categorical_edge_match([],[]))
for subgraph_x in GM.subgraph_monomorphisms_iter():
subgraphs.append(subgraph_x)
subgraphs
使用subgraph_monomorphisms_iter
打印:
[{1: 10, 2: 20, 3: 30, 4: 40, 5: 50},
{1: 10, 2: 20, 3: 30, 7: 40, 8: 50},
{1: 10, 2: 20, 4: 30, 7: 40, 8: 50},
{1: 10, 2: 20, 7: 30, 4: 40, 5: 50},
{1: 10, 3: 20, 2: 30, 4: 40, 5: 50},
{1: 10, 3: 20, 2: 30, 7: 40, 8: 50},
{1: 10, 3: 20, 4: 30, 7: 40, 8: 50},
{1: 10, 3: 20, 7: 30, 4: 40, 5: 50},
{1: 10, 4: 20, 2: 30, 7: 40, 8: 50},
{1: 10, 4: 20, 3: 30, 7: 40, 8: 50},
{1: 10, 7: 20, 2: 30, 4: 40, 5: 50},
{1: 10, 7: 20, 3: 30, 4: 40, 5: 50}]
這似乎非常多余,因為應該只有 3 個子圖,但即使使用subgraph_isomorphisms_iter
似乎也能找到 2 個多余的子圖:
subgraphs = []
GM = nx.algorithms.isomorphism.DiGraphMatcher(G,subgraph) #dictionary that maps nodes of main graph to nodes of subgraph.
for subgraph_x in GM.subgraph_isomorphisms_iter():
subgraphs.append(subgraph_x)
這打印
[{1: 10, 2: 20, 3: 30, 7: 40, 8: 50}, {1: 10, 3: 20, 2: 30, 7: 40, 8: 50}]
但是,當我嘗試將前兩種方法切換到subgraph_isomorphisms_iter
時,它沒有找到任何子圖。 所以我很困惑。
更新我發現這個SO問題很好地解釋了networkx中同構和單態之間的區別。 並找到匹配器的源代碼,如果有幫助的話。 從這里看來,冗余是對稱性的結果,如果我理解正確,networkx 不會考慮對稱性。
我的做法:
將 MultiDiGraph 擴展為有向圖,對擴展的有向圖執行同構檢查,這樣您就可以考慮多個邊,因為它們是單獨計算的:
import networkx as nx
import networkx.algorithms.isomorphism as iso
G = nx.MultiDiGraph()
G.add_edge(1, 2, label="A")
G.add_edge(1, 2, label="D")
G.add_edge(2, 1, label="B")
G.add_edge(2, 3, label="C")
SG = nx.MultiDiGraph()
SG.add_edge(5, 6)
SG.add_edge(6, 7)
def expand_graph(G):
'''
Expand the graph such that for each edge e from u to v:
e=(u,v) you create an edge specific vertex: e -> (u,e_)(e_,u)
This way your MultiDiGraph becomes a DiGraph
'''
H = nx.DiGraph()
for u in G.nodes():
key = 0
for e in G.out_edges(u, data=True):
H.add_edge(u, str(u)+'_out_'+str(key))
H.add_edge(str(u)+'_out_'+str(key), e[1])
key+=1
nx.set_node_attributes(H, {n:0 for n in H.nodes()}, 'original')
nx.set_node_attributes(H, {n:1 for n in G.nodes()}, 'original')
return H
def collapse_graph(G_):
'''
Turns a DiGraph generated with the expand_graph function back
to a MultiDiGraph, adds edge attribtute label to distinguish edges
'''
H = nx.MultiDiGraph()
for u, d in G_.nodes(data=True):
if d['original']==1:
for u, e_ in G_.out_edges(u):
for e_, v in G_.out_edges(e_):
H.add_edge(u, v, label = e_.split('_')[-1])
return H
G_ = expand_graph(G)
SG_ = expand_graph(SG)
subgraphs = []
GM = nx.algorithms.isomorphism.DiGraphMatcher(G_,SG_)
#Perform the check solely for isomorph subgraphs,
#note this includes also isomorph subgraphs on the set of vertices e_
for subgraph_x in GM.subgraph_isomorphisms_iter():
subgraphs.append(subgraph_x)
result=[]
subgraphs_valid = []
#This gets rid of subgraphs that are isomorph but not on the right set of nodes,
#i.e. we want to check whether the nodes are of mixed type (e_ mixed with u)
for s in subgraphs:
if all([True if type(u)== type(v) else False for u,v in s.items()]):
subgraphs_valid.append(s)
#Iterate over the isomorph subgraphs, extract the subgraphs and parse them as results
#Collapse them in order to get a edge representations with labels for the edges
for s in subgraphs_valid:
f = [k for k, v in s.items()]
F_ = G_.subgraph(f)
F = collapse_graph(F_)
result.append(list(F.edges(data=True)))
print(result)
我相信有一個更簡單的解決方案......但也許這種方法可以幫助你。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.