Quick answer: NetworkX builds and analyses graphs β nodes connected by edges β while Matplotlib draws them. Create a nx.Graph(), add nodes and edges (devices and links, people and friendships, pages and hyperlinks), pick a layout such as spring_layout, and call nx.draw_networkx() followed by plt.show(). In about ten lines you have a diagram, and with a few more you can colour nodes by role, size them by importance and highlight the shortest path between any two points.
This guide walks from the minimal example through a realistic network-topology visualisation, explains the layout algorithms and when to use each, shows how to load data from CSV and compute graph metrics, and lists the mistakes that make graph plots unreadable. The code targets Python 3.9+, NetworkX 3.x and Matplotlib 3.x.
Why visualise networks as graphs?
A spreadsheet of 200 links tells you nothing at a glance. The same data drawn as a graph instantly reveals hubs, isolated islands, redundant paths and single points of failure. Graph thinking applies far beyond computer networks: social connections, supply chains, citation networks, microservice dependencies, flight routes and recommendation systems are all graphs. Learning NetworkX gives you one mental model and one toolset for every one of them.
Installing the libraries
Both packages are pure Python and install in seconds with pip install networkx matplotlib.
If you work in Jupyter, plots render inline automatically. In a plain script, plt.show() opens a window and plt.savefig("graph.png", dpi=150) writes a file.
Hands-on 1: the minimal graph
Start with the core vocabulary β nodes, edges, layout, draw:
import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph() # undirected graph
G.add_nodes_from([1, 2, 3, 4])
G.add_edges_from([(1, 2), (1, 3), (1, 4), (3, 4)])
print("Nodes:", G.number_of_nodes(), "Edges:", G.number_of_edges())
print("Degree of node 1:", G.degree[1]) # how many links it has
pos = nx.spring_layout(G, seed=42) # seed makes the picture reproducible
nx.draw_networkx(G, pos, node_color="lightblue", node_size=900, font_weight="bold")
plt.axis("off")
plt.show()
Three things to note. add_edges_from creates any nodes that do not exist yet, so you often skip add_nodes_from. spring_layout runs a physics simulation; without a seed the picture changes every run. And nx.draw_networkx is the convenient all-in-one call; the individual draw_networkx_nodes, draw_networkx_edges and draw_networkx_labels functions give fine control when you need it.
Hands-on 2: a realistic network topology
Now a small enterprise network: two core switches, three distribution switches, access switches and a firewall. Nodes carry a role attribute, edges carry link speed in Gbps, and the plot encodes both.
import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()
devices = {
"fw1": "firewall", "core1": "core", "core2": "core",
"dist1": "distribution", "dist2": "distribution", "dist3": "distribution",
"acc1": "access", "acc2": "access", "acc3": "access", "acc4": "access",
}
for name, role in devices.items():
G.add_node(name, role=role)
links = [
("fw1", "core1", 10), ("fw1", "core2", 10), ("core1", "core2", 40),
("core1", "dist1", 10), ("core1", "dist2", 10), ("core2", "dist2", 10),
("core2", "dist3", 10), ("dist1", "acc1", 1), ("dist1", "acc2", 1),
("dist2", "acc3", 1), ("dist3", "acc4", 1),
]
G.add_weighted_edges_from(links, weight="speed")
colours = {"firewall": "#d9534f", "core": "#f0ad4e",
"distribution": "#5bc0de", "access": "#5cb85c"}
node_colours = [colours[G.nodes[n]["role"]] for n in G.nodes]
edge_widths = [G.edges[e]["speed"] / 5 for e in G.edges] # 1G thin, 40G thick
# Place devices in tiers using a shell layout: centre outwards
tiers = [["fw1"], ["core1", "core2"],
["dist1", "dist2", "dist3"], ["acc1", "acc2", "acc3", "acc4"]]
pos = nx.shell_layout(G, nlist=tiers)
plt.figure(figsize=(9, 7))
nx.draw_networkx_edges(G, pos, width=edge_widths, edge_color="grey")
nx.draw_networkx_nodes(G, pos, node_color=node_colours, node_size=1400, edgecolors="black")
nx.draw_networkx_labels(G, pos, font_size=9, font_weight="bold")
nx.draw_networkx_edge_labels(G, pos, edge_labels={e: f'{G.edges[e]["speed"]}G' for e in G.edges},
font_size=7)
for role, colour in colours.items():
plt.scatter([], [], c=colour, s=120, label=role, edgecolors="black")
plt.legend(loc="lower left", frameon=False)
plt.title("Campus network topology")
plt.axis("off")
plt.tight_layout()
plt.savefig("topology.png", dpi=150)
plt.show()
If you collect real inventory with SNMP or SSH, as shown in Network Monitoring with Python: Using PySNMP and Netmiko, you can feed the discovered neighbour table straight into G.add_edge() and generate this diagram automatically every night.
Hands-on 3: load from CSV, analyse, highlight a path
Most real data lives in files or databases. This example reads an edge list, computes centrality to find the most important nodes, and highlights the shortest path between two devices in red.
import csv
import networkx as nx
import matplotlib.pyplot as plt
# links.csv has a header: source,target,latency_ms
G = nx.Graph()
with open("links.csv", newline="") as fh:
for row in csv.DictReader(fh):
G.add_edge(row["source"], row["target"], weight=float(row["latency_ms"]))
# --- analysis ---
centrality = nx.betweenness_centrality(G, weight="weight")
top = sorted(centrality, key=centrality.get, reverse=True)[:3]
print("Most critical nodes (betweenness):", top)
print("Connected components:", nx.number_connected_components(G))
path = nx.shortest_path(G, "siteA", "siteF", weight="weight")
print("Lowest-latency path:", " -> ".join(path),
f'({nx.path_weight(G, path, "weight"):.1f} ms)')
# --- visualisation ---
pos = nx.kamada_kawai_layout(G)
sizes = [300 + 4000 * centrality[n] for n in G.nodes] # bigger = more central
path_edges = list(zip(path, path[1:]))
nx.draw_networkx_nodes(G, pos, node_size=sizes, node_color="lightsteelblue", edgecolors="black")
nx.draw_networkx_edges(G, pos, edge_color="lightgrey")
nx.draw_networkx_edges(G, pos, edgelist=path_edges, edge_color="red", width=3)
nx.draw_networkx_labels(G, pos, font_size=8)
plt.title("Lowest-latency path from siteA to siteF")
plt.axis("off")
plt.show()
Betweenness centrality measures how many shortest paths pass through a node; a high value flags a device whose failure would hurt the most. That one metric alone often justifies learning NetworkX.
Choosing a layout algorithm
| Layout | How it positions nodes | Use it when |
|---|---|---|
spring_layout |
Force-directed physics; connected nodes attract | General purpose, up to a few hundred nodes |
kamada_kawai_layout |
Minimises distance error; often cleaner than spring | Small to medium graphs where clarity matters |
shell_layout |
Concentric circles you define | Hierarchies: core/distribution/access |
circular_layout |
All nodes on one circle | Showing every connection evenly, ring topologies |
multipartite_layout |
Columns by a node attribute | Layered systems, bipartite data |
spectral_layout |
Eigenvectors of the graph Laplacian | Revealing clusters in larger graphs |
For true tree-like hierarchies, install Graphviz and use nx.nx_agraph.graphviz_layout(G, prog="dot"), which produces the cleanest top-down diagrams.
Seven visualisation mistakes to avoid
- Drawing hundreds of labelled nodes. It becomes a hairball. Filter to a subgraph, aggregate, or label only the top-k nodes by centrality.
- No
seedonspring_layout. Your diagram changes shape on every run and screenshots never match. - Forgetting
plt.axis("off"). Axis ticks are meaningless on a graph and clutter the image. - Using
Graphwhen direction matters. Traffic flows, dependencies and hyperlinks neednx.DiGraph; otherwise shortest paths and in/out degree are wrong. - Encoding data in colour alone. Combine colour with size or shape so the plot survives greyscale printing and colour-blindness.
- Calling
plt.show()beforesavefig(). Aftershow()the figure is cleared, so the saved file is blank. Save first. - Treating a drawing as analysis. Compute degree, centrality and components in code; the picture confirms, it does not prove.
Frequently asked questions
How large a graph can NetworkX handle?
Analysis works comfortably to hundreds of thousands of nodes; drawing with Matplotlib becomes unreadable past a few hundred. For big interactive graphs use PyVis, Plotly or Gephi, or export with nx.write_gexf().
Can I make the plot interactive?
Yes. pyvis converts a NetworkX graph into an HTML page with drag-and-zoom in two lines, and Plotly can render the node positions from any NetworkX layout.
What is the difference between nx.draw and nx.draw_networkx?
nx.draw is a bare-bones call with no labels and no axes. nx.draw_networkx adds labels by default and accepts every styling keyword. Prefer the latter.
Key takeaways
- NetworkX models the structure; Matplotlib renders it. Build the graph, choose a layout, draw, then style.
- Use node attributes for colour, edge weights for width, and centrality for size to make plots carry real information.
- Pick the layout for the shape of your data: shell for hierarchies, kamada-kawai for clarity, Graphviz
dotfor trees. - Always seed layouts, turn axes off, save before showing, and use
DiGraphwhen direction matters.
Visualisation is a core skill in every data role. The Techknowledgehub Data Science course covers Python, pandas, Matplotlib, graph analysis and machine learning through guided projects, with mentor support and placement assistance. For free step-by-step videos, subscribe to our YouTube channel.



