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

发布于 2024-09-25 01:42:48 字数 2804 浏览 0 评论 0原文

我正在使用 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 类完全相同,但除了 successors(node) 函数之外,还有一个 predecessors(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)

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

I'm working on a graphical model project with python using NetworkX. NetworkX provides simple and good functionality using dictionaries:

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

I want to use directed graphs because I am coding dependencies that have directions (in the above example I have the closed form for 'b' conditional on 'a', not the other way around).

For a given node, I want to find the predecessors of that node. For the above example, par('b') should return ['a']. NetworkX does have a successor function, which finds the children of any node. Obviously, by going through all the nodes and finding those that have 'b' as a child will work, but it will be Ω(n) in the number of nodes (which will be too expensive for my application).

I cannot imagine that something this simple would be left out of this well-made package, but can't find anything.

One efficient option is to store a directed and an undirected version of the graph; all undirected edges are essentially implemented by adding both directed edges, and so it would be possible to take the set-wise difference between the adjacent nodes and the children (which would be the predecessor).

The trouble is I'm not sure of the most pythonic way to wrap the existing networkx DiGraph and Graph class to accomplish this. Really I just want to end up with a class PGraph that behaves exactly like the networkx DiGraph class but has a predecessors(node) function in addition to the successors(node) function.

Should PGraph inherit from DiGraph and encapsulate Graph (for use in the predecessors function)? How then should I force all nodes and edges to be added to both the directed and undirected graphs that it contains? Should I just reimplement the functions for adding and removing nodes and edges in PGraph (so that they are added and removed from both the directed and undirected version)? I worry that if I miss something obscure I'll be in for a headache later, which may not imply good design.

Or (and please let this be True) is there simply an easy way to get a node's predecessors in a networkx.DiGraph and I've completely missed it?

Thanks a lot for your help.


EDIT:

I think this does the job. PGraph inherits from DiGraph and encapsulates another DiGraph (this one reversed). I've overridden the methods to add & remove nodes & edges.

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)

What do you think about this solution? It might double the memory usage, but I think that's acceptable. Is it too complicated? Is this good design?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

幸福不弃 2024-10-02 01:42:48

有一个前驱(和前驱_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': {}}

There is a predecessor (and predecessor_iter) method:
http://networkx.lanl.gov/reference/generated/networkx.DiGraph.predecessors.html#networkx.DiGraph.predecessors

Also there is nothing stopping you from accessing the data structure directly as 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': {}}
小鸟爱天空丶 2024-10-02 01:42:48

另一种实现方法如下:

创建基本图

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

更多详细信息请参见 博客

Another way to implement this can be as follows:

Creating the basic graph

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

Finding Downstream Edges

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

Finding Upstream Edges

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

More details in this blog

记忆里有你的影子 2024-10-02 01:42:48

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

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

A graph is not always a tree, so the notion of "parent" often makes no sense. Therefore, I assume that this is not implemented.

To implement what you need, inherit from DiGraph and overload all methods which allow to add nodes. Build the tree data structure from that information.

只想待在家 2024-10-02 01:42:48

如果 Gnx.DiGraph() 的实例,并且 node 是您搜索其前辈的源节点,则以下内容将为您提供一个列表前驱节点:

predecessors = [pred for pred in G.predecessors(node)]

If G is an instance of nx.DiGraph() and node is the source node whose predecessors you search, the following gives you a list of predecessor nodes:

predecessors = [pred for pred in G.predecessors(node)]
寂寞笑我太脆弱 2024-10-02 01:42:48

一般来说,如果您尝试创建一个与现有图相比边缘方向相反的有向图(您对 PGraph 类所做的操作),转置邻接矩阵 应该可以解决问题。

具体来说,在 NetworkX 中,可以使用 DiGraph.reverse 方法用于此目的。

(如果由于某种原因您被迫使用没有 predecessorspred 的非常旧的 NetworkX 版本 - 反转图表并使用 successors< /代码>。)

In general, if you're trying to create a directed graph where the edge directions are reversed compared to an existing graph (what you did with your PGraph class), transposing the adjacency matrix should do the trick.

Specifically, in NetworkX, one can use the DiGraph.reverse method for this.

(If for some reason you're forced to use a very old version of NetworkX that didn't have predecessors or pred - reverse the graph and use successors.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文