Graphs as matrices

NetworkX · Advanced · 12 min

Matrix form connects graphs to linear algebra and machine learning, and is how fast numeric code processes them.

A graph with n nodes can be written as an n x n adjacency matrix A, where A[i, j] is the weight of edge i-j (or 1), and 0 otherwise.

  • nx.to_numpy_array(G, nodelist=order, weight="weight") gives a dense NumPy array. Fine for up to a few thousand nodes.
  • nx.to_scipy_sparse_array(G, nodelist=order) gives a SciPy sparse array, the right choice for large sparse graphs.
  • nx.from_numpy_array(A) and nx.from_scipy_sparse_array(S) go back; nodes are numbered 0 to n-1, so relabel afterwards if needed.

Pass nodelist= to control row order; otherwise it follows G.nodes insertion order, which is easy to get wrong when matching rows to nodes.

Matrix powers count walks: (A @ A)[i, j] is the number of 2-step walks from i to j. The Laplacian L = D - A (nx.laplacian_matrix(G)) underlies spectral methods. The number of zero eigenvalues of L equals the number of connected components, and the second-smallest eigenvalue, the algebraic connectivity (nx.algebraic_connectivity), measures how well connected the graph is. Its eigenvector (Fiedler vector) gives a classic way to split a graph in two.

nx.adjacency_spectrum, nx.laplacian_spectrum and nx.normalized_laplacian_matrix are available too. These need NumPy and SciPy installed.

Example

import networkx as nx
import numpy as np

G = nx.Graph([("a", "b"), ("b", "c"), ("c", "a"), ("d", "e")])
order = ["a", "b", "c", "d", "e"]

A = nx.to_numpy_array(G, nodelist=order)
print(A[0].tolist())                       # [0.0, 1.0, 1.0, 0.0, 0.0]
print(int((A @ A)[0, 0]))                  # 2  (a->b->a and a->c->a)

S = nx.to_scipy_sparse_array(G, nodelist=order)
print(type(S).__name__, S.nnz)             # csr_array 8

L = nx.laplacian_matrix(G, nodelist=order).toarray()
eig = np.linalg.eigvalsh(L)
print(int(np.sum(np.isclose(eig, 0))))     # 2  (= number of components)

H = nx.from_numpy_array(A)
print(sorted(H.edges)[:2])                 # [(0, 1), (0, 2)]  (integer labels)

Remember

  • Dense arrays for small graphs, SciPy sparse arrays for large ones
  • Always pass nodelist= to fix the row order
  • Laplacian spectra reveal components and connectivity

Watch out for

  • Building a dense adjacency matrix for a million-node graph and running out of memory.

Check yourself

  1. A graph's Laplacian has three eigenvalues equal to zero. What does that tell you?

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

Official docs: networkx.org/documentation/stable/reference/linalg.html