NetworkX中如何将有向图双向边转换为无向边
NetworkX 有向图转仅保留双向边的无向图实现
转换规则说明:
- 输出无向图h和输入有向图g的节点集合完全一致
- 无向边
(u, v)存在于h中,当且仅当g中同时存在u→v和v→u两条有向边
示例输入的有向图结构如下:
对应定义代码:
import networkx as nx g = nx.DiGraph([("A", "B"), ("B", "A"), ("A", "C"), ("C", "A"), ("B", "C"), ("C", "B"), ("A", "D"), ("C", "D"), ("B", "E"), ("C", "E")])
期望输出的无向图结构如下:
对应定义代码:
h = nx.Graph([("A", "B"), ("A", "C"), ("B", "C")]) h.add_nodes_from(["D", "E"])
下面提供两种可直接使用的实现方案:
方案1:直接构造独立新图(易读性最高,适合初学者)
逻辑直观:先复制所有节点保证节点集合完全一致,再筛选双向存在的边加入无向图,最终得到的h是完全独立于g的新图,修改g不会影响h。
def f(g: nx.DiGraph) -> nx.Graph: h = nx.Graph() # 复制所有节点(包含节点自带属性) h.add_nodes_from(g.nodes(data=True)) # 遍历所有有向边,筛选双向边 for u, v in g.edges(): # 加u<v判断是为了避免对同一对节点重复判断,提升效率 # 如果节点是不支持大小比较的自定义类型,可以删掉这个判断,不影响最终结果 if u < v and g.has_edge(v, u): h.add_edge(u, v, **g.edges[u, v]) return h
测试验证:
h = f(g) print(sorted(h.nodes())) # 输出 ['A', 'B', 'C', 'D', 'E'] print(sorted(h.edges())) # 输出 [('A', 'B'), ('A', 'C'), ('B', 'C')]
运行结果和预期完全一致。
方案2:用过滤器构造动态视图(内存占用更低)
即你提到的图视图+过滤器实现方式,这种方案不会复制原图数据,生成的h是和g动态关联的视图,修改g的边/节点时h会自动同步,适合处理大图、不需要独立副本的场景。
from networkx import graphviews def f(g: nx.DiGraph) -> nx.Graph: # 先转成无向基础视图 undirected_base = g.to_undirected(as_view=True) # 定义边过滤规则:仅保留双向都存在的边 def is_bidirectional(u, v): return g.has_edge(u, v) and g.has_edge(v, u) # 返回过滤后的视图(默认保留所有节点,仅过滤边) return graphviews.subgraph_view(undirected_base, filter_edge=is_bidirectional)
如果需要把视图转成独立的静态图,只需要在外层套一层nx.Graph()即可:
h = nx.Graph(f(g))
内容的提问来源于stack exchange,提问作者W.P. McNeill
相关产品推荐
相关产品推荐

