Views, subgraphs and copies

NetworkX · Practical · 12 min

Views avoid copying big graphs, but misunderstanding them causes surprising bugs when the original changes.

G.nodes, G.edges, G.adj and G.degree are views: read-only, live windows onto the graph's internal dicts. They cost nothing to create and always reflect the current graph, so changing G after creating a view changes what the view shows.

Subgraph views work the same way:

  • G.subgraph(nodes): the induced subgraph on those nodes.
  • G.edge_subgraph(edges): only the given edges and their endpoints.
  • G.reverse(copy=False) for DiGraphs, and nx.subgraph_view(G, filter_node=f, filter_edge=g) for predicate-based filtering.

These views are frozen: you can't add or remove nodes through them, and they reflect later changes to G. Call .copy() to get an independent, editable graph, for example G.subgraph(nodes).copy(). Note that copy() is shallow for attribute values; a mutable value such as a list stored in an attribute is shared. Use copy.deepcopy if you will mutate those.

Don't remove nodes from G while iterating over G.nodes or a view; it raises RuntimeError: dictionary changed size during iteration. Collect them first: G.remove_nodes_from([n for n in G if cond(n)]).

nx.relabel_nodes(G, mapping) renames nodes, returning a copy by default; nx.freeze(G) makes a graph immutable.

Example

import networkx as nx

G = nx.path_graph(6)                      # 0-1-2-3-4-5
sub = G.subgraph([1, 2, 3])               # a view, no copy
print(sorted(sub.edges))                  # [(1, 2), (2, 3)]

G.remove_edge(2, 3)                       # change the original...
print(sorted(sub.edges))                  # [(1, 2)]  ...and the view follows

try:
    sub.add_edge(1, 3)
except nx.NetworkXError as e:
    print(e)                              # Frozen graph can't be modified

own = G.subgraph([1, 2, 3]).copy()        # independent and editable
own.add_edge(1, 3)
print(G.has_edge(1, 3))                   # False

G.remove_nodes_from([n for n in G if G.degree(n) == 0])   # safe bulk removal
big = nx.subgraph_view(G, filter_node=lambda n: n >= 3)
print(sorted(big.nodes))                  # [3, 4, 5]

Remember

  • G.nodes, G.edges, G.adj and subgraphs are live, read-only views
  • Call .copy() for an independent, editable subgraph
  • Don't mutate a graph while iterating over its views

Watch out for

  • Keeping a subgraph view, then editing the original graph and being surprised the 'snapshot' changed.

Check yourself

  1. sub = G.subgraph([1, 2]); then G.remove_node(2). What does sub contain?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: networkx.org/documentation/stable/reference/classes/index.html#module-networkx.classes.graphviews