使用 networkX 查找节点前辈的最优雅方式

新手上路,请多包涵

我正在使用 NetworkX 使用 python 开发图形模型项目。 NetworkX 使用字典提供简单而良好的功能:

 import networkx as nx
G = nx.DiGraph() # a directed graph
G.add_edge('a', 'b')
print G['a'] # prints {'b': {}}
print G['b'] # prints {}

我想使用有向图,因为我正在编写具有方向的依赖项(在上面的示例中,我有条件为“a”的“b”的封闭形式,而不是相反)。

对于给定的节点,我想找到该节点的前任。对于上面的例子,par(‘b’) 应该返回 [‘a’]。 NetworkX 确实有一个后继函数,它可以找到任何节点的子节点。显然,通过遍历所有节点并找到那些子节点为“b”的节点是可行的,但节点数量将是 Ω(n)(这对我的应用程序来说太昂贵了)。

我无法想象这么简单的东西会被排除在这个制作精良的包裹之外,但找不到任何东西。

一个有效的选择是存储图的有向和无向版本;所有无向边基本上都是通过添加两个有向边来实现的,因此可以采用相邻节点和子节点(这将是前身)之间的集合差异。

问题是我不确定包装现有 networkx DiGraph 和 Graph 类来完成此操作的最 pythonic 方法。真的,我只想得到一个类 PGraph,它的行为与 networkx DiGraph 类完全一样,但除了 predecessors(node) 函数之外,还有一个 successors(node) 函数。

PGraph是否应该继承DiGraph并封装Graph(以供前辈功能使用)?那么我应该如何强制将所有节点和边添加到它包含的有向图和无向图中呢?我是否应该重新实现在 PGraph 中添加和删除节点和边的功能(以便在有向和无向版本中添加和删除它们)?我担心如果我错过了一些晦涩难懂的东西,我以后会头疼,这可能并不意味着好的设计。

或者(请让它成为 True )是否有一种简单的方法可以在 networkx.DiGraph 中获取节点的前辈,而我完全错过了它?

非常感谢你的帮助。


编辑:

我认为这可以完成工作。 PGraph继承自DiGraph,封装了另一个DiGraph(这个倒过来)。我已经覆盖了添加和删除节点和边缘的方法。

 import networkx as nx

class PGraph(nx.DiGraph):
    def __init__(self):
        nx.DiGraph.__init__(self)
        self.reversed_graph = nx.DiGraph()
    def add_node(self, n, attr_dict=None, **attr):
        nx.DiGraph.add_node(self, n, attr_dict, **attr)
        self.reversed_graph.add_node(n, attr_dict, **attr)
    def add_nodes_from(self, ns, attr_dict=None, **attr):
        nx.DiGraph.add_nodes_from(self, ns, attr_dict, **attr)
        self.reversed_graph.add_nodes_from(ns, attr_dict, **attr)
    def add_edge(self, a, b, attr_dict=None, **attr):
        nx.DiGraph.add_edge(self, a, b, attr_dict, **attr)
        self.reversed_graph.add_edge(b, a, attr_dict, **attr)
    def add_edges_from(self, es, attr_dict=None, **attr):
        nx.DiGraph.add_edges_from(self, es, attr_dict, **attr)
        self.reversed_graph.add_edges_from(es, attr_dict, **attr)
    def remove_node(self, n):
        nx.DiGraph.remove_node(self, n)
        self.reversed_graph.remove_node(n)
    def remove_nodes_from(self, ns):
        nx.DiGraph.remove_nodes_from(self, ns)
        self.reversed_graph.remove_nodes_from(ns)
    def remove_edge(self, a, b):
        nx.DiGraph.remove_edge(self, b, a)
        self.reversed_graph.remove_edge(a, b)
    def remove_edges_from(self, es):
        nx.DiGraph.remove_edges_from(self, es)
        self.reversed_graph.remove_edges_from([ (b,a) for a,b in es])
# the predecessors function I wanted
    def predecessors(self, n):
        return self.reversed_graph.successors(n)

您如何看待这个解决方案?它可能会使内存使用量增加一倍,但我认为这是可以接受的。是不是太复杂了?这是好的设计吗?

原文由 user 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.8k
2 个回答

另一种实现方式如下:

创建基本图

import networkx as nx
import matplotlib.pyplot as plt

G = nx.DiGraph()
G.add_edges_from([('A', 'B'), ('A', 'C'), ('D', 'B'), ('E', 'C'), ('E','F'), ('B', 'H'), ('B', 'G'), ('B', 'F'), ('C', 'G'), ('Q', 'D')])

pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos, cmap=plt.get_cmap('jet'),node_size = 50)
nx.draw_networkx_edges(G, pos, edge_color='r', arrows=True)
nx.draw_networkx_labels(G, pos)
plt.show()

寻找下游边缘

print("Downstream Edges of 'B' (just example)-->")
print(list(nx.dfs_edges(G,'B')))

寻找上游边缘

print("Upstream Edges of 'B' (just example)-->")
print(list(nx.edge_dfs(G,'B', orientation='reverse')))

博客 中的更多详细信息

原文由 KuboAndTwoStrings 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题