How to rename a single node of a networkx graph? - python

I wanted to know how I can change a single node name of a node of a digraph. I am new to networkx and could only find answers on how to change all node names.
In my case I am iterating over a graph A to create graph B. p and c are nodes of graph A. The edge (p,c) of graph A contains data I want to add to the node p of B. However, when I am adding the edge data from graph A to the already existing node p of graph B, I would like to update the name of p to be equal to the name of c so I am able to reference it again for the next edge of graph A because it then is the edge (c,x) and I can use the c to reference it again...
The relevant part of my code looks like this
new_stages = A.in_edge(c, data='stages')
stages = B.node[p]['stages']
stages.append(new_stages)
<<Update node p to have name of c??>>
B.add_node(p, stages=new_stage_set)
Any help is appreciated, thanks!

You have nx.relabel_nodes for this. Here's a simple use case:
G = nx.from_edgelist([('a','b'), ('f','g')])
mapping = {'b':'c'}
G = nx.relabel_nodes(G, mapping)
G.edges()
# EdgeView([('a', 'c'), ('f', 'g')])

Related

Remove all nodes that has a particular attribute in networkx

I`m trying to remove all nodes within a Graph that has an specific attribute value.
I have seen this: python networkx remove nodes and edges with some condition
But in that case the degree is a property and not an attribute.
My graph has an attribute call "Line" which could have different values like: A, B, C.
So for example I want to remove all nodes with attribute "Line" equal to A
You can filter nodes in your graph with a subgraph view:
filter_node = lambda node: G.nodes[node]['Line'] == 'A'
filtered_nodes = list(nx.subgraph_view(G, filter_node=filter_node).nodes)
You can then remove these nodes from the graph:
G.remove_nodes_from(e)

Get filtered Networkx MultiDiGraph to behave like a DiGraph

I have a MultiDiGraph with all my data in it, now I want to do some math on a filtered view of it that has only single directed edges between nodes.
>>> filtered_view[0][1]
Out[23]: AtlasView(FilterAtlas({0: {'d': 0.038, 'l': 2, 'showfl': True, 'type': 'pipe', 'q': 0.0001}}, <function FilterMultiInner.__getitem__.<locals>.new_node_ok at 0x7fa0987b55a0>))
I already have a lot of code that was working on a DiGraph, so a lot of it would not work anymore because of the differences in accessing and storing information. So thus my question:
Is there a way to have the view behave like a DiGraph?
Alternatively, I can do: ndg = nx.DiGraph(filtered_view)to get a DiGraph, but is there a smart (simple, clear, error free) way of merging it back into the main graph?
This is the implementation I came up with, it allows to either merge only data on existing nodes and edges (allnodes=False) or to merge the entire results_graph which is a DiGraph (allnodes=True). Condition is that the MultiDiGraph has not changed since the filtered view was created.
def merge_results_back(results_graph, multidigraph, allnodes=False):
for n in results_graph.nodes:
if n not in multidigraph.nodes and allnodes:
multidigraph.add_node(n)
if n in multidigraph.nodes:
nx.set_node_attributes(multidigraph, {n : results_graph.nodes[n]})
for e in results_graph.edges:
if e in multidigraph.edges:
for ed1, ed2, key, data in multidigraph.edges(e[0], keys=True, data=True):
if data['type'] == results_graph.edges[e]['type']:
nx.set_edge_attributes(multidigraph, {(e[0], e[1], key) : results_graph.edges[e]})
else:
nx.set_edge_attributes(multidigraph, {(e[0], e[1], 0): results_graph.edges[e]})
Offering a couple of suggestions for improvement here based on the code that you posted. It's unclear under what circumstances a node would be added (if the DiGraph is based on the MultiDiGraph, how is a new node possible?), so I'll leave that part alone.
In the loop for modifying edges, you end up looping through multidigraph every time a common edge is found. As an improvement, I'd suggest the following (assuming the type attribute differs based on the edge index, which wasn't clear in your question):
for u, v, data in results_graph.edges(data = True):
#only loops through each edge in the multidigraph one time
for i in range(multidigraph.number_of_edges(u, v)):
if multidigraph.edges[u, v, i]['type'] == data['type']:
multidigraph.edges[u, v, i].update(data)
If the type doesn't change based on the index, just eliminate that if statement line.
I think you can also get rid of the else block:
else:
nx.set_edge_attributes(multidigraph, {(e[0], e[1], 0): results_graph.edges[e]})
If edge e from results_graph isn't in multidigraph, then setting the edge attributes won't create edge e and it will be silently ignored. If you have a new edge and attributes though (again, unclear how this is possible if results_graph was created from multidigraph), you can add the following directly under the for u, v, data... line:
if (u, v) not in multidigraph.edges:
multidigraph.add_edge(u, v, **data)

Why are all my edges being assigned the same value in a networkx DiGraph?

I have been stuck on this simple problem for awhile and cant quite figure out the solution. I have a dictionary that is structured like {(node1, node2): weight} called EdgeDictFull. I wanted to create a DiGraph that has the weight stored as an attribute in the graph. I have tried a whole bunch of different ideas but no seem to work. When I run this code....
(weights is just a list of all the weights I want to add to the edges as attributes)
TG = nx.DiGraph()
for x in weights:
TG.add_edges_from(EdgeDictFull.keys(), weight = x)
TG.edges(data = True)
What this does is it will create all the correct edges, but all edges will have the attribute value of the last integer in my weights list. I think I understand why it does that, however, I cant seem to figure out how to fix it. I know it's something really simple. Any advice would be great!
# the problem with your code is that in every iteration of your loop you add
# *all* edges, and all of them get the same weight.
# you can do either of the following:
# zip:
TG = nx.DiGraph()
for edge, weight in zip(EdgeDictFull.keys(), weights):
TG.add_edge(*edge, weight=weight)
# or directly work with the dictionary:
## dummy dictionary:
EdgeDictFull = {(np.random.randint(5),np.random.randint(5)):np.random.rand() for i in range(3)}
TG = nx.DiGraph()
TG.add_weighted_edges_from((a,b,c) for (a,b), c in EdgeDictFull.items())
TG.edges(data = True)

Recursively add Subgraphs to all Nodes in a Graph

So, I've a data structure of graph as follows:
graph = {'graphData': {'nodes': List of Nodes,
'edges': List of Edges,}
}
In addition to that, I've a list of subgraphs as follows:
In first step, I add those subgraphs having one node as SOI [3 under Annotation 3] together to form something like this:
The following code take care of it:
for coreferanceGraph in unattached_graphs:
coreferanceGraph = self.merge_entity_in_graph(soi_node, coreferanceGraph, "value-entityName")
if coreferanceGraph:
edges_in_coreferenceGraph = coreferanceGraph['graphData']['edges']
nodes_in_coreferenceGraph = coreferanceGraph['graphData']['nodes']
for node in nodes_in_coreferenceGraph:
if node not in graph['graphData']['nodes']:
graph['graphData']['nodes'].append(node)
for edge in edges_in_coreferenceGraph:
if edge not in graph['graphData']['edges']:
graph['graphData']['edges'].append(edge)
attached_graphs.append(coreferanceGraph)
else:
continue
What happens in above code is that:
For every unattached subgraph in 1st image, I find if any subgraph has SOI node using function merge_entity_in_graph(). If SOI is present in subgraph, the subgraph is returned, and added to original Graph variables of structure defined defined by variables graph = dict().
In next step, I want to recursively add all those sugraphs in Image 1, to be added to open nodes present. In my case, according to Image 2, thats Ec, Ep, E1.
What I expect is:
Had there been more subgraphs with one of their nodes as Ef, Eq, Er, they also would have been added recursively.
My question is, how do I achieve this? I've written a method as follows, but not sure weather approach is right or not:
def add_other_subgraphs(self, subgraph, node, all_nodes, graph, coreference_graphs):
"""
Args:
subgraph (dict):
node (dict):
all_nodes (list):
graph (dict):
Returns:
"""
subgraph_edges = subgraph['graphData']['edges']
subgraph_nodes = subgraph['graphData']['nodes']
for node in subgraph_nodes:
if node not in graph['graphData']['nodes']:
graph['graphData']['nodes'].append(node)
for edge in subgraph_edges:
if edge not in graph['graphData']['edges']:
graph['graphData']['edges'].append(edge)
for node in graph['graphData']['nodes']:
coreference_graph = self.fetch_coreference_graph(node, coreference_graphs)
if coreference_graph:
graph = self.add_other_subgraphs(coreference_graph, node, all_nodes, graph, coreference_graphs)
else:
return graph
return graph
And the function add_other_subgraphs() will be called for each of Nodes i.e. Ec, Ep, E1

How to define multiple attributes for an edge in multi Digraph

I have a graph with four nodes with two directional edges betweeen each node (a to b and b to a), so I use a multi Digraph. The graph already has the 'weight' attribute defined by default which for my case represent the capacity of each edge for traffic flow. I need to define two more attributes for each edge, let's say tcv1 and tcv2.
Being a beginner with Python and networkx I am not able to figure this out. Some google search took me here but I could not use it correctly.
add_attribute_to_edge(router_matrix, tmp_path[0][0], tmp_path[0][1], 'tcv1', traffic_cv[0])
I used code above where router_matrix is graph, tmp_path[x][x] will represent a node name like 'a' or 'b', tcv1 is the attribute and traffic_cv[0] in code will be an integer calculated. Printing tcv1 only gives {}.
Can someone suggest a solution or point out where I go wrong.
You could use the add_edge function to add new attributes to existing edges in a MultiDiGraph, but you need to watch out for the key keyword (its value needs to be 0).
In my example I add the tcv1 attribute the first "a" -> "b" edge (I use your variable names and my example graph created with add_edges_from):
import networkx as nx
router_matrix = nx.MultiDiGraph()
# add two weighted edges ("a" -> "b" and "b" -> "a")
router_matrix.add_edges_from([
("a", "b", {"weight": 0.5}),
("b", "a", {"weight": 0.99})
])
# print list of edges with all data
print(router_matrix.edges(data=True))
tmp_path = [["a", "b"], ["b", "a"]]
traffic_cv = [42, 66]
# add "tcv1" for only the first edge of tmp_path
router_matrix.add_edge(tmp_path[0][0], tmp_path[0][1], key=0, tcv1=traffic_cv[0])
print(router_matrix.edges(data=True))
To add tcv1 to all edges, you could use router_matrix.edges() to iterate trough all edges (note that here I use the G[src_node][dest_node][key][attribute] syntax instead of add_edge):
# add new attribute to all edges
counter = 0
for src, dest in router_matrix.edges():
router_matrix[src][dest][0]['tcv1'] = traffic_cv[counter]
counter += 1
print(router_matrix.edges(data=True))

Categories

Resources