Networkx: обновить единый атрибут всех узлов в графе без цикла for

#python #networkx

#python #networkx

Вопрос:

У меня есть некоторый код, который может обновлять атрибут графика:

 import networkx as nx

def update_nodes(graph):
    values = dict.fromkeys(graph.nodes, True)
    nx.set_node_attributes(graph, name='test_attribute', values=values)
    print(f"graph.nodes.data(): {graph.nodes.data()}")
    return graph

def loop(graph):
    graph.graph['test_attribute'] = False
    print(f"graph.nodes.data(): {graph.nodes.data()}")
    print(f"graph.graph['test_attribute']: {graph.graph['test_attribute']}")
    for node in range(0, len(graph.nodes)):
        print(f"graph.nodes[node]['test_attribute']: {graph.nodes[node]['test_attribute']}")
    return graph

graph = nx.erdos_renyi_graph(n = 3, p = 0.1, seed = 5)
for i in range(0, 2):
    graph = update_nodes(graph)
    graph = loop(graph)

 

Это не обновляет атрибут узла, а вместо этого применяет обновление к графу как к отдельному объекту. Есть ли какой-либо способ массового обновления одного атрибута всего набора узлов, не вставляя for node in (range(0, len(graph.nodes)): graph.nodes[node]['test_attribute'] = <new_value> где-нибудь?

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

Комментарии:

1. Что плохого в этом понятном, понятном простом for цикле из 2 строк?

2. Вы могли бы просто упростить этот цикл for node in graph.nodes: node['test_attribute'] = <new_value> вместо итерации индексов…

3. Спасибо, это уже улучшение, но склонность к отказу от цикла for, если это возможно, была результатом слишком долгого изучения этого документа и мысли о том, что массовое обновление с помощью graph.graph['test_attribute'] = <new_value> может быть каким-то образом возможно. Я обнаружил, что слишком сильно полагаюсь на циклы for, и надеялся, что в таких случаях будет чем заменить его!

4. К сожалению, приведенное выше предложение не сработало, поскольку graph.nodes обрабатывается как список целых чисел, представляющих индексы узлов, например [0, 1, 2, ...] , поэтому я оставил использование range() в цикле как есть. Я не смог найти менее трудоемкого в вычислительном отношении решения (не то, что цикл for), и что касается чистоты, то лучшее, что я мог придумать, это переместить цикл ‘reset’ в отдельный метод и вызвать его как таковой. вместо этого. В любом случае спасибо.

Ответ №1:

Если вы хотите установить для атрибута для всех узлов одинаковое значение, вы можете использовать graph.add_nodes_from(graph.nodes,attribute_name='attribute_value')

Это обновляет предоставленный атрибут (добавляет узел и атрибут, если они не существуют), но сохраняет любые другие атрибуты узла и ребра, которые у вас уже есть на графике. Смотрите пример ниже:

 import networkx as nx

graph = nx.erdos_renyi_graph(n = 3, p = 0.1, seed = 5)

# setup some initial attributes
graph.nodes[0]['attr1'] = 'foo'
graph.nodes[1]['attr1'] = 'bar'
graph.nodes[2]['attr1'] = 'baz'

# make some attribute we want to change
for node in graph.nodes:
    graph.nodes[node]['attr_to_change'] = 'orig_value'

# View initial attribute values
print(graph.nodes(data=True))
# [(0, {'attr1': 'foo', 'attr_to_change': 'orig_value'}),
#  (1, {'attr1': 'bar', 'attr_to_change': 'orig_value'}),
#  (2, {'attr1': 'baz', 'attr_to_change': 'orig_value'})]

# change the value of the attr_to_change attribute for all
graph.add_nodes_from(graph.nodes,attr_to_change='new_value')

# View updated attribute value
print(graph.nodes(data=True))
# [(0, {'attr1': 'foo', 'attr_to_change': 'new_value'}),
#  (1, {'attr1': 'bar', 'attr_to_change': 'new_value'}),
#  (2, {'attr1': 'baz', 'attr_to_change': 'new_value'})]
 

Комментарии:

1. Спасибо за ответ. Документ не кажется ясным, но str переданный в **attr параметр имеет значение для узла как ссылка на определенное значение k / v dict, подключенного к набору узлов? Выполнение команды в вашем ответе, похоже, приводит к той же проблеме, что и мое первоначальное использование graph.graph['attribute_value'] = <value> , а именно к созданию нового параллельного графика, который, несмотря на правильное значение, не содержит ссылок на узлы, повторенные позже.

2. Меня смущает разница между graph.nodes[index] отсутствием обновления атрибута из graph.add_nodes_from(graph.nodes, attribute_name='attribute_value') though — это вообще не существенная проблема, поэтому я бы не стал слишком беспокоиться, скорее просто очень незначительное удобство!

3. **attr Параметр позволяет вам предоставить набор пар attribute_name, attribute_value для присвоения всем узлам, которые вы предоставляете nodes_for_adding параметру. В новом примере я меняю значение attr_to_change атрибута на "new_value" для всех узлов. При печати данных узла обратите внимание, что этот атрибут был обновлен, но другой атрибут остался прежним

4. Какое прекрасное объяснение. Это решает проблему точно так, как предполагалось. Одна строка, которая не только заменяет цикл for, но и заменяет метод, содержащий цикл for (для чистоты), и более ранняя попытка переназначения, не показанная в примере кода. Лучшие решения всегда самые простые, спасибо за помощь!