Network Diagram
chartAlso known as: force-directed graph, node-link diagram, network graph, graph visualization
Description
A network diagram (often implemented as a force-directed graph) represents relational data using nodes (points or circles) for entities and links (lines or curves) for relationships between them. The spatial layout is typically computed by a physics simulation: nodes repel each other like charged particles, while links act as springs pulling connected nodes together. The simulation reaches an equilibrium where densely connected clusters naturally group together and sparsely connected nodes drift to the periphery.
The resulting layout reveals the topology of the network: communities appear as clusters, bridge nodes sit between clusters, hub nodes accumulate many connections near the center, and isolated nodes float at the edges. Node attributes like size, color, and label encode additional data dimensions (e.g., degree centrality, category, importance), while link attributes like thickness, color, and style can encode relationship strength, type, or directionality.
Force-directed layouts work well for networks of moderate size (tens to low thousands of nodes). Beyond that scale, the visual density becomes overwhelming and the simulation becomes computationally expensive. For very large networks, alternative strategies like hierarchical aggregation, adjacency matrices, or filtered subgraph views become necessary. Interactivity — zooming, panning, hovering to highlight neighborhoods, and clicking to expand or filter — is essential for making network diagrams useful beyond simple illustration.
When to Use
- Showing the structure of social networks, citation networks, or organizational relationships
- Identifying clusters, communities, and central nodes in relational data
- Exploring how entities are connected and which connections are strongest
- Visualizing dependency graphs (software packages, supply chains, knowledge graphs)
- Communicating the overall topology of a network (hub-and-spoke, small-world, etc.)
When NOT to Use
- When the network is very large (>5,000 nodes) — the hairball problem makes force-directed layouts unreadable; use an adjacency matrix or hierarchical aggregation
- When the relationship is better described as flow — use a Sankey diagram or chord diagram
- When there is a clear hierarchy — use a tree diagram or sunburst
- When precise relationship values matter more than topology — use a heatmap or table
- When the data is not inherently relational — don’t force non-network data into a graph layout
Anatomy
- Nodes: Circles (or other shapes) representing entities; size often encodes degree or another metric
- Links (edges): Lines connecting nodes, representing relationships; may be directed (arrows) or undirected
- Node labels: Text identifying each entity, shown directly or on hover
- Node color: Encodes categorical grouping (community, type) or a quantitative attribute
- Link weight/thickness: Encodes relationship strength or frequency
- Force simulation: The algorithm computing positions; parameters include charge strength, link distance, and collision radius
Variations
- Force-directed layout: The standard physics-based approach; good general-purpose layout for small-to-medium networks
- Hierarchical / layered layout (Sugiyama): Nodes are arranged in layers for directed acyclic graphs, emphasizing flow direction
- Circular layout: Nodes are placed on a circle with links crossing the interior; good for showing community structure
- Arc diagram: Nodes on a line with semicircular arcs for links; compact but limited to smaller networks
- Adjacency matrix: An alternative representation where a grid cell at (i,j) is filled if nodes i and j are connected; scales better than node-link for dense networks
- Ego network: A focused view showing one node and its immediate neighbors, filtering out the rest
Code Reference
// D3 force-directed graph
import * as d3 from "d3";
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(50))
.force("charge", d3.forceManyBody().strength(-100))
.force("center", d3.forceCenter(width / 2, height / 2));
const svg = d3.select("#chart").append("svg").attr("viewBox", [0, 0, width, height]);
const link = svg.append("g").selectAll("line").data(links).join("line")
.attr("stroke", "#999").attr("stroke-opacity", 0.6)
.attr("stroke-width", d => Math.sqrt(d.value));
const node = svg.append("g").selectAll("circle").data(nodes).join("circle")
.attr("r", d => Math.sqrt(d.degree) * 3)
.attr("fill", d => d3.schemeTableau10[d.group])
.call(d3.drag()
.on("start", (e, d) => { if (!e.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; })
.on("drag", (e, d) => { d.fx = e.x; d.fy = e.y; })
.on("end", (e, d) => { if (!e.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; }));
simulation.on("tick", () => {
link.attr("x1", d => d.source.x).attr("y1", d => d.source.y)
.attr("x2", d => d.target.x).attr("y2", d => d.target.y);
node.attr("cx", d => d.x).attr("cy", d => d.y);
});