Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

R clustering is an unsupervised method for grouping observations by similarity. A reliable analysis starts before kmeans(): define what rows and columns represent, select an appropriate distance measure, clean and scale the data, compare plausible algorithms, test candidate cluster counts, and assess stability. The resulting groups are analytical partitions—not automatically “natural” or causally meaningful categories.

What cluster analysis does

Cluster analysis groups observations without a predefined outcome variable. It assigns observations according to a chosen representation, distance or similarity measure, algorithm, and set of parameters.

Cluster labels are arbitrary: cluster 1 is not inherently better than cluster 2. Results can change when you change the features, scaling, transformations, distance metric, algorithm, initialization, missing-value treatment, or outlier handling.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Hard clustering assigns each observation to one group. Soft clustering reports probabilities or degrees of membership. Partitioning methods directly seek a chosen number of groups; hierarchical methods produce nested groupings; and density-based methods search for dense regions while potentially labeling sparse observations as noise.

R includes kmeans(), dist(), hclust(), and cutree() in the stats package. The cluster package adds PAM, CLARA, Gower dissimilarities, and silhouette analysis.

Read the current R documentation for kmeans(), hclust(), dist(), and PAM for version-specific behavior.

Set up a reproducible R clustering workflow

Install the packages used by the examples:

install.packages(c("cluster", "factoextra", "dbscan", "mclust"))

Record your R and package versions when publishing results. Defaults and package behavior can change between versions. Use a seed whenever an algorithm uses random initialization.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
set.seed(42)

Inspect and prepare the data

Suppose df contains one observation per row and candidate clustering features in columns. Audit the data first:

str(df)
summary(df)
colSums(is.na(df))
sapply(df, function(x) sum(!is.finite(x)))

Remove identifier columns unless the identifier encodes meaningful information. Do not convert factors to integer codes merely to make them numeric. Coding small, medium, and large as 1, 2, and 3 imposes a distance and ordering that may not be justified.

Dates, text, counts, proportions, binary variables, and categorical variables need different representations. Most basic distance and clustering functions do not solve missing-data problems automatically. Complete-case analysis is simple but may be biased when missingness is systematic; imputation should preserve the structure relevant to clustering and be sensitivity-tested.

Investigate extreme values rather than deleting them automatically. An outlier can pull a k-means centroid, but it may also be the observation most important to your application. Strongly right-skewed positive features may benefit from a transformation such as log1p().

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Build a numeric feature matrix

x <- df[, c("feature_1", "feature_2", "feature_3")]
x <- x[complete.cases(x), , drop = FALSE]
x_scaled <- scale(x)

scale() centers columns and, by default, divides them by their standard deviations. This prevents a variable measured in large units from dominating Euclidean distance.

Scaling is not automatically correct. If all variables share a meaningful unit, or absolute magnitude is the question of interest, standardization may remove important information. The choice of scaling is part of the analytical question. See the R documentation for scale().

Choose a distance measure

Distance defines what “similar” means. Euclidean distance is common for numeric data and is sensitive to scale and large coordinate differences. Manhattan distance sums absolute differences and can be useful when individual large deviations should have less influence.

d_euclidean <- dist(x_scaled, method = "euclidean")
d_manhattan <- dist(x_scaled, method = "manhattan")

Correlation distance can emphasize the shape of a profile rather than its absolute level, but it needs careful interpretation. Binary data may require binary or Jaccard-type measures.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For mixed numeric, categorical, ordinal, and binary data, use Gower dissimilarity through cluster::daisy():

library(cluster)
d_gower <- daisy(df_mixed, metric = "gower")

Ordinary k-means operates on a numeric matrix and is not a direct solution for arbitrary mixed-type data. For mixed data, consider Gower distance with PAM or hierarchical clustering, or a method designed for mixed variables. The dist() documentation describes the available distance methods.

K-means clustering in R

K-means is a useful baseline when numeric observations are reasonably compact and similarly shaped. It partitions observations around arithmetic means by minimizing within-cluster squared Euclidean variation. It is fast and easy to explain, but it requires a chosen number of clusters and is sensitive to scaling, outliers, initialization, and non-spherical structure.

set.seed(42)

km <- kmeans(
  x_scaled,
  centers = 3,
  nstart = 25,
  iter.max = 100
)

km$cluster
km$centers
km$size
km$withinss
km$tot.withinss
km$betweenss

centers = 3 requests three groups. nstart runs the algorithm from multiple random starting configurations; using more starts reduces the chance of accepting a poor local solution. set.seed() makes the random result reproducible.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Because this example uses x_scaled, km$centers are standardized centers. They are useful for comparing feature profiles, but report summaries on the original scale for readers who need practical interpretation.

A lower within-cluster sum of squares does not by itself prove that the solution is better. It generally decreases as more clusters are requested. K-means is a poor match for elongated, nested, crescent-shaped, unequal-density, or heavily contaminated groups.

The algorithm argument supports methods including Hartigan–Wong, Lloyd, and MacQueen. Check the documentation for the R version used.

Choose the number of clusters

There is rarely a single objectively “true” number of clusters. Use multiple diagnostics, then consider stability, interpretability, sample size, and the decision the groups must support.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Elbow method

wss <- sapply(1:10, function(k) {
  kmeans(x_scaled, centers = k, nstart = 25)$tot.withinss
})

plot(
  1:10, wss,
  type = "b",
  xlab = "Number of clusters",
  ylab = "Total within-cluster sum of squares"
)

Look for a bend where additional clusters produce diminishing improvement. The elbow is often ambiguous and is a heuristic, not proof.

Silhouette width

library(cluster)

sil <- silhouette(km$cluster, dist(x_scaled))
plot(sil)
mean(sil[, "sil_width"])

Silhouette width compares an observation’s cohesion with its assigned cluster against its separation from the nearest alternative. A higher average is generally better geometrically, but it does not establish domain usefulness or external validity. See the silhouette documentation.

Gap statistic and factoextra

library(factoextra)

fviz_nbclust(x_scaled, kmeans, method = "wss", k.max = 10)
fviz_nbclust(x_scaled, kmeans, method = "silhouette", k.max = 10)

set.seed(42)
gap <- clusGap(
  x_scaled,
  FUN = kmeans,
  K.max = 10,
  B = 50,
  nstart = 25
)

fviz_gap_stat(gap)

WSS, silhouette, and gap statistics optimize different notions of structure, so disagreement is normal. Do not choose the most convenient result without reporting the disagreement. The fviz_nbclust() documentation covers these workflows.

Hierarchical clustering

Hierarchical clustering creates a tree of nested groupings. It is useful when you want to inspect several resolutions rather than commit immediately to one value of k.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
d <- dist(x_scaled, method = "euclidean")
hc <- hclust(d, method = "ward.D2")

plot(hc, labels = FALSE, hang = -1)

groups <- cutree(hc, k = 3)
table(groups)

Linkage matters. Single linkage can produce chaining; complete linkage tends to favor compact groups; average linkage is a compromise. Ward-style methods favor compact partitions and should be used with compatible distance assumptions. The dendrogram reflects the algorithm and distance measure; it is not automatically an evolutionary or causal tree.

library(factoextra)

fviz_dend(
  hc,
  k = 3,
  rect = TRUE,
  show_labels = FALSE
)

You can cut by a chosen number of groups or by dendrogram height, depending on what is meaningful for the application. See hclust() and factoextra::hcut().

PAM and k-medoids

Partitioning around medoids, or PAM, represents each cluster with an actual observation rather than an arithmetic mean. This makes representatives easier to inspect and allows distance objects such as Gower dissimilarities.

library(cluster)

pam_fit <- pam(
  x_scaled,
  k = 3,
  metric = "euclidean"
)

pam_fit$clustering
pam_fit$medoids
pam_fit$silinfo$avg.width

PAM is often less affected by outliers than k-means because medoids are observed records, but it remains sensitive to feature selection, scaling, distance, and data composition. CLARA provides a sampling-based medoid approach for larger datasets, although sampling may miss small or rare groups. See the PAM documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

DBSCAN and HDBSCAN

Density-based methods are appropriate when groups may have irregular shapes, when noise matters, or when specifying k is undesirable. DBSCAN uses a neighborhood radius, eps, and a minimum number of neighbors, minPts.

library(dbscan)

db <- dbscan(
  x_scaled,
  eps = 0.8,
  minPts = 5
)

table(db$cluster)
plot(db)

kNNdistplot(x_scaled, k = 5)
abline(h = 0.8, lty = 2)

In common dbscan output, a noise label identifies observations that do not belong to a discovered density-connected group. Confirm the exact output convention for the installed version.

eps is scale-dependent. A single global density threshold can fail when groups have different densities, and high-dimensional neighborhoods may become uninformative. Poor settings can classify most observations as noise. HDBSCAN can represent varying density more flexibly, but its membership and cluster-selection parameters still require interpretation. The dbscan package documentation covers DBSCAN, HDBSCAN, OPTICS, and related tools.

Gaussian mixture models

Gaussian mixture models treat observations as arising from a mixture of probability distributions. Unlike hard k-means labels, they can provide membership probabilities and uncertainty.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
library(mclust)

mc <- Mclust(x_scaled)

summary(mc)
mc$classification
mc$uncertainty
plot(mc, what = "BIC")
plot(mc, what = "classification")

mclust compares covariance structures and component counts using model-based criteria such as BIC. Mixtures can represent overlapping or differently shaped groups more flexibly than spherical k-means, but they require stronger distributional assumptions and may have convergence or covariance problems. A mixture component is a statistical model component, not automatically a naturally occurring population. See the mclust documentation.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Visualize and profile the clusters

Plot both the input data and the fitted result. A simple two-feature view is useful but incomplete:

plot(
  x_scaled[, 1],
  x_scaled[, 2],
  col = km$cluster,
  pch = 19,
  xlab = "Feature 1",
  ylab = "Feature 2"
)

PCA provides a compact visualization for many numeric features:

library(factoextra)

fviz_cluster(
  km,
  data = x_scaled,
  geom = "point",
  ellipse.type = "convex"
)

A PCA plot is a projection. It can hide separation in other dimensions, and PCA maximizes variance rather than practical relevance. Ellipses and convex hulls are visual aids, not proof of validity. Do not reduce dimensions solely to manufacture visible clusters.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Profile groups on the original scale:

profile_original <- aggregate(
  x,
  by = list(cluster = km$cluster),
  FUN = mean
)

profile_original
table(km$cluster)

Useful reporting includes cluster sizes, original-scale means or medians, standardized profiles, categorical distributions, missingness patterns, representative observations or medoids, and uncertainty for probabilistic methods.

Test stability and sensitivity

One seed, one value of k, and one attractive plot are not enough evidence. Repeat k-means with different seeds and larger nstart. Resample observations and refit. Compare the resulting partitions with adjusted Rand index or another agreement measure.

Test sensitivity to:

  • Scaling and transformations.
  • Outlier treatment.
  • Feature inclusion and highly correlated variables.
  • Distance metric and linkage method.
  • Candidate values of k.
  • Rare or very small groups.

The fpc ecosystem provides interfaces for repeated clustering and stability workflows. “Robust” should be defined precisely: resistance to outliers, seed stability, bootstrap stability, or replication in new data are different claims.

A complete numeric baseline script

library(cluster)
library(factoextra)

# Select meaningful numeric features
features <- c("feature_1", "feature_2", "feature_3", "feature_4")
x <- df[, features, drop = FALSE]

# Simple complete finite-row baseline
keep <- complete.cases(x) &&
  apply(x, 1, function(row) all(is.finite(row)))
x <- x[keep, , drop = FALSE]

# Decide transformations before scaling
# x$feature_1 <- log1p(x$feature_1)

x_scaled <- scale(x)

set.seed(42)
fviz_nbclust(x_scaled, kmeans, method = "wss", k.max = 10)
fviz_nbclust(x_scaled, kmeans, method = "silhouette", k.max = 10)

set.seed(42)
km <- kmeans(
  x_scaled,
  centers = 3,
  nstart = 50,
  iter.max = 100
)

table(km$cluster)
km$centers

d <- dist(x_scaled)
sil <- silhouette(km$cluster, d)
mean(sil[, "sil_width"])
fviz_silhouette(sil)

hc <- hclust(d, method = "ward.D2")
hc_groups <- cutree(hc, k = 3)
fviz_dend(hc, k = 3, rect = TRUE, show_labels = FALSE)

profile_original <- aggregate(
  x,
  by = list(cluster = km$cluster),
  FUN = mean
)
profile_original

The keep expression is written for a simple numeric baseline. In production, handle missingness, imputation, and non-finite values explicitly rather than assuming complete cases are appropriate.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Which R clustering method should you use?

Method Use it when Strengths Weaknesses
K-means Numeric data and compact, similarly shaped groups are plausible Fast, simple, familiar Requires k; sensitive to scale and outliers
Hierarchical You want nested structure or a dendrogram Shows multiple resolutions Linkage-sensitive and potentially expensive
PAM Actual representative records or custom distances matter Interpretable medoids; often less affected by outliers Requires k; slower than k-means
CLARA You need medoid clustering on larger data More scalable than PAM Sampling can miss rare clusters
DBSCAN Irregular shapes and noise are expected Finds density-connected shapes; no preset k Sensitive to eps, density variation, and dimension
HDBSCAN Density varies across groups Represents a density hierarchy Membership and parameters still need interpretation
Gaussian mixtures Overlapping groups and probabilities matter Soft assignments and model selection Distributional assumptions and computational complexity

There is no universally best algorithm. Choose the method whose assumptions match the data representation and analytical objective, then compare plausible alternatives.

Common mistakes and edge cases

  • Mixed types: Do not feed factor codes into k-means as continuous measurements. Consider Gower distance with PAM or hierarchical clustering.
  • Missing data: Complete cases can bias results. Use and sensitivity-test an appropriate imputation strategy.
  • Outliers: Compare transformed, unfiltered, and robust alternatives; do not delete unusual observations automatically.
  • Correlated features: Near-duplicate columns can overweight one construct. Remove, combine, or justify a reduction strategy.
  • High dimensions: Distance concentration can make neighborhoods less informative. Use domain-informed feature selection or a justified representation.
  • Imbalanced sizes: K-means may split large groups and absorb small ones. Inspect sizes and compare methods.
  • Small samples: Tiny clusters may disappear under resampling. Avoid strong generalizations.
  • Leakage: If clusters later enter a predictive model, exclude post-outcome information and reproduce preprocessing at deployment.
  • New data: A partition may not remain valid when feature distributions drift. Define how new observations will be assigned and monitor the data.

Practical checklist

  1. Define exactly what an observation and feature represent.
  2. Remove identifiers and inspect missing, invalid, skewed, and extreme values.
  3. Decide whether scaling changes the question you want to answer.
  4. Choose a distance measure appropriate for the data types.
  5. Fit more than one plausible algorithm.
  6. Compare candidate cluster counts with multiple diagnostics.
  7. Test seeds, resamples, features, scaling, and outlier decisions.
  8. Profile groups on the original scale and report their sizes.
  9. Show uncertainty or borderline membership where available.
  10. Separate geometric separation from domain usefulness, causal claims, and external validation.

A clustering analysis can end with the conclusion that the data do not show clear or stable groups. That is a valid result. Internal metrics describe the geometry of a fitted partition; they do not prove business value, clinical usefulness, causality, or reproducibility in a new population.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.