Search code examples
pythonnetworkx

removing isolated vertices in networkx


The documentation says that isolated vertices in graph can be obtained using networkx.isolates(*G*). It adds that the isolated vertices can be removed from a graph G using the code *G*.remove_nodes_from(nx.isolates(*G*)).

https://networkx.github.io/documentation/networkx-1.10/reference/generated/networkx.algorithms.isolate.isolates.html

screenshot of documentation(url above)

But I get the run time error "dictionary changed size during iteration" when I run the code.

Error report:-
>>> G.remove_nodes_from(nx.isolates(G)) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/iiitdm/anaconda2/lib/python2.7/site-packages/networkx/classes/graph.py", line 617, in remove_nodes_from for n in nodes: File "/home/iiitdm/anaconda2/lib/python2.7/site-packages/networkx/algorithms/isolate.py", line 94, in <genexpr> return (n for n, d in G.degree() if d == 0) File "/home/iiitdm/anaconda2/lib/python2.7/site-packages/networkx/classes/reportviews.py", line 443, in __iter__ for n in self._nodes: RuntimeError: dictionary changed size during iteration

It is understandable and was expected because (I think) the generator object created using the function isolates() changes with G and hence changing graph G while it is being 'iterated' should give a similar error. Then that line in the documentation must be wrong, isn't it? Am I completely off the mark? I am pretty new to python.

By the way, the object returned by networkx.isolates() is a generator object.


Solution

  • I think you are right, submit a documentation patch?

    also you can cast the generator to a list to get around this:

    G.remove_nodes_from(list(nx.isolates(G)))

    But why does your work-around work? I don't understand it; the situation has not changed!

    I would have to look at their code, but my hunch is the lazyness of a generator is working against it based on the Exception message.

    Casting to list, the collection is created before it is fed as an argument, so there is no side effects on the object as it is iterated.

    As noted from @Dyz's answer, the documentation is correct, you are using Nx 2.0.

    https://networkx.github.io/documentation/networkx-2.0/reference/algorithms/generated/networkx.algorithms.isolate.isolates.html

    Does this collection creation before fed as argument behaviour hold for any kind of cast (say to dict or set) ? –

    Well not quite (a set will work) dict won't because it wants a pair of items. list and set when called like a function (and dict but again it needs pairs (a list of tuples will work)) calls __iter__

    Generators have __iter__ which makes them iterables (+ a lot of other objects). Generators are really nice to have to address various usecases, for example when you have a large collection of items, and need to loop through them multiple times, it can save you on runtime complexity.

    However, there is nuances such as what you ran into, where you have to understand some of the internals for proper use.