| Title: | Clustering Large Datasets by Merging K-Means Solutions |
|---|---|
| Description: | Fast clustering of large datasets by hierarchically merging components of a K-means solution based on the pairwise overlap between the Gaussian mixture components implied by the K-means partition, as proposed by Melnykov and Michael (2020) <doi:10.1007/s00357-019-09314-8>. Implements the DEMP-K merging algorithm with single, Ward's, average, and complete linkages, the overlap map display for selecting the number of clusters, four K-means variants corresponding to Gaussian mixtures with spherical or elliptical, homoscedastic or heteroscedastic components, and a tool for selecting the number of K-means components. |
| Authors: | Aqi Dong [aut, cre] (ORCID: <https://orcid.org/0009-0004-1676-3528>), Volodymyr Melnykov [aut], Yang-Li Liao [aut], Peng Li [aut], Xuejian Li [aut] |
| Maintainer: | Aqi Dong <[email protected]> |
| License: | GPL (>= 2) |
| Version: | 0.2.0 |
| Built: | 2026-07-19 15:41:20 UTC |
| Source: | https://github.com/cran/MergeKmeans |
Computes the proportion of the total variability explained by the
between-cluster variability (SSB/SSTO) of K-means solutions over a grid
of K values, plots it, and reports a recommended K for
MergeKmeans().
chooseK( x, K = seq(5, 50, by = 5), nstart = 10, iter.max = 100, plot = TRUE, cores = 1, ... )chooseK( x, K = seq(5, 50, by = 5), nstart = 10, iter.max = 100, plot = TRUE, cores = 1, ... )
x |
numeric data matrix or data frame with observations in rows. |
K |
integer vector of candidate numbers of components. |
nstart |
number of K-means restarts per candidate. |
iter.max |
maximum number of K-means iterations. |
plot |
logical; plot SSB/SSTO against |
cores |
number of CPU cores used to evaluate the grid in parallel (POSIX systems only). |
... |
further arguments passed to |
Following Melnykov and Michael (2020), a suitable number of components
is found at or somewhat beyond the elbow of the curve — the point at
which the increase in the explained proportion becomes marginal.
Over-estimating K is safe (the merging procedure is robust to it),
but K must exceed the number of clusters sought: setting K equal to
the anticipated cluster count defeats the merging step. The reported
recommendation is therefore one grid step beyond the detected elbow.
Invisibly, a data frame with columns K and ssb.ssto, with
the recommended K in attr(, "recommended").
Melnykov, V. and Michael, S. (2020) Clustering large datasets by merging K-means solutions. Journal of Classification, 37, 97-123. doi:10.1007/s00357-019-09314-8
set.seed(5) x <- rbind(matrix(rnorm(400), ncol = 2), matrix(rnorm(400, mean = 5), ncol = 2)) out <- chooseK(x, K = 2:12, nstart = 5) attr(out, "recommended")set.seed(5) x <- rbind(matrix(rnorm(400), ncol = 2), matrix(rnorm(400, mean = 5), ncol = 2)) out <- chooseK(x, K = 2:12, nstart = 5) attr(out, "recommended")
Fits the DEMP-K clustering procedure of Melnykov and Michael (2020). A
K-means solution with K components (chosen larger than the anticipated
number of clusters) is interpreted as a Gaussian mixture fitted by the
classification EM algorithm; the pairwise overlap between all component
pairs is computed in closed form; and components are merged by
hierarchical clustering with 1 - overlap as the dissimilarity. The
merge tree is cut at a requested number of clusters G, at an overlap
threshold omega.star, or automatically (G = "auto").
MergeKmeans( x, K = NULL, G = NULL, omega.star = NULL, variant = c("HoSC", "HoEC", "HeSC", "HeEC"), linkage = NULL, nstart = 10, iter.max = 100, cem.iter.max = 50, init = c("kmeans++", "random"), start = NULL, cores = 1, keep.data = TRUE, ... )MergeKmeans( x, K = NULL, G = NULL, omega.star = NULL, variant = c("HoSC", "HoEC", "HeSC", "HeEC"), linkage = NULL, nstart = 10, iter.max = 100, cem.iter.max = 50, init = c("kmeans++", "random"), start = NULL, cores = 1, keep.data = TRUE, ... )
x |
numeric data matrix or data frame with observations in rows. |
K |
number of K-means components; choose it comfortably larger
than the number of clusters |
G |
requested number of clusters after merging, between 1 and |
omega.star |
overlap threshold in (0, 1): merging stops when no
pair of merged groups attains this overlap. Supply at most one of |
variant |
K-means variant (case-insensitive; aliases in Details). |
linkage |
linkage for the hierarchical merging, passed to
|
nstart |
number of K-means restarts. |
iter.max |
maximum number of K-means iterations. |
cem.iter.max |
maximum number of classification EM iterations for the non-spherical variants. |
init |
initialization of the K-means restarts: |
start |
optional precomputed partition to be merged instead of
running K-means: an object of class |
cores |
number of CPU cores for the K-means restarts (forked via
parallel; POSIX systems only, ignored on Windows). With
|
keep.data |
logical; store the data in the returned object (needed
only for |
... |
further arguments passed to |
Four variants of K-means are supported, each corresponding to a Gaussian mixture with specific restrictions (Melnykov and Michael, 2020, Section 3 and Appendix A):
"HoSC" (default; alias "spherical"): homoscedastic spherical
components - the traditional K-means algorithm based on Euclidean
distances. The fastest variant and the one recommended for large
datasets.
"HoEC" (alias "elliptical"): homoscedastic elliptical components -
K-means with Mahalanobis distances and a common covariance matrix.
"HeSC" (alias "spherical-unequal"): heteroscedastic spherical
components - per-component spherical variances.
"HeEC" (alias "elliptical-unequal"): heteroscedastic elliptical
components - unrestricted per-component covariance matrices (requires
the CompQuadForm package for the overlap computation).
Variant names are matched case-insensitively. The non-spherical variants are initialized from the K-means partition and refined by classification EM iterations.
The choice of linkage should be driven by the cluster characteristics
sought: single linkage is appropriate for detecting well-separated
clusters of arbitrary shape, while Ward's linkage ("ward.D", the
default for p >= 2) targets compact clusters. Two situations where
Ward's linkage is unreliable and single linkage should be preferred:
univariate data (the components form a chain in which only adjacent
pairs overlap, so the default there is "single"), and severely
unequal cluster sizes (the homoscedastic overlap does not weight
components by mass, and Ward merges can then cross a genuine gap).
When clusters are genuinely overlapping Gaussian ellipsoids,
classical model-based clustering (e.g. mclust) remains the
better tool; DEMP-K is at its best for non-Gaussian or arbitrarily
shaped clusters and for sample sizes where model-based clustering is
impractical.
Robustness limits worth knowing (established by simulation): single
linkage can chain two clusters together through even small amounts of
uniform background contamination – the breakdown level depends on the
geometry, with failures observed between 1\
Ward's linkage was stable to at least 10\
heavy-tailed clusters (e.g. multivariate t with 1-2 degrees of
freedom, or strong exponential tails) create bridge points that defeat
overlap-based merging - "skewed" clusters are handled well, but
"heavy-tailed" ones are not. The heteroscedastic variants ("HeSC",
"HeEC") inherit unstable fixed points from their implied mixing
proportions and may drop components or stop at the last stable
iteration (with a warning) for larger p or K; "HoSC" is the
robust default.
Like K-means itself, the component solution depends on the scaling of
the variables; consider standardizing (e.g. scale(x)) when variables
are measured on very different scales. The merging step is fairly
robust to scaling provided K is large enough.
For very large datasets the K-means step is the only material cost (the
merging operates on the K x K overlap matrix). Two escape hatches are
provided: cores forks the restarts on POSIX systems, and start
accepts a partition computed by any external engine - for example
ClusterR::MiniBatchKmeans + predict, biganalytics::bigkmeans(), or
a previous stats::kmeans() run - so that MergeKmeans() only performs
the merging. Component means are recomputed from the data, so only the
labels of the supplied partition are used. The default Hartigan-Wong
algorithm may emit "Quick-TRANSfer" warnings on some starts for very
large n; results are unaffected, but algorithm = "MacQueen" can be
passed through ... to silence them.
If neither G nor omega.star is supplied, the fit is returned uncut
and a message reports the number of clusters suggested by the largest
gap in the merge heights: inspect the overlap map with
plot(fit, what = "overlap") and then call recut(). G = "auto"
accepts that suggestion directly. The threshold omega.star is
interpretable as a minimum pairwise overlap for merging only with
single, average, or complete linkage, and is therefore not accepted
with Ward's linkage.
An object of class "MergeKmeans": a list with components
clusterinteger vector of final cluster labels (NULL if
the tree has not been cut).
Gnumber of clusters after merging.
componentsinteger vector of K-means component labels.
component.mapinteger vector mapping each component to its merged cluster.
Keffective number of components.
centersK x p matrix of component means.
covestimated covariance parameter(s); form depends on the variant.
taumixing proportions implied by the variant.
overlapK x K matrix of pairwise overlaps.
treethe merge tree, an stats::hclust object.
variant, linkage
the settings used.
kmeansthe underlying stats::kmeans() fit (NULL when
start was supplied).
cemiteration details for non-spherical variants, or NULL.
datathe data matrix if keep.data = TRUE, else NULL.
Melnykov, V. and Michael, S. (2020) Clustering large datasets by merging K-means solutions. Journal of Classification, 37, 97-123. doi:10.1007/s00357-019-09314-8
Arthur, D. and Vassilvitskii, S. (2007) k-means++: the advantages of careful seeding. Proceedings of SODA 2007, 1027-1035.
recut(), chooseK(), overlap_map(), pairwise_overlap(),
predict.MergeKmeans()
## two well-separated half-circular clusters set.seed(7) th <- runif(500, 0, pi) x <- rbind( cbind(cos(th[1:250]), sin(th[1:250])) + matrix(rnorm(500, 0, 0.1), 250), cbind(1 - cos(th[251:500]), 0.4 - sin(th[251:500])) + matrix(rnorm(500, 0, 0.1), 250)) fit <- MergeKmeans(x, K = 10, G = 2, linkage = "single", nstart = 20) table(fit$cluster, rep(1:2, each = 250)) plot(fit, what = "overlap") ## automatic G, and merging a partition computed elsewhere fit2 <- MergeKmeans(x, K = 10, G = "auto", linkage = "single") km <- kmeans(x, 10, nstart = 20) fit3 <- MergeKmeans(x, start = km, G = 2, linkage = "single")## two well-separated half-circular clusters set.seed(7) th <- runif(500, 0, pi) x <- rbind( cbind(cos(th[1:250]), sin(th[1:250])) + matrix(rnorm(500, 0, 0.1), 250), cbind(1 - cos(th[251:500]), 0.4 - sin(th[251:500])) + matrix(rnorm(500, 0, 0.1), 250)) fit <- MergeKmeans(x, K = 10, G = 2, linkage = "single", nstart = 20) table(fit$cluster, rep(1:2, each = 250)) plot(fit, what = "overlap") ## automatic G, and merging a partition computed elsewhere fit2 <- MergeKmeans(x, K = 10, G = "auto", linkage = "single") km <- kmeans(x, 10, nstart = 20) fit3 <- MergeKmeans(x, start = km, G = 2, linkage = "single")
Methods for the broom generics, registered when broom is
loaded: tidy() returns one row per K-means component (its merged
cluster, size, and center coordinates), glance() returns a one-row
model summary, and augment() appends .component and .cluster
columns to the data.
## S3 method for class 'MergeKmeans' tidy(x, ...) ## S3 method for class 'MergeKmeans' glance(x, ...) ## S3 method for class 'MergeKmeans' augment(x, data = NULL, ...)## S3 method for class 'MergeKmeans' tidy(x, ...) ## S3 method for class 'MergeKmeans' glance(x, ...) ## S3 method for class 'MergeKmeans' augment(x, data = NULL, ...)
x |
a |
... |
ignored. |
data |
the original data, for |
A data frame (a tibble when available): per-component rows for
tidy(), a single row for glance(), and data plus .component
and .cluster columns for augment().
set.seed(6) x <- rbind(matrix(rnorm(200), ncol = 2), matrix(rnorm(200, mean = 6), ncol = 2)) fit <- MergeKmeans(x, K = 6, G = 2, linkage = "single", nstart = 5) if (requireNamespace("broom", quietly = TRUE)) { broom::tidy(fit) broom::glance(fit) head(broom::augment(fit, x)) }set.seed(6) x <- rbind(matrix(rnorm(200), ncol = 2), matrix(rnorm(200, mean = 6), ncol = 2)) fit <- MergeKmeans(x, K = 6, G = 2, linkage = "single", nstart = 5) if (requireNamespace("broom", quietly = TRUE)) { broom::tidy(fit) broom::glance(fit) head(broom::augment(fit, x)) }
Draws the overlap map of Melnykov and Michael (2020, Section 3.3): a lower-triangular heat map of all pairwise component overlaps, with components arranged so that strongly overlapping components are adjacent. Cells range from pale yellow (low overlap) to dark red (high overlap). The detached bottom row summarizes each column by its darkest hue: groups of adjacent dark cells indicate components that model a common cluster, while pale cells mark gaps between well-separated clusters, making the map an intuitive device for choosing the number of clusters.
overlap_map( omega, order = TRUE, zlim = NULL, col = NULL, legend = TRUE, main = "Overlap map", cex.axis = 0.8 )overlap_map( omega, order = TRUE, zlim = NULL, col = NULL, legend = TRUE, main = "Overlap map", cex.axis = 0.8 )
omega |
a symmetric matrix of pairwise overlaps, typically the
|
order |
|
zlim |
numeric range mapped onto the color scale; defaults to the range of the off-diagonal overlaps. |
col |
vector of colors for the scale; defaults to a yellow-to-red ramp. |
legend |
logical; draw the color legend. |
main |
plot title. |
cex.axis |
expansion factor for the component labels. |
Components are ordered as in the paper: the first two are the pair with the highest overlap, and each subsequent component is the one with the highest overlap with any component already displayed.
Invisibly, the integer ordering of the components used in the display.
Melnykov, V. and Michael, S. (2020) Clustering large datasets by merging K-means solutions. Journal of Classification, 37, 97-123. doi:10.1007/s00357-019-09314-8
MergeKmeans(), plot.MergeKmeans()
set.seed(2) x <- rbind(matrix(rnorm(300, 0, 1), ncol = 2), matrix(rnorm(300, 7, 1), ncol = 2)) fit <- MergeKmeans(x, K = 8, linkage = "single") overlap_map(fit$overlap)set.seed(2) x <- rbind(matrix(rnorm(300, 0, 1), ncol = 2), matrix(rnorm(300, 7, 1), ncol = 2)) fit <- MergeKmeans(x, K = 8, linkage = "single") overlap_map(fit$overlap)
Computes the matrix of pairwise overlaps between Gaussian mixture
components, where the overlap between components and is
defined as the sum of the two misclassification probabilities
(Maitra and Melnykov,
2010). The covariance structure is inferred from the form of cov:
pairwise_overlap(centers, cov, tau = NULL, lim = 50000, acc = 1e-06)pairwise_overlap(centers, cov, tau = NULL, lim = 50000, acc = 1e-06)
centers |
numeric matrix of component means with one row per
component ( |
cov |
covariance specification; see Details. |
tau |
optional vector of |
lim, acc
|
accuracy settings passed to |
a single number: homoscedastic spherical components
("HoSC"),
a p x p matrix: homoscedastic elliptical components
("HoEC"),
a vector of length K: heteroscedastic spherical components
("HeSC"),
a p x p x K array: heteroscedastic elliptical components ("HeEC").
For the homoscedastic cases the mixing proportions tau may be supplied
(equal proportions by default). For the heteroscedastic cases the mixing
proportions implied by the corresponding K-means variant are used:
for "HeSC" and
for "HeEC", under which the
log-ratio term in the misclassification probability vanishes (Melnykov
and Michael, 2020, Appendix A). The "HeEC" case requires the
CompQuadForm package, which evaluates the distribution of linear
combinations of non-central chi-squared variables (Davies, 1980).
A symmetric K x K matrix of pairwise overlaps with zero
diagonal.
Melnykov, V. and Michael, S. (2020) Clustering large datasets by merging K-means solutions. Journal of Classification, 37, 97-123. doi:10.1007/s00357-019-09314-8
Maitra, R. and Melnykov, V. (2010) Simulating data to study performance of finite mixture modeling and clustering algorithms. Journal of Computational and Graphical Statistics, 19(2), 354-376.
Davies, R. B. (1980) The distribution of a linear combination of chi-squared random variables. Applied Statistics, 29, 323-333.
centers <- rbind(c(0, 0), c(3, 0), c(10, 10)) pairwise_overlap(centers, cov = 1) # spherical, sigma^2 = 1 pairwise_overlap(centers, cov = c(1, 4, 1)) # heteroscedastic sphericalcenters <- rbind(c(0, 0), c(3, 0), c(10, 10)) pairwise_overlap(centers, cov = 1) # spherical, sigma^2 = 1 pairwise_overlap(centers, cov = c(1, 4, 1)) # heteroscedastic spherical
Plot method for MergeKmeans fits
## S3 method for class 'MergeKmeans' plot(x, what = c("overlap", "tree", "classification"), data = NULL, ...)## S3 method for class 'MergeKmeans' plot(x, what = c("overlap", "tree", "classification"), data = NULL, ...)
x |
a |
what |
type of display: |
data |
data matrix for |
... |
further arguments passed to |
For what = "overlap", invisibly the component ordering;
otherwise NULL, invisibly.
set.seed(3) x <- rbind(matrix(rnorm(300), ncol = 2), matrix(rnorm(300, mean = 6), ncol = 2)) fit <- MergeKmeans(x, K = 8, G = 2, linkage = "single") plot(fit, what = "overlap") plot(fit, what = "tree") plot(fit, what = "classification")set.seed(3) x <- rbind(matrix(rnorm(300), ncol = 2), matrix(rnorm(300, mean = 6), ncol = 2)) fit <- MergeKmeans(x, K = 8, G = 2, linkage = "single") plot(fit, what = "overlap") plot(fit, what = "tree") plot(fit, what = "classification")
Assigns new observations to K-means components by the decision rule of the fitted variant (nearest center in the appropriate metric) and then maps components to merged clusters.
## S3 method for class 'MergeKmeans' predict(object, newdata, ...)## S3 method for class 'MergeKmeans' predict(object, newdata, ...)
object |
a |
newdata |
numeric matrix or data frame of new observations with the same variables as the training data. |
... |
ignored. |
On the training data, the predicted components reproduce the fitted
components exactly for K-means-based fits and for converged
classification EM fits. The round-trip is not guaranteed when a
user-supplied start partition was not nearest-center consistent or
when the CEM iterations did not converge.
A list with entries component (assigned K-means components)
and cluster (merged cluster labels, or NULL if the merge tree has
not been cut).
set.seed(4) x <- rbind(matrix(rnorm(300), ncol = 2), matrix(rnorm(300, mean = 6), ncol = 2)) fit <- MergeKmeans(x, K = 8, G = 2, linkage = "single") predict(fit, rbind(c(0, 0), c(6, 6)))set.seed(4) x <- rbind(matrix(rnorm(300), ncol = 2), matrix(rnorm(300, mean = 6), ncol = 2)) fit <- MergeKmeans(x, K = 8, G = 2, linkage = "single") predict(fit, rbind(c(0, 0), c(6, 6)))
Produces a new partition from a fitted MergeKmeans() object by cutting
its merge tree at a different number of clusters or overlap threshold,
without re-running K-means or the overlap computation.
recut(object, G = NULL, omega.star = NULL)recut(object, G = NULL, omega.star = NULL)
object |
a |
G |
requested number of clusters, or |
omega.star |
overlap threshold in (0, 1); meaningful with single,
average, or complete linkage, where the merge heights are
|
The input object with updated cluster, G, and
component.map entries.
set.seed(1) x <- rbind(matrix(rnorm(200), ncol = 2), matrix(rnorm(200, mean = 6), ncol = 2)) fit <- MergeKmeans(x, K = 8, G = 2, linkage = "single") recut(fit, G = "auto")set.seed(1) x <- rbind(matrix(rnorm(200), ncol = 2), matrix(rnorm(200, mean = 6), ncol = 2)) fit <- MergeKmeans(x, K = 8, G = 2, linkage = "single") recut(fit, G = "auto")
Summary method for MergeKmeans fits
## S3 method for class 'MergeKmeans' summary(object, ...)## S3 method for class 'MergeKmeans' summary(object, ...)
object |
a |
... |
ignored. |
object, invisibly.