The clustering methods of HistDAWass group the individuals (rows) of
a MatH object using the L2 Wasserstein distance between
histograms. The prototype of each cluster is itself a histogram for each
variable (the Wasserstein mean of the histograms of the cluster).
We use the OzoneFull data: 78 stations in the USA
described by the histograms of four variables.
get.MatH.main.info(OzoneFull)$varnames
#> [1] "Ozone.Conc.ppb" "Temperature.C" "Solar.Radiation.WattM2"
#> [4] "Wind.Speed.mSec"Two options are common to all the methods:
standardize = TRUE standardises each variable by its
Wasserstein standard deviation: useful when the variables have different
units, as here;simplify = TRUE approximates each histogram with
qua equally spaced quantiles: much faster for large
datasets, with an approximation error that decreases with
qua (see the vignette on basic statistics).The algorithms start from random partitions, so we fix the seed and
repeat each algorithm rep times, keeping the best
solution.
set.seed(1)
km <- WH_kmeans(OzoneFull, k = 3, rep = 5,
simplify = TRUE, qua = 20, standardize = TRUE)
names(km$solution)
#> [1] "IDX" "cardinality" "centers" "Crit"
km$solution$cardinality
#> IDX
#> Cl.1 Cl.2 Cl.3
#> 31 13 34
km$quality # proportion of the total sum of squares explained by the partition
#> [1] 0.4845037
head(km$solution$IDX)
#> I1 I2 I3 I4 I5 I6
#> 1 1 3 3 1 3The prototypes are a MatH object, so they can be
plotted:
The quality (between / total sum of squares) increases with
k; a common choice is where the gain begins to flatten
(elbow):
set.seed(1)
qual <- sapply(2:7, function(k)
WH_kmeans(OzoneFull, k = k, rep = 5, simplify = TRUE, qua = 20,
standardize = TRUE)$quality)round(qual, 3)
#> [1] 0.345 0.485 0.571 0.639 0.682 0.713
plot(2:7, qual, type = "b", xlab = "k", ylab = "quality")WH_adaptive.kmeans() also estimates a weight for each
variable, so that the variables that better separate the clusters weigh
more. With schema = 2 there is a weight for the position
(mean) and one for the variability (centred distribution) of each
variable; with schema = 3 or 4 the weights
also change with the cluster.
set.seed(1)
akm <- WH_adaptive.kmeans(OzoneFull, k = 3, schema = 2, rep = 5,
simplify = TRUE, qua = 20, standardize = TRUE)round(akm$weights[, 1], 3) # the same for all the clusters with schema = 2
#> Ozone.Conc.ppb(Cen.) Ozone.Conc.ppb(Var.)
#> 0.990 1.100
#> Temperature.C(Cen.) Temperature.C(Var.)
#> 0.502 1.699
#> Solar.Radiation.WattM2(Cen.) Solar.Radiation.WattM2(Var.)
#> 2.172 0.834
#> Wind.Speed.mSec(Cen.) Wind.Speed.mSec(Var.)
#> 0.925 0.642
akm$cardinality
#> IDX
#> Cl.1 Cl.2 Cl.3
#> 28 13 37
akm$quality
#> [1] 0.5701985By default the product of the weights is one
(weight.sys = "PROD"); with weight.sys = "SUM"
they sum to one.
WH_fcmeans() assigns to each individual a degree of
membership to each cluster; m (greater than 1) controls the
fuzziness.
set.seed(1)
fcm <- WH_fcmeans(OzoneFull, k = 3, m = 1.6, rep = 5,
simplify = TRUE, qua = 20, standardize = TRUE)round(head(fcm$solution$membership), 3)
#> [,1] [,2] [,3]
#> I1 0.955 0.002 0.043
#> I2 0.599 0.017 0.383
#> I3 0.185 0.119 0.696
#> I4 0.010 0.004 0.986
#> I5 0.920 0.002 0.077
#> I6 0.074 0.018 0.908
fcm$solution$cardinality # crisp assignment to the cluster of maximum membership
#> IDX
#> Cl.1 Cl.2 Cl.3
#> 33 13 32
fcm$quality
#> [1] 0.4470096Individuals with no clearly dominant membership lie between clusters:
set.seed(1)
afcm <- WH_adaptive_fcmeans(OzoneFull, k = 3, schema = 2, m = 1.6, rep = 3,
simplify = TRUE, qua = 20, standardize = TRUE)round(afcm$solution$var.weights[, 1], 3)
#> Ozone.Conc.ppb(Cen.) Ozone.Conc.ppb(Var.)
#> 0.944 1.110
#> Temperature.C(Cen.) Temperature.C(Var.)
#> 0.599 1.776
#> Solar.Radiation.WattM2(Cen.) Solar.Radiation.WattM2(Var.)
#> 1.776 0.794
#> Wind.Speed.mSec(Cen.) Wind.Speed.mSec(Var.)
#> 0.995 0.638
afcm$quality
#> [1] 0.5286997Hierarchical clustering (WH_hclust(), a wrapper of
stats::hclust()) gives a dendrogram that can be cut at any
number of clusters:
hc <- WH_hclust(OzoneFull, simplify = TRUE, qua = 20, standardize = TRUE,
method = "ward.D2")
plot(hc, labels = FALSE, main = "OzoneFull, Ward method")table(hclust = cutree(hc, k = 3), kmeans = km$solution$IDX)
#> kmeans
#> hclust 1 2 3
#> 1 12 0 34
#> 2 19 0 0
#> 3 0 13 0Self-organising maps (WH_2d_Kohonen_maps() and
WH_2d_Adaptive_Kohonen_maps()) are also available.
Irpino, A., Verde, R., Lechevallier, Y. (2006) Dynamic clustering of histograms using Wasserstein metric. In: COMPSTAT 2006, Physica-Verlag, 869-876.
Irpino, A., Verde, R., De Carvalho, F.A.T. (2014) Dynamic clustering of histogram data based on adaptive squared Wasserstein distances. Expert Systems with Applications, 41, 3351-3366. https://doi.org/10.1016/j.eswa.2013.12.001