Dendrogram
chartAlso known as: tree diagram, cluster diagram, hierarchical tree, phylogenetic tree
Description
A dendrogram is a branching tree diagram that represents hierarchical relationships. Each leaf node represents an individual entity, and internal nodes represent clusters or groupings. The branching structure shows which entities are most closely related: entities that merge at lower levels (shorter branch lengths) are more similar than those that merge at higher levels. The height or length of each branch segment typically encodes the distance or dissimilarity at which two groups merge.
Dendrograms are foundational in hierarchical cluster analysis, where an algorithm (such as Ward’s method, complete linkage, or UPGMA) iteratively merges the most similar pairs of clusters until all entities belong to one tree. The dendrogram visualizes this entire merging history, allowing analysts to choose a “cutting height” that partitions the data into a desired number of clusters. Drawing a horizontal line across the dendrogram at any height reveals the clusters that exist at that level of granularity.
Beyond statistics, dendrograms appear in biology (phylogenetic trees showing evolutionary relationships), linguistics (language family trees), and information architecture (file system or organizational hierarchies). The chart works best with moderate numbers of leaf nodes (20-200); beyond that, labels become unreadable and the tree structure overwhelming without interactive zoom capabilities.
When to Use
- Displaying the results of hierarchical clustering to show which entities group together
- Showing taxonomic or evolutionary relationships (phylogenetics)
- Visualizing organizational hierarchies, file systems, or decision trees
- Enabling users to explore different levels of granularity by choosing a “cut” height
When NOT to Use
- When the hierarchy is very wide (hundreds of leaf nodes) without interactive zoom — the tree becomes unreadable; use a treemap for space-efficient display
- When you need to show quantitative values at leaf nodes — pair with a heatmap or bar chart alongside the dendrogram
- When the relationships are not truly hierarchical (e.g., a network with cycles) — use a network diagram
- When the audience is unfamiliar with tree diagrams — use a simpler grouped list or bar chart
Anatomy
- Leaf nodes: Terminal points representing individual entities, typically labeled
- Internal nodes: Branching points where two sub-trees merge, representing clusters
- Branches: Line segments connecting parent nodes to children, whose length encodes distance or dissimilarity
- Root: The topmost node where all entities merge into a single cluster
- Height axis: The scale (usually vertical or horizontal) measuring the distance/dissimilarity at each merge
- Cut line: An optional horizontal line at a chosen height to define cluster boundaries
- Color: Often applied to branches or leaves to highlight cluster membership
Variations
- Circular dendrogram (radial tree): Branches radiate outward from a central root, useful for large trees that wrap around a circle
- Polar dendrogram: Similar to circular but with branch lengths encoded radially
- Dendrogram + heatmap: A dendrogram on one or both axes of a heatmap, showing cluster structure alongside quantitative data (very common in genomics)
- Cladogram: Branch lengths are uniform (not encoding distance), focusing purely on topology
- Interactive dendrogram: Click to expand/collapse sub-trees, enabling exploration of large hierarchies
Code Reference
// D3 dendrogram using d3.cluster layout
import * as d3 from "d3";
const root = d3.hierarchy(data).sort((a, b) => a.height - b.height);
const cluster = d3.cluster().size([height, width - 160]);
cluster(root);
const svg = d3.select("#chart").append("svg")
.attr("viewBox", [0, 0, width, height]);
svg.selectAll("path")
.data(root.links()).join("path")
.attr("d", d3.linkHorizontal().x(d => d.y).y(d => d.x))
.attr("fill", "none").attr("stroke", "#999");
svg.selectAll("text")
.data(root.leaves()).join("text")
.attr("x", d => d.y + 6).attr("y", d => d.x)
.text(d => d.data.name).attr("font-size", 10);