21

我正在使用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 继承自有向图并封装了另一个有向图(这个颠倒了)。我已经覆盖了添加和删除节点和边的方法。

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)

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

4

4 回答 4

39

有一个前身(和前任_iter)方法: http: //networkx.lanl.gov/reference/generated/networkx.DiGraph.predecessors.html#networkx.DiGraph.predecessors

此外,没有什么可以阻止您直接以 G.pred 访问数据结构

 In [1]: import networkx as nx
 In [2]: G = nx.DiGraph() # a directed graph
 In [3]: G.add_edge('a', 'b')
 In [4]: G.predecessors('b')
 Out[4]: ['a']
 In [5]: G.pred['b']
 Out[5]: {'a': {}}
于 2010-11-04T15:00:10.413 回答
8

另一种实现方式如下:

创建基本图

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')))

此博客中的更多详细信息

于 2018-05-02T19:16:16.747 回答
2

如果G是 的一个实例nx.DiGraph()并且node是您搜索其前任的源节点,则以下为您提供前任节点的列表:

predecessors = [pred for pred in G.predecessors(node)]
于 2019-05-20T08:34:02.010 回答
1

图并不总是一棵树,因此“父”的概念通常没有意义。因此,我认为这没有实现。

要实现您所需要的,请继承DiGraph并重载所有允许添加节点的方法。根据该信息构建树数据结构。

于 2010-09-28T08:08:38.487 回答