Подтвердить что ты не робот

Самый элегантный способ найти предшественников node с помощью сетиXX

Я работаю над графическим проектом модели с помощью python, используя NetworkX. 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', а не наоборот).

Для данного node я хочу найти предшественники этого node. В приведенном выше примере par ('b') должен возвращать ['a']. NetworkX имеет функцию-преемник, которая находит дочерние элементы любого node. Очевидно, что, пройдя все узлы и найдя те, у которых есть "b", как ребенок, будет работать, но это будет Q (n) в количестве узлов (что будет слишком дорого для моего приложения).

Я не могу себе представить, что что-то такое простое было бы исключено из этого хорошо сделанного пакета, но ничего не может найти.

Одним эффективным вариантом является сохранение направленной и неориентированной версии графика; все неориентированные ребра по существу реализуются путем добавления обоих направленных ребер, и поэтому можно было бы установить разницу между соседними узлами и дочерними элементами (которая была бы предшественником).

Проблема заключается в том, что я не уверен в наиболее питоническом способе обернуть существующий класс DiGraph и Graph для этого. На самом деле я просто хочу получить класс PGraph, который ведет себя точно так же, как и класс DiGraph для сети, но имеет функцию predecessors(node) в дополнение к функции successors(node).

Должен ли PGraph наследовать от DiGraph и инкапсулировать Graph (для использования в функции предшественников)? Как тогда я должен заставить все узлы и ребра быть добавлены как к направленным, так и к неориентированным графам, которые он содержит? Должен ли я просто переопределить функции для добавления и удаления узлов и ребер в PGraph (чтобы они были добавлены и удалены как из направленной, так и неориентированной версии)? Я беспокоюсь, что, если я пропущу что-то неясное, у меня будет головная боль позже, что может не означать хороший дизайн.

Или (и, пожалуйста, пусть это будет True) есть простой способ получить предшественников node в networkx.DiGraph, и я полностью пропустил его?

Большое спасибо за вашу помощь.


EDIT:

Я думаю, что это делает эту работу. 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)

Что вы думаете об этом решении? Это может удвоить использование памяти, но я думаю, что это приемлемо. Это слишком сложно? Это хороший дизайн?

4b9b3361

Ответ 1

Существует предшественник (и метод 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': {}}

Ответ 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')))

Подробнее в этом блоге

Ответ 3

Граф не всегда является деревом, поэтому понятие "родитель" часто не имеет смысла. Поэтому я предполагаю, что это не реализовано.

Чтобы реализовать то, что вам нужно, наследуйте от DiGraph и перегружайте все методы, которые позволяют добавлять узлы. Создайте структуру данных дерева из этой информации.

Ответ 4

Если G является экземпляром nx.DiGraph() а node - это исходный узел, чьи предшественники вы ищете, следующий список дает вам список предшественников:

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