Hierarchical Clustering in R: Dendrograms, Agglomerative & Divisive

Build a tree of nested clusters with hclust(), agnes() and diana() — compare linkage methods with the agglomerative coefficient, read the dendrogram, and cut it into groups

Machine Learning

Hierarchical clustering in R, step by step. Compute the distance matrix, build the cluster tree with hclust() and agnes(), compare linkage methods (complete, average, single, Ward) with the agglomerative coefficient, draw a publication-ready dendrogram with fviz_dend(), cut it into groups with cutree(), and check the fit with the cophenetic correlation. Includes divisive clustering (DIANA) and when to use each.

Published

June 24, 2026

Modified

July 7, 2026

TipKey takeaways
  • Hierarchical clustering builds a tree of nested clusters (a dendrogram) — and, unlike k-means, you don’t pre-specify the number of clusters: you read it off the tree afterwards.
  • Agglomerative (bottom-up, AGNES) starts with each observation as its own cluster and merges the closest pair at each step; divisive (top-down, DIANA) starts with one big cluster and splits it.
  • The result depends on two choices: the distance between observations (dist()) and the linkage rule for the distance between clusters — complete, average, single, or Ward’s (ward.D2).
  • Use agnes()’s agglomerative coefficient to compare linkage methods objectively — higher is a stronger clustering structure (Ward usually wins).
  • Draw the tree with fviz_dend(), cut it into groups with cutree(), and check the fit with the cophenetic correlation (above 0.75 is good).
Get the book — Practical Guide to Cluster Analysis in R (PDF)

Introduction

Say you have crime statistics for the 50 US states and you want to find groups of states that behave alike — without deciding up front how many groups there are. Hierarchical clustering (also called hierarchical cluster analysis, or HCA) is built for exactly this. Instead of forcing the data into a fixed number of clusters the way k-means does, it produces a tree — a dendrogram — that shows how observations merge into clusters at every level of similarity. You then cut the tree wherever the grouping makes sense.

This is the method of choice when:

  • you don’t know how many clusters to expect (the tree shows you the structure at every scale),
  • you want a visual, interpretable map of how observations relate, or
  • your data is small to medium (the distance matrix grows fast — hierarchical clustering is not for millions of rows; use k-means or CLARA there).

Hierarchical clustering comes in two flavours:

  • Agglomerative (bottom-up, AGNESAgglomerative Nesting): each observation starts as its own cluster; at every step the two most similar clusters are merged, until one big cluster remains. Good at finding small clusters.
  • Divisive (top-down, DIANADIvisive ANAlysis): everything starts in one cluster, which is split again and again until every observation stands alone. Good at finding large clusters.

We focus on agglomerative clustering (the common case), then cover divisive at the end. The four steps are always the same: prepare the data → compute a distance matrix → link into a tree → cut the tree into groups.

The data

We use R’s built-in USArrests dataset — arrests per 100,000 residents for Murder, Assault and Rape, plus the percent UrbanPop, for each US state. The variables are on very different scales, so we standardize them first with scale() (z-scores): otherwise Assault (hundreds) would dominate Murder (single digits) purely because of its units.

# Load the data
data("USArrests")

# Standardize: every variable to mean 0, sd 1 (so scales are comparable)
df <- scale(USArrests)

# First 6 rows of the standardized data
head(df, 6)
               Murder   Assault   UrbanPop         Rape
Alabama    1.24256408 0.7828393 -0.5209066 -0.003416473
Alaska     0.50786248 1.1068225 -1.2117642  2.484202941
Arizona    0.07163341 1.4788032  0.9989801  1.042878388
Arkansas   0.23234938 0.2308680 -1.0735927 -0.184916602
California 0.27826823 1.2628144  1.7589234  2.067820292
Colorado   0.02571456 0.3988593  0.8608085  1.864967207
Note

Standardizing before clustering is almost always the right call when variables are measured in different units. The distance measures lesson covers when (and how) to standardize in detail.

Step 1 — the distance matrix

Every clustering method needs a rule for how far apart two observations are. The base function dist() computes that for every pair of rows. By default it uses the Euclidean distance; you can switch with method = ("manhattan", "maximum", …) — see the distance measures lesson.

df <- scale(USArrests)

# Dissimilarity matrix (Euclidean distance between every pair of states)
res.dist <- dist(df, method = "euclidean")

# Peek at the first 6×6 corner of the matrix
as.matrix(res.dist)[1:6, 1:6]
            Alabama   Alaska  Arizona Arkansas California Colorado
Alabama    0.000000 2.703754 2.293520 1.289810   3.263110 2.651067
Alaska     2.703754 0.000000 2.700643 2.826039   3.012541 2.326519
Arizona    2.293520 2.700643 0.000000 2.717758   1.310484 1.365031
Arkansas   1.289810 2.826039 2.717758 0.000000   3.763641 2.831051
California 3.263110 3.012541 1.310484 3.763641   0.000000 1.287619
Colorado   2.651067 2.326519 1.365031 2.831051   1.287619 0.000000

Read the matrix like a mileage chart: the cell at row i, column j is the distance between state i and state j. The diagonal is zero (every state’s distance to itself), and the matrix is symmetric.

Step 2 — agglomerative clustering with hclust()

The base function hclust() takes the distance matrix and links observations into a tree. The key argument is method =, the linkage rule — how to measure the distance between two clusters (not just two points):

df <- scale(USArrests)
res.dist <- dist(df, method = "euclidean")

# Build the hierarchical tree using Ward's method
res.hc <- hclust(d = res.dist, method = "ward.D2")

Which linkage method?

Linkage is the one choice that most changes the shape of the tree. The common rules:

  • Complete (maximum) linkage — distance between clusters = the largest pairwise distance. Tends to produce compact, tight clusters.
  • Single (minimum) linkage — the smallest pairwise distance. Tends to produce long, straggly “chained” clusters (one outlier can stretch a cluster).
  • Average linkage — the average of all pairwise distances. A middle ground.
  • Centroid linkage — distance between the two cluster centroids (mean vectors).
  • Ward’s minimum variance (ward.D2) — at each step merges the pair that increases the total within-cluster variance the least. Produces balanced, roughly equal-sized, compact clusters.
Tip

Complete linkage and Ward’s method are the usual defaults — they give compact, interpretable clusters. Single linkage is rarely what you want for grouping (it chains), but it is good at spotting outliers. We use ward.D2 here. (Use ward.D2, not ward.D — see Common issues.)

Compare linkage methods objectively: the agglomerative coefficient

Instead of eyeballing trees, you can compare linkage methods with a single number. The agnes() function (in the cluster package) does agglomerative clustering and reports the agglomerative coefficient (AC) — a measure of the strength of the clustering structure, between 0 and 1. Values closer to 1 mean a stronger, clearer structure. agnes() also does the scale() + dist() + linkage in one call.

library(cluster)

# Try four linkage methods and grab each agglomerative coefficient
methods <- c(average = "average", single = "single",
             complete = "complete", ward = "ward")

ac <- sapply(methods, function(m) {
  agnes(USArrests, stand = TRUE, metric = "euclidean", method = m)$ac
})

round(ac, 3)
 average   single complete     ward 
   0.738    0.634    0.854    0.934 

Ward’s method gives the highest agglomerative coefficient — the strongest clustering structure — which is why we use it. This is a quick, defensible way to pick a linkage method rather than guessing.

Step 3 — the dendrogram

The tree is best read as a dendrogram. The base plot(res.hc) works, but fviz_dend() (in factoextra) draws a far cleaner, publication-ready one.

library(factoextra)

df <- scale(USArrests)
res.hc <- hclust(dist(df), method = "ward.D2")

fviz_dend(res.hc, cex = 0.5)   # cex = label size

Dendrogram of the standardized US Arrests data built with Ward's method. State names are the leaves along the bottom; branches join at increasing heights up the vertical axis, showing how states merge into larger clusters.

How to read it. Each leaf at the bottom is one state. Moving up, similar states join into branches, and branches fuse into bigger branches. The height at which two items join (the vertical axis) is their dissimilarity — the cophenetic distance. The lower two items fuse, the more similar they are.

Warning

Judge similarity by fusion height, not horizontal position. Two leaves that happen to sit next to each other along the bottom may only join at a high level — they are not necessarily similar. Only the height where their branches first merge tells you how close they are.

Step 4 — cut the tree into groups

A dendrogram by itself doesn’t tell you how many clusters there are. You decide, by cutting the tree at a height — every branch the cut crosses becomes a cluster. cutree() does this, either by a target number of groups (k =) or a cut height (h =). It returns the cluster number of each observation.

df <- scale(USArrests)
res.hc <- hclust(dist(df), method = "ward.D2")

# Cut the tree into 4 groups
grp <- cutree(res.hc, k = 4)

# Cluster label of the first 4 states
head(grp, 4)
 Alabama   Alaska  Arizona Arkansas 
       1        2        2        3 
# How many states in each cluster?
table(grp)
grp
 1  2  3  4 
 7 12 19 12 
# Which states are in cluster 1?
rownames(df)[grp == 1]
[1] "Alabama"        "Georgia"        "Louisiana"      "Mississippi"   
[5] "North Carolina" "South Carolina" "Tennessee"     

Draw the cut on the dendrogram — fviz_dend() colours the branches by group and boxes each cluster:

library(factoextra)

df <- scale(USArrests)
res.hc <- hclust(dist(df), method = "ward.D2")

fviz_dend(res.hc, k = 4,                                  # cut into 4 groups
          cex = 0.5,                                      # label size
          k_colors = c("#3a86d4", "#00AFBB", "#E7B800", "#FC4E07"),
          color_labels_by_k = TRUE,                       # colour labels by group
          rect = TRUE)                                    # box each cluster

The same US Arrests dendrogram cut into four colored clusters, with a rectangle drawn around each group of states to show the four-cluster partition.

You can also see the clusters as a scatter plot. fviz_cluster() projects the states onto their first two principal components and draws a frame around each group — a clearer view of separation than the tree:

library(factoextra)

df <- scale(USArrests)
res.hc <- hclust(dist(df), method = "ward.D2")
grp <- cutree(res.hc, k = 4)

fviz_cluster(list(data = df, cluster = grp),
             palette = c("#3a86d4", "#00AFBB", "#E7B800", "#FC4E07"),
             ellipse.type = "convex",   # frame around each cluster
             repel = TRUE,              # avoid label overlap
             show.clust.cent = FALSE,
             ggtheme = theme_minimal())

Scatter plot of US states on the first two principal components, with the four hierarchical-clustering groups shown as colored points inside convex frames, and state names labeled.

Verify the cluster tree: the cophenetic correlation

How faithfully does the tree reflect the original distances? Compute the cophenetic distances — the heights at which pairs merge in the tree — and correlate them with the original dist() distances. The closer to 1, the better the tree preserves the real distances; above 0.75 is considered good.

df <- scale(USArrests)
res.dist <- dist(df)
res.hc <- hclust(res.dist, method = "ward.D2")

# Cophenetic distances from the tree
res.coph <- cophenetic(res.hc)

# Correlation with the original distances
cor(res.dist, res.coph)
[1] 0.6975266

Ward’s tree preserves the distances reasonably. Average linkage often scores higher on this particular statistic (one reason it’s popular) — compare:

df <- scale(USArrests)
res.dist <- dist(df)

res.hc2 <- hclust(res.dist, method = "average")
cor(res.dist, cophenetic(res.hc2))
[1] 0.7180382

Average linkage gives a higher cophenetic correlation here — it represents the raw distances slightly more faithfully. That doesn’t make it the “best” choice outright: it trades off against the compact, balanced groups Ward gives (and Ward’s higher agglomerative coefficient). Pick the linkage that matches your goal, not just one number — see the decision cue below.

Divisive clustering (DIANA)

Divisive clustering — DIANA (DIvisive ANAlysis) — is agglomerative clustering run in reverse. It starts with all observations in one cluster and, at each step, splits off the most heterogeneous cluster, until every observation is on its own. Where agglomerative is bottom-up, divisive is top-down; where agglomerative is good at finding small clusters, divisive is good at finding large ones.

The diana() function (in cluster) computes it and, like agnes(), does scale() + dist() + the algorithm in one call. It returns a divisive coefficient (analogous to the agglomerative coefficient) measuring the strength of the structure.

library(cluster)
library(factoextra)

# Divisive clustering (standardizes internally)
res.diana <- diana(USArrests, stand = TRUE)

# Divisive coefficient: strength of the clustering structure
res.diana$dc
[1] 0.8530481
# Visualize the tree, cut into 4 groups
fviz_dend(res.diana, cex = 0.5,
          k = 4,
          palette = "jco")

Dendrogram of the US Arrests data built top-down with divisive clustering (DIANA), cut into four colored groups using a journal color palette.

You read, cut and verify a DIANA tree exactly like an agglomerative one (cutree(), fviz_dend(), cophenetic()). In practice agglomerative clustering is used far more often, but DIANA is a useful alternative when you expect a few large groups rather than many small ones.

Which linkage when

You want… Use
Compact, balanced, roughly equal-sized groups (a safe default) Ward (ward.D2)
Tight, compact clusters; sensitive to the farthest points Complete
A middle-ground; best cophenetic correlation Average
To detect outliers / chaining (rarely for grouping) Single
A few large groups, top-down Divisive (diana())

Confirm your choice with the agglomerative coefficient (agnes()$ac) and the cophenetic correlation (cor(dist, cophenetic(hc))) — don’t pick by eye alone.

Try it live

Cluster the USArrests states yourself. Change k to cut the tree into a different number of groups, or swap "ward.D2" for "complete" or "average" and watch the dendrogram change. Click Run.

🟢 With an AI agent

Ask Prova “run a hierarchical clustering on my data and tell me how many clusters to keep” — it answers with R code you can run on your own data frame, then helps you read the dendrogram, choose the cut, and check the cophenetic correlation. The runtime is the judge. Ask Prova →

Common issues

ward.D vs ward.D2 — which one? Use ward.D2. The original "ward.D" does not implement Ward’s criterion correctly unless your input is squared Euclidean distances; "ward.D2" squares the distances internally and gives the genuine Ward result. Reach for ward.D2 unless you have a specific reason not to.

I forgot to scale and one variable dominates. If your variables are on different scales (e.g. dollars vs. counts), the largest-magnitude variable will drive the distances and the clusters will essentially reflect that one column. Standardize first with scale() (or agnes(..., stand = TRUE)).

How do I know where to cut the tree? There’s no single rule, but look for a large vertical gap — cut just below a height where branches jump up a long way (big jumps mean very different clusters are being forced together). For a principled choice, use the optimal number of clusters methods (elbow, silhouette, gap statistic) and pass that k to cutree().

My single-linkage tree looks like one long chain. That’s single linkage’s known “chaining” behaviour — it links clusters by their single closest pair. Switch to complete or Ward for compact groups.

Frequently asked questions

k-means requires you to pick the number of clusters (k) up front and returns a flat partition into exactly that many groups; it scales to large data. Hierarchical clustering builds a whole tree (dendrogram) of nested clusters and lets you choose the number afterwards by cutting the tree — but the distance matrix makes it impractical for very large datasets. Use hierarchical clustering to explore structure and k-means to scale.

Ward’s method (ward.D2) and complete linkage are the usual defaults — they give compact, balanced clusters. Average linkage is a middle ground that often maximizes the cophenetic correlation. Single linkage chains and is mostly useful for spotting outliers. Compare them objectively with the agglomerative coefficient from agnes() (closer to 1 = stronger structure).

Use cutree() on the hclust() output: cutree(res.hc, k = 4) cuts into 4 groups, or cutree(res.hc, h = 5) cuts at height 5. It returns the cluster number of each observation. To draw the cut, pass k = and rect = TRUE to fviz_dend().

Agglomerative (AGNES) is bottom-up: each observation starts alone and the closest clusters merge step by step. Divisive (DIANA) is top-down: everything starts in one cluster that is split repeatedly. Agglomerative is better at finding small clusters and is by far the more common; divisive is better at finding a few large clusters.

Compute the cophenetic correlation: cor(res.dist, cophenetic(res.hc)). It compares the merge heights in the tree to the original pairwise distances — closer to 1 is better, and above 0.75 is considered good. The average linkage method often scores highest on this measure.

Test your understanding

Build a hierarchical clustering of the standardized USArrests data with complete linkage, cut it into 3 groups, and print how many states fall in each.

# method = "complete" inside hclust(), and k = 3 inside cutree()
df <- scale(USArrests) res.hc <- hclust(dist(df), method = "complete") grp <- cutree(res.hc, k = 3) table(grp)
df <- scale(USArrests)
res.hc <- hclust(dist(df), method = "complete")
grp <- cutree(res.hc, k = 3)
table(grp)
  1. Conceptual. In a dendrogram, two states sit right next to each other along the bottom axis but their branches only join near the very top. Are they similar? (Answer: No — similarity is read from the height at which branches fuse, not from horizontal position. A high fusion height means they are dissimilar.)

  2. Conceptual. You compare four linkage methods with agnes() and Ward has the highest agglomerative coefficient while average has the highest cophenetic correlation. Which do you pick? (Answer: There’s no single “right” one — Ward gives the strongest, most compact structure; average reproduces the raw distances most faithfully. Choose by your goal, and confirm the cut with the optimal number of clusters methods.)

Conclusion

Hierarchical clustering gives you a tree of nested groups instead of a fixed partition: compute a distance matrix with dist(), link it into a tree with hclust() (or agnes()), pick a linkage method — ward.D2 is a strong default, confirmed by the agglomerative coefficient — draw it with fviz_dend(), and cut it into groups with cutree(). Check the fit with the cophenetic correlation, and reach for divisive clustering (diana()) when you expect a few large groups. The number of clusters is a decision you make from the tree, ideally backed by the formal methods in the next lessons.

Reuse

Citation

BibTeX citation:
@online{2026,
  author = {},
  title = {Hierarchical {Clustering} in {R:} {Dendrograms,}
    {Agglomerative} \& {Divisive}},
  date = {2026-06-24},
  url = {https://www.datanovia.com/learn/machine-learning/clustering/hierarchical-clustering},
  langid = {en}
}
For attribution, please cite this work as:
“Hierarchical Clustering in R: Dendrograms, Agglomerative & Divisive.” 2026. June 24. https://www.datanovia.com/learn/machine-learning/clustering/hierarchical-clustering.