0

Given a directed networkx graph. I would like to have a function, say "abc()", called for every node that is deleted from the graph. I went through the networkx documentation but did not find any such callback feature.

What I considered:

  1. Adding a call to abc() to the __del__() (deconstructor) method of the object associated with a node. Yet, besides the pitfalls of using __del__() (see e.g. here) this does not work if any links to the node object exist somewhere in memory.
  2. Subclassing the networkx.DiGraph() class and overriding the remove_node() method. Drawback: This would require overriding all methods that remove a node, e.g. remove_nodes_from (are there any more?)
  3. As the networkx graph implementation is based on dictionaries, it could be a solution to somehow 'hook' the del function of this dictionary. Interfering this deep into networkx seems inappropriate though.

What is the easiest way to implement a callback function which is called everytime a networkx node is deleted?

Community
  • 1
  • 1
langlauf.io
  • 3,009
  • 2
  • 28
  • 45
  • 1
    You just need to adjust two methods to subclass: DiGraph.remove_node() and DiGraph_remove_nodes_from(). Take a look at https://github.com/networkx/networkx/blob/master/examples/subclass/printgraph.py . I think it does mostly what you want. – Aric Mar 30 '15 at 23:46
  • Ok, so you suggest the second of the above options. It makes sense as I was wrong to assume that removing edges may remove nodes. If there are only two two functions which delete nodes this is probably the best approach. I cannot currently accept your answer as it is a comment. Could you transfrom it into an answer? – langlauf.io Mar 31 '15 at 09:26

1 Answers1

1

In this case you only need to subclass two methods. Here is an example from networkx import DiGraph

class RemoveNodeDiGraph(DiGraph):
    def remove_node(self, n):
        DiGraph.remove_node(self, n)
        print("Remove node: %s"%n)

    def remove_nodes_from(self, nodes):
        for n in nodes:
            self.remove_node(n)

if __name__=='__main__':
    G = RemoveNodeDiGraph()
    G.add_node('foo')
    G.add_nodes_from('bar',weight=8)
    G.remove_node('b')
    G.remove_nodes_from('ar')

This won't be quite as fast as the original methods in the DiGraph class (especially remove_nodes_from()) but unless you have very large graphs it won't likely be significant. If you need better performance you could copy the code directly from those methods instead of calling the superclass.

Aric
  • 24,511
  • 5
  • 78
  • 77
  • One small comment: As I understand it, the `adj = self.adj` is not necessary, right? I mean it is not used in your code snippet and deleting edges from, or to, the target node is taken care of by `remove_node`. BTW: The same holds for `remove_nodes_from` in the sample subclass at https://github.com/networkx/networkx/blob/master/examples/subclass/printgraph.py Or am I missing something? – langlauf.io Mar 31 '15 at 16:06
  • 1
    Another hint for anybody who will also go for this solution: Note that the original `Digraph.remove_nodes_from()` "silently ignores if a node in the container is not in the graph". Using the (neat) solution from Aric will throw an exception if a node is not found as this is the regular behaviour for `remove_node()`. – langlauf.io Mar 31 '15 at 16:20