Quick Reference · graph & network analysis in Python

networkx cheat sheet

A graph is just nodes (any hashable object) + edges (pairs of nodes), each carrying an optional attribute dict. Under the hood it's a dict-of-dicts: G.adj[u][v] is the attributes of edge u–v. Pick a graph class for your directed-/multi-ness, build it, inspect its structure, run an algorithm (paths, centrality, communities), then draw or export. Conventionally imported as nx.

setup / from-data nodes · edges · attrs inspect · views · ops paths · traversal centrality · community drawing · layouts · I/O gotcha most common

Distilled & cross-checked against: networkx.org (Reference · Tutorial, v3.6) · verified by running networkx 3.6.1 (current, re-verified 2026-08-28; Python 3.11+) · cheatography (gonz95alo · murenei) · community guides on Medium

The workflow & the data model behind every call
THE WORKFLOW 1 · choose class Graph / DiGraph / Multi… 2 · build add_edge · from_pandas 3 · inspect degree · neighbors · adj 4 · analyze paths · centrality · comm. 5 · draw / export nx.draw · read/write_* THE DATA MODEL — a graph is a dict of dicts w=4 A B C D G.adj ≡ G._adj (dict of dicts) {'A': {'B': {'weight': 4},       'C': {}},  'B': {'A': {'weight': 4}, 'C':{}, 'D':{}},  'C': {...}, 'D': {...}} G['A']['B'] → the edge's attribute dict FOUR CLASSES = directed? × parallel edges? undirected directed → simple multi Graph DiGraph MultiGraph MultiDiGraph Nodes can be any hashable object (int, str, tuple) — not lists/dicts. add_edge(u, v) silently creates u and v if missing. G.nodes, G.edges, G.degree are live views — iterate them, or wrap in list(). Pass data=True to get attributes too. Most algorithms return a dict keyed by node (centrality) or a generator (components, paths) — wrap generators in list() to materialise. weight is just an edge attribute named 'weight'; unweighted shortest-path = fewest edges (BFS), weighted = Dijkstra on that attribute. Directed graphs split the neighbourhood: successors / predecessors and in_degree / out_degree.
quickstart.py — build, analyze, draw a weighted graph
import networkx as nx
import matplotlib.pyplot as plt

G = nx.Graph()                                  # undirected; use nx.DiGraph() for directed
G.add_edges_from([("A","B"), ("B","C"), ("A","C"), ("C","D")])
G.add_edge("A", "B", weight=4)                 # attributes ride along as kwargs

print(G.number_of_nodes(), G.number_of_edges())    # 4 4
print(nx.shortest_path(G, "A", "D"))              # ['A', 'C', 'D']
print(nx.degree_centrality(G))                    # {'A':0.67, 'B':0.67, 'C':1.0, ...}

pos = nx.spring_layout(G, seed=42)               # compute node positions (fix seed!)
nx.draw(G, pos, with_labels=True, node_color="#c7d2fe")
plt.show()
01Setup & Graph Typesimport & pick a class
02Add Nodesany hashable object
03Add Edgespairs of nodes
04Attributesdata on nodes & edges
05Build From Dataimport existing structures
06Inspect Nodes & Edgesthe views
07Neighbors & Degreelocal structure
08Graph Generatorsinstant test graphs
09Shortest Pathsget from u to v
10Traversal & Orderingwalk the graph
11Connectivity & Componentsis it one piece?
12Centralitywho matters most
13Clustering & Structureglobal properties
14Communitiesfind clusters
15Subgraphs & Operatorsslice & combine
16Drawingwith matplotlib
17Read / Write & Convertpersist & interop
18Scale & Backendsbigger graphs, 2026

Four ideas worth a picture

The intuitions the API rests on — traversal order, weighted paths, what "important" means, and directed neighbourhoods. Values below are the real NetworkX outputs.

BFS vs DFS — order you visit nodes

Same tree, same root A. BFS sweeps level by level; DFS dives deep before backtracking.

BFS · by layer A B C D E F A │ B C │ D E F DFS · go deep A1 B2 D3 E4 C5 F6 A→B→D→E→C→F

Shortest path — hops vs weight

Unweighted picks the fewest edges; weighted (Dijkstra) picks the least total weight — often a different route.

1 8 1 1 1 S X T A B S-X-T2 hops · cost 9 S-A-B-T3 hops · cost 3 ✓

Centrality — "important" has many meanings

Node size ∝ score. Degree favours the hubs; betweenness exposes the bridge m — few links, but every crossing path runs through it.

degree h1 h2 m hubs win · m looks minor (0.25) betweenness h1 h2 m m jumps to 0.57 — the broker

Directed graphs split the neighbourhood

On a DiGraph, neighbors() = successors only. Use predecessors() for edges coming in.

A B C D E predecessors successors C.in_degree = 2 · C.out_degree = 2 · neighbors(C) → {D, E}

Worth memorizing

nodes = hashableint/str/tuple work; a list or dict as a node raises TypeError
add_edge auto-addsadd_edge(u,v) silently creates u and v if missing
re-adding updatesadding an existing node/edge merges attributes — no duplicate (except MultiGraph)
views, not listsG.nodes/G.edges/G.degree are live views — wrap in list()
data=Trueadd it to .nodes/.edges to get the attribute dicts
weight is an attrnamed 'weight' by default; pass weight='cost' to use another
unweighted = BFSshortest_path with no weight = fewest edges; add weight= for Dijkstra
generatorscomponents & paths return generators — list() them to reuse
directed ≠ undirectedon DiGraph neighbors=successors; use predecessors for in-edges
degree on DiGraphdegree = in+out; use in_degree/out_degree separately
check preconditionsmany algos need connected / DAG — test is_connected/is_directed_acyclic_graph first
subgraph is a viewread-only & ties to G; .copy() to detach and edit
fix the layout seedspring_layout(G, seed=…) or the plot jumps every run
removing cascadesremove_node also drops all its incident edges — silently