Проблемы с получением узлов по атрибутам в NetworkX

Я разрабатываю код с использованием NetworkX, в котором у меня есть многостраничный граф, подобный следующему:

введите описание изображения здесь

Каждый узел имеет следующие атрибуты:

  • Слой: это относится к столбцу графа, в котором можно найти узел
  • Метка: для каждого столбца я присваиваю метку каждому узлу
  • Траектория: это отношение между узлами. Если узел связан с другим, эти узлы могут быть помечены по-разному, но будут иметь одну и ту же траекторию.

На фотографии выше каждая метка узла изображена как «label.layer» (в этом случае мы видим подмножество большего графа, поэтому номер слоя начинается с 451, а не с 0).

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

Выбрать сетевые узлы с заданным значением атрибута

Выбор узлов и ребер образуют граф networkx с атрибутами

for i in range(trajectory):
        sel_nodes = dict((node, attribute['trajectory']) for node, attribute in G.nodes().items() if attribute['trajectory'] == i)
        print(sel_nodes)

Это должно вернуть словарь для каждой «строки» узлов, однако на выходе будут следующие dicts:

{'0.451': 0, '0.452': 0, '0.453': 0, '0.454': 0, '0.455': 0, '0.456': 0, '0.457': 0, '0.458': 0, '0.459': 0, '0.460': 0}
{'1.451': 1, '1.452': 1, '1.453': 1, '1.454': 1, '1.455': 1, '1.456': 1, '1.457': 1, '1.458': 1, '1.459': 1, '1.460': 1}
{'2.451': 2, '3.452': 2, '3.453': 2, '3.454': 2, '3.455': 2, '3.456': 2, '3.457': 2, '3.458': 2, '3.459': 2, '4.460': 2}
{'3.451': 3, '2.452': 3, '2.453': 3, '2.454': 3, '2.455': 3, '2.456': 3, '2.457': 3, '2.458': 3, '2.459': 3, '3.460': 3}
{'4.451': 4, '4.452': 4, '4.453': 4, '4.454': 4, '4.455': 4, '4.456': 4, '4.457': 4, '4.458': 4, '4.459': 4, '5.460': 4}
{'5.451': 5, '5.452': 5, '5.453': 5, '5.454': 5, '5.455': 5, '5.456': 5, '5.457': 5, '5.458': 5, '5.459': 5, '6.460': 5}
{'6.451': 6, '6.452': 6, '6.453': 6, '6.454': 6, '6.455': 6, '6.456': 6, '6.457': 6, '6.458': 6, '6.459': 6, '7.460': 6}
{'7.451': 7, '7.452': 7, '7.453': 7, '7.454': 7, '7.455': 7, '7.456': 7, '7.457': 7, '7.458': 7, '7.459': 7, '8.460': 7}
{'8.451': 8, '8.452': 8, '8.453': 8, '8.454': 8, '8.455': 8, '8.456': 8, '8.457': 8, '8.458': 8, '8.459': 8, '9.460': 8}
{}
{}

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

Есть ли способ исправить такое поведение?

РЕДАКТИРОВАТЬ: Чтобы немного сузить проблему, я считаю, что проблема заключается в понимании словаря, которое я использовал, поскольку я проверил, имеет ли траектория атрибута присвоенное ей значение, выполнив:

print(G.nodes['9.455']['trajectory']) 

И результат дает мне траекторию 9, которая согласуется с тем, что я ожидаю от траектории.


person BizarroJr    schedule 26.05.2021    source источник


Ответы (1)


Мне кажется, что список dicts - это неправильная структура данных. Похоже, что вы действительно ищете структуру данных, которая сообщает вам, учитывая траекторию i, какие узлы принадлежат этой траектории? Наборы наборов или список наборов кажется более подходящим.

Вот как вы могли бы построить такой список на небольшом примере графа, где верхняя траектория представляет собой изолированный узел, а два нижних пересекают друг друга:

>>> import networkx as nx
>>> G = nx.Graph()
>>> G.add_nodes_from([(0.3, {"trajectory": 0}), (1.1, {"trajectory": 1}), (2.2, {"trajectory": 1}), (2.3, {"trajectory": 1}), (2.1, {"trajectory": 2}), (1.2, {"trajectory": 2}), (1.3, {"trajectory": 2})])
>>> from collections import defaultdict
>>> d = defaultdict(set)
>>> for node, attrs in G.nodes().items():
...     d[attrs["trajectory"]].add(node)
... 
>>> d
defaultdict(<class 'set'>, {0: {0.3}, 1: {1.1, 2.2, 2.3}, 2: {1.2, 2.1, 1.3}})

Если вам действительно нужен список dicts, как описано выше, вы можете легко создать его отсюда:

>>> for trajectory, nodes in d.items():
...     print({node: trajectory for node in nodes})
... 
{0.3: 0}
{1.1: 1, 2.2: 1, 2.3: 1}
{1.2: 2, 2.1: 2, 1.3: 2}

Если вам нужен упорядоченный список для каждой траектории вместо набора, вы можете отсортировать часть имени, которая идет после десятичной точки:

>>> d = {k: sorted(nodes, key=lambda x: str(x)[2:]) for k, nodes in d.items()}
>>> d
{0: [0.3], 1: [1.1, 2.2, 2.3], 2: [2.1, 1.2, 1.3]}
person Andrew Eckart    schedule 27.05.2021
comment
Очень благодарен за это, он отлично работает. Я относительно новичок в Python и, честно говоря, не знал, что это за наборы, но они определенно кажутся гораздо лучшим решением того, что я планировал. - person BizarroJr; 27.05.2021
comment
Единственный недостаток, который я вижу, это то, что набор неупорядочен, и, поскольку мой график показывает некоторую временную прогрессию, было бы неплохо заказать его, чтобы я позже проанализировал подмножества. Но я определенно могу найти решение. - person BizarroJr; 27.05.2021
comment
С вашей схемой именования легко преобразовать наборы в списки, которые дадут вам упорядоченную последовательность узлов для каждой траектории. Обновил свой ответ примером. - person Andrew Eckart; 27.05.2021
comment
Замечательно! Думаю, понимание в Python является ключом к достижению многих целей ... Думаю, мне следует узнать, как это работает, потому что я понятия не имел, насколько мощным он может быть. Опять же, очень благодарен за ваши ответы. - person BizarroJr; 28.05.2021