Recursively add Subgraphs to all Nodes in a Graph - python

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

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)

runtime error: dictionary changed size during iteration

I have a social network graph 'G'.
I'm trying to check that they keys of my graph are in the characteristics dataset as well.
I ran this command:
for node in G.nodes():
if node in caste1.keys():
pass
else:
G = G.remove_node(node)
It shows an error RuntimeError: dictionary changed size during iteration
The RuntimeError is self-explanatory. You're changing the size of an object you're iterating within the iteration. It messes up the looping.
What you can do is to first iterate across the nodes to get the ones you would like to remove, and store it in a separate variable. Then you can iterate with this variable to remove the nodes:
# Identify nodes for removal
nodes2remove = [node for node in G.nodes() if node not in caste1.keys()]
# Remove target-nodes
for node in nodes2remove:
G = G.remove_node(node)

How to rename a single node of a networkx graph?

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

How to write quotient_graph result to file

Here is a MWE to create the "issue"
>>> A = nx.complete_bipartite_graph(2, 3)
>>> same_neighbors = lambda u, v: (u not in A[v] and v not in A[u] and A[u] == A[v])
>>> B = nx.quotient_graph(A, same_neighbors)
>>> B.nodes()
NodeView((frozenset({0, 1}), frozenset({2, 3, 4})))
>>> B[frozenset({0, 1})]
AtlasView({frozenset({2, 3, 4}): {'weight': 6}})
>>> B.nodes[frozenset({0, 1})]
{'graph': <networkx.classes.graph.Graph object at 0x12b066e80>, 'nnodes': 2, 'nedges': 0, 'density': 0}
I believe this graph attribute on the node is specifying the subgraph which this node comes from in the original graph, but I'm not sure. If someone could verify that would be nice.
Either way though, this graph attribute prevents me from using the nx.write_graphml function because subgraphs cannot be used as data formats. In particular it raises
networkx.exception.NetworkXError: GraphML writer does not support <class 'networkx.classes.graph.Graph'> as data values.
Now I don't actually need the subgraph in the graphml file, so just dropping that data is probably the best way for me to get the graph written to a file. What is the best way to do this?
I believe this graph attribute on the node is specifying the subgraph which this node comes from in the original graph, but I'm not sure. If someone could verify that would be nice.
Yes, you are right in assuming that the graph attribute is actually specifying the subgraph based on the original graph. It is mentioned in the documentation for quotient graph . Check out the description of node_data, you will find that:
node_data (function) – This function takes one argument, B, a set of
nodes in G, and must return a dictionary representing the node data
attributes to set on the node representing B in the quotient graph. If
None, the following node attributes will be set:
- graph, the subgraph of the graph G that this block represents,
- nnodes, the number of nodes in this block,
- nedges, the number of edges within this block,
- density, the density of the subgraph of G that this block represents.
You can also check out the subgraph yourself, by typing
for node in B.nodes():
print("Information for subgraph ", node)
print(B.nodes[node]['graph'].nodes())
# Output:
# Information for subgraph frozenset({0, 1})
# Nodes are [0, 1]
# Information for subgraph frozenset({2, 3, 4})
# Nodes are [2, 3, 4]
Now I don't actually need the subgraph in the graphml file, so just
dropping that data is probably the best way for me to get the graph
written to a file. What is the best way to do this?
You can simply create a new graph and add only the nodes and edges, and discard any other data. (Again, you are right in assuming that this information is irrelevant for writing data in GraphML format as you only need the nodes and edges).
Let's check the EdgeView of the Quotient graph, before going further:
B.edges(data=True)
# EdgeDataView([(frozenset({0, 1}), frozenset({2, 3, 4}), {'weight': 6})])
Notice, how the data for the edge is dictionary with weight as key. This info. will be useful in the subsequent code.
Now, create a new graph, and add the edges directly from the quotient graph.
H = nx.Graph()
# Here B is the quotient graph
for u,v,d in B.edges(data=True):
# Notice how the weight is assigned to the new graph
H.add_edge(u, v, weight=d['weight'])
If you want to verify that the new graph has the same structure, you can check it out using nx.is_isomorphic
nx.is_isomorphic(H, B)
# True
Now you can simply write your graph into GraphML format
nx.readwrite.graphml.write_graphml(H, "my_graph.graphml")
For more information, you can check out this Google Colab Notebook, with the above working code.
References:
NetworkX - Quotient Graph
write_graphml
NetworkX - is_isomorphic

How could I generate a graph from a list?

Could I generate a graph from the following data, where the node "root" is the the node with label "V_ini" and it has edges with the different items of a given list "List":
V_ini=(0,0,0)
List=[(a,b,c),(1,0,1),(1,2,2)]
The expected result is a graph like that:
(0,0,0)->(a,b,c)
(0,0,0)->(1,0,1)
(0,0,0)->(1,2,2)

Categories

Resources