我正在使用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 {}
我想使用有向图,因为我正在编写具有方向的依赖项(在上面的示例中,我有'b'的封闭形式,条件是'a',而不是相反)。
对于给定节点,我想找到该节点的前身。对于上面的例子,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)
您对此解决方案有何看法?它可能会使内存使用量增加一倍,但我认为这是可以接受的。太复杂了吗?这是好设计吗?
答案 0 :(得分:27)
有一个前身(和predecessor_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': {}}
答案 1 :(得分: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')))
此blog
中的详细信息答案 2 :(得分:1)
图形并不总是树,因此“父”的概念通常没有意义。因此,我认为这没有实现。
要实现所需内容,请继承DiGraph
并重载所有允许添加节点的方法。根据该信息构建树数据结构。
答案 3 :(得分:0)
如果G
是nx.DiGraph()
的实例,而node
是要搜索其前身的源节点,则以下内容将为您提供前身节点的列表:
predecessors = [pred for pred in G.predecessors(node)]