Steps to follow in clustering Analysis

1:-Data Preparation 2:-Assesing clustering tendency 3:-Defining the optimal number of cluster 4:-Computing cluster analyses 5:-Validating clustering analyses

Step1:-Data Preparation

library(factoextra)
library(cluster)
# Load the data set
data(USArrests)
# Remove any missing value (i.e, NA values for not available)
# That might be present in the data
USArrests <- na.omit(USArrests)
# View the first 6 rows of the data
head(USArrests, n = 6)

To inspect the data before the K-means clustering we’ll compute some descriptive statistics such as the mean and the standard deviation of the variables.

The apply() function is used to apply a given function (e.g : min(), max(), mean(), …) on the data set. The second argument can take the value of:

1: for applying the function on the rows 2: for applying the function on the columns

desc_stats <- data.frame(
  Min = apply(USArrests, 2, min), # minimum
  Med = apply(USArrests, 2, median), # median
  Mean = apply(USArrests, 2, mean), # mean
  SD = apply(USArrests, 2, sd), # Standard deviation
  Max = apply(USArrests, 2, max) # Maximum
  )
desc_stats <- round(desc_stats, 1)
head(desc_stats)

**Note that the variables have a large different means and variances. They must be standardized to make them comparable.

Standardization consists of transforming the variables such that they have mean zero and standard deviation one. The scale() function can be used as follow:

df<-scale(USArrests)

Step2:-Assesing clustering tendency

The function get_clust_tendency() [in factoextra] can be used. It computes Hopkins statistic and provides a visual approach.

res <- get_clust_tendency(df, 40, graph = FALSE)
# Hopskin statistic
res$hopkins_stat
[1] 0.3440875
# Visualize the dissimilarity matrix
res$plot
NULL

The value of Hopkins statistic is significantly < 0.5, indicating that the data is highly clusterable. Additionally, It can be seen that the ordered dissimilarity image contains patterns (i.e., clusters).

Step 3:-Defining the optimal number of cluster As k-means clustering requires to specify the number of clusters to generate, we’ll use the function clusGap() [in cluster] to compute gap statistics for estimating the optimal number of clusters . The function fviz_gap_stat() [in factoextra] is used to visualize the gap statistic plot.

set.seed(123)
# Compute the gap statistic
gap_stat <- clusGap(df, FUN = kmeans, nstart = 25, 
                    K.max = 10, B = 500) 
Clustering k = 1,2,..., K.max (= 10): .. done
Bootstrapping, b = 1,2,..., B (= 500)  [one "." per sample]:
.................................................. 50 
.................................................. 100 
.................................................. 150 
.................................................. 200 
.................................................. 250 
.................................................. 300 
.................................................. 350 
.................................................. 400 
.................................................. 450 
.................................................. 500 
# Plot the result
fviz_gap_stat(gap_stat)

Step4:-Computing cluster analyses K Means clustering with K = 4

# Compute k-means
set.seed(123)
km.res <- kmeans(df, 4, nstart = 25)
head(km.res$cluster, 20)
    Alabama      Alaska     Arizona    Arkansas  California    Colorado Connecticut    Delaware     Florida     Georgia      Hawaii       Idaho    Illinois     Indiana        Iowa 
          4           3           3           4           3           3           2           2           3           4           2           1           3           2           1 
     Kansas    Kentucky   Louisiana       Maine    Maryland 
          2           1           4           1           3 
# Visualize clusters using factoextra
fviz_cluster(km.res, USArrests)

Step5:-Validating clustering analyses

The silhouette measures (SiSi) how similar an object ii is to the the other objects in its own cluster versus those in the neighbor cluster. SiSi values range from 1 to - 1: A value of SiSi close to 1 indicates that the object is well clustered. In the other words, the object ii is similar to the other objects in its group. A value of SiSi close to -1 indicates that the object is poorly clustered, and that assignment to some other cluster would probably improve the overall results.

sil <- silhouette(km.res$cluster, dist(df))
rownames(sil) <- rownames(USArrests)
head(sil[, 1:3])
           cluster neighbor  sil_width
Alabama          4        3 0.48577530
Alaska           3        4 0.05825209
Arizona          3        2 0.41548326
Arkansas         4        2 0.11870947
California       3        2 0.43555885
Colorado         3        2 0.32654235
fviz_silhouette(sil)
  cluster size ave.sil.width
1       1   13          0.37
2       2   16          0.34
3       3   13          0.27
4       4    8          0.39

It can be seen that there are some samples which have negative silhouette values. Some natural questions are : Which samples are these? To what cluster are they closer?

This can be determined from the output of the function silhouette() as follow:

neg_sil_index <- which(sil[, "sil_width"] < 0)
sil[neg_sil_index, , drop = FALSE]

Enhanced Clustering Analysis The function eclust() [in factoextra] provides several advantages compared to the standard packages used for clustering analysis:

It simplifies the workflow of clustering analysis It can be used to compute hierarchical clustering and partitioning clustering in a single line function call The function eclust() computes automatically the gap statistic for estimating the right number of clusters. It automatically provides silhouette information It draws beautiful graphs using ggplot2

  1. Using K -Means
# Compute k-means
res.km <- eclust(df, "kmeans")
Clustering k = 1,2,..., K.max (= 10): .. done
Bootstrapping, b = 1,2,..., B (= 100)  [one "." per sample]:
.................................................. 50 
.................................................. 100 

# Gap statistic plot
fviz_gap_stat(res.km$gap_stat)

# Silhouette plot
fviz_silhouette(res.km)
  cluster size ave.sil.width
1       1   13          0.27
2       2   13          0.37
3       3    8          0.39
4       4   16          0.34

2.Hierarchical clustering

 # Enhanced hierarchical clustering
res.hc <- eclust(df, "hclust") # compute hclust
Clustering k = 1,2,..., K.max (= 10): .. done
Bootstrapping, b = 1,2,..., B (= 100)  [one "." per sample]:
.................................................. 50 
.................................................. 100 

fviz_dend(res.hc, rect = TRUE) # dendrogam

The R code below generates the silhouette plot and the scatter plot for hierarchical clustering.

fviz_silhouette(res.hc) # silhouette plot
  cluster size ave.sil.width
1       1    7          0.46
2       2   12          0.29
3       3   19          0.26
4       4   12          0.43

fviz_cluster(res.hc) # scatter plot

LS0tCnRpdGxlOiAiQ2x1c3RlcmluZyBpbiByIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tClN0ZXBzIHRvIGZvbGxvdyBpbiBjbHVzdGVyaW5nIEFuYWx5c2lzCgoxOi1EYXRhIFByZXBhcmF0aW9uCjI6LUFzc2VzaW5nIGNsdXN0ZXJpbmcgdGVuZGVuY3kKMzotRGVmaW5pbmcgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXIKNDotQ29tcHV0aW5nIGNsdXN0ZXIgYW5hbHlzZXMKNTotVmFsaWRhdGluZyBjbHVzdGVyaW5nIGFuYWx5c2VzCgoKU3RlcDE6LURhdGEgUHJlcGFyYXRpb24KYGBge3J9CmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShjbHVzdGVyKQojIExvYWQgdGhlIGRhdGEgc2V0CmRhdGEoVVNBcnJlc3RzKQojIFJlbW92ZSBhbnkgbWlzc2luZyB2YWx1ZSAoaS5lLCBOQSB2YWx1ZXMgZm9yIG5vdCBhdmFpbGFibGUpCiMgVGhhdCBtaWdodCBiZSBwcmVzZW50IGluIHRoZSBkYXRhClVTQXJyZXN0cyA8LSBuYS5vbWl0KFVTQXJyZXN0cykKIyBWaWV3IHRoZSBmaXJzdCA2IHJvd3Mgb2YgdGhlIGRhdGEKaGVhZChVU0FycmVzdHMsIG4gPSA2KQpgYGAKClRvIGluc3BlY3QgdGhlIGRhdGEgYmVmb3JlIHRoZSBLLW1lYW5zIGNsdXN0ZXJpbmcgd2XigJlsbCBjb21wdXRlIHNvbWUgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBzdWNoIGFzIHRoZSBtZWFuIGFuZCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSB2YXJpYWJsZXMuCgpUaGUgYXBwbHkoKSBmdW5jdGlvbiBpcyB1c2VkIHRvIGFwcGx5IGEgZ2l2ZW4gZnVuY3Rpb24gKGUuZyA6IG1pbigpLCBtYXgoKSwgbWVhbigpLCDigKYpIG9uIHRoZSBkYXRhIHNldC4gVGhlIHNlY29uZCBhcmd1bWVudCBjYW4gdGFrZSB0aGUgdmFsdWUgb2Y6CgoxOiBmb3IgYXBwbHlpbmcgdGhlIGZ1bmN0aW9uIG9uIHRoZSByb3dzCjI6IGZvciBhcHBseWluZyB0aGUgZnVuY3Rpb24gb24gdGhlIGNvbHVtbnMKCgpgYGB7cn0KZGVzY19zdGF0cyA8LSBkYXRhLmZyYW1lKAogIE1pbiA9IGFwcGx5KFVTQXJyZXN0cywgMiwgbWluKSwgIyBtaW5pbXVtCiAgTWVkID0gYXBwbHkoVVNBcnJlc3RzLCAyLCBtZWRpYW4pLCAjIG1lZGlhbgogIE1lYW4gPSBhcHBseShVU0FycmVzdHMsIDIsIG1lYW4pLCAjIG1lYW4KICBTRCA9IGFwcGx5KFVTQXJyZXN0cywgMiwgc2QpLCAjIFN0YW5kYXJkIGRldmlhdGlvbgogIE1heCA9IGFwcGx5KFVTQXJyZXN0cywgMiwgbWF4KSAjIE1heGltdW0KICApCmRlc2Nfc3RhdHMgPC0gcm91bmQoZGVzY19zdGF0cywgMSkKaGVhZChkZXNjX3N0YXRzKQpgYGAKKipOb3RlIHRoYXQgdGhlIHZhcmlhYmxlcyBoYXZlIGEgbGFyZ2UgZGlmZmVyZW50IG1lYW5zIGFuZCB2YXJpYW5jZXMuIFRoZXkgbXVzdCBiZSBzdGFuZGFyZGl6ZWQgdG8gbWFrZSB0aGVtIGNvbXBhcmFibGUuCgoKU3RhbmRhcmRpemF0aW9uIGNvbnNpc3RzIG9mIHRyYW5zZm9ybWluZyB0aGUgdmFyaWFibGVzIHN1Y2ggdGhhdCB0aGV5IGhhdmUgbWVhbiB6ZXJvIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb25lLiAKVGhlIHNjYWxlKCkgZnVuY3Rpb24gY2FuIGJlIHVzZWQgYXMgZm9sbG93OgoKYGBge3J9CmRmPC1zY2FsZShVU0FycmVzdHMpCmBgYAoKClN0ZXAyOi1Bc3Nlc2luZyBjbHVzdGVyaW5nIHRlbmRlbmN5CgpUaGUgZnVuY3Rpb24gZ2V0X2NsdXN0X3RlbmRlbmN5KCkgW2luIGZhY3RvZXh0cmFdIGNhbiBiZSB1c2VkLiBJdCBjb21wdXRlcyBIb3BraW5zIHN0YXRpc3RpYyBhbmQgcHJvdmlkZXMgYSB2aXN1YWwgYXBwcm9hY2guCmBgYHtyfQpyZXMgPC0gZ2V0X2NsdXN0X3RlbmRlbmN5KGRmLCA0MCwgZ3JhcGggPSBGQUxTRSkKIyBIb3Bza2luIHN0YXRpc3RpYwpyZXMkaG9wa2luc19zdGF0CmBgYAoKYGBge3J9CiMgVmlzdWFsaXplIHRoZSBkaXNzaW1pbGFyaXR5IG1hdHJpeApyZXMkcGxvdApgYGAKCgpUaGUgdmFsdWUgb2YgSG9wa2lucyBzdGF0aXN0aWMgaXMgc2lnbmlmaWNhbnRseSA8IDAuNSwgaW5kaWNhdGluZyB0aGF0IHRoZSBkYXRhIGlzIGhpZ2hseSBjbHVzdGVyYWJsZS4gQWRkaXRpb25hbGx5LCBJdCBjYW4gYmUgc2VlbiB0aGF0IHRoZSBvcmRlcmVkIGRpc3NpbWlsYXJpdHkgaW1hZ2UgY29udGFpbnMgcGF0dGVybnMgKGkuZS4sIGNsdXN0ZXJzKS4KCgpTdGVwIDM6LURlZmluaW5nIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVyCkFzIGstbWVhbnMgY2x1c3RlcmluZyByZXF1aXJlcyB0byBzcGVjaWZ5IHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgdG8gZ2VuZXJhdGUsIHdl4oCZbGwgdXNlIHRoZSBmdW5jdGlvbiBjbHVzR2FwKCkgW2luIGNsdXN0ZXJdIHRvIGNvbXB1dGUgZ2FwIHN0YXRpc3RpY3MgZm9yIGVzdGltYXRpbmcgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIC4gVGhlIGZ1bmN0aW9uIGZ2aXpfZ2FwX3N0YXQoKSBbaW4gZmFjdG9leHRyYV0gaXMgdXNlZCB0byB2aXN1YWxpemUgdGhlIGdhcCBzdGF0aXN0aWMgcGxvdC4KCmBgYHtyfQpzZXQuc2VlZCgxMjMpCiMgQ29tcHV0ZSB0aGUgZ2FwIHN0YXRpc3RpYwpnYXBfc3RhdCA8LSBjbHVzR2FwKGRmLCBGVU4gPSBrbWVhbnMsIG5zdGFydCA9IDI1LCAKICAgICAgICAgICAgICAgICAgICBLLm1heCA9IDEwLCBCID0gNTAwKSAKIyBQbG90IHRoZSByZXN1bHQKZnZpel9nYXBfc3RhdChnYXBfc3RhdCkKYGBgCgoKU3RlcDQ6LUNvbXB1dGluZyBjbHVzdGVyIGFuYWx5c2VzCksgTWVhbnMgY2x1c3RlcmluZyB3aXRoIEsgPSA0CgpgYGB7cn0KIyBDb21wdXRlIGstbWVhbnMKc2V0LnNlZWQoMTIzKQprbS5yZXMgPC0ga21lYW5zKGRmLCA0LCBuc3RhcnQgPSAyNSkKaGVhZChrbS5yZXMkY2x1c3RlciwgMjApCmBgYAoKYGBge3J9CiMgVmlzdWFsaXplIGNsdXN0ZXJzIHVzaW5nIGZhY3RvZXh0cmEKZnZpel9jbHVzdGVyKGttLnJlcywgVVNBcnJlc3RzKQpgYGAKCgpTdGVwNTotVmFsaWRhdGluZyBjbHVzdGVyaW5nIGFuYWx5c2VzCgpUaGUgc2lsaG91ZXR0ZSBtZWFzdXJlcyAoU2lTaSkgaG93IHNpbWlsYXIgYW4gb2JqZWN0IGlpIGlzIHRvIHRoZSB0aGUgb3RoZXIgb2JqZWN0cyBpbiBpdHMgb3duIGNsdXN0ZXIgdmVyc3VzIHRob3NlIGluIHRoZSBuZWlnaGJvciBjbHVzdGVyLiBTaVNpIHZhbHVlcyByYW5nZSBmcm9tIDEgdG8gLSAxOgoqKkEgdmFsdWUgb2YgU2lTaSBjbG9zZSB0byAxIGluZGljYXRlcyB0aGF0IHRoZSBvYmplY3QgaXMgd2VsbCBjbHVzdGVyZWQuIEluIHRoZSBvdGhlciB3b3JkcywgdGhlIG9iamVjdCBpaSBpcyBzaW1pbGFyIHRvIHRoZSBvdGhlciBvYmplY3RzIGluIGl0cyBncm91cC4KKipBIHZhbHVlIG9mIFNpU2kgY2xvc2UgdG8gLTEgaW5kaWNhdGVzIHRoYXQgdGhlIG9iamVjdCBpcyBwb29ybHkgY2x1c3RlcmVkLCBhbmQgdGhhdCBhc3NpZ25tZW50IHRvIHNvbWUgb3RoZXIgY2x1c3RlciB3b3VsZCBwcm9iYWJseSBpbXByb3ZlIHRoZSBvdmVyYWxsIHJlc3VsdHMuCgpgYGB7cn0Kc2lsIDwtIHNpbGhvdWV0dGUoa20ucmVzJGNsdXN0ZXIsIGRpc3QoZGYpKQpyb3duYW1lcyhzaWwpIDwtIHJvd25hbWVzKFVTQXJyZXN0cykKaGVhZChzaWxbLCAxOjNdKQoKYGBgCgpgYGB7cn0KZnZpel9zaWxob3VldHRlKHNpbCkKYGBgCkl0IGNhbiBiZSBzZWVuIHRoYXQgdGhlcmUgYXJlIHNvbWUgc2FtcGxlcyB3aGljaCBoYXZlIG5lZ2F0aXZlIHNpbGhvdWV0dGUgdmFsdWVzLiBTb21lIG5hdHVyYWwgcXVlc3Rpb25zIGFyZSA6CldoaWNoIHNhbXBsZXMgYXJlIHRoZXNlPyBUbyB3aGF0IGNsdXN0ZXIgYXJlIHRoZXkgY2xvc2VyPwoKVGhpcyBjYW4gYmUgZGV0ZXJtaW5lZCBmcm9tIHRoZSBvdXRwdXQgb2YgdGhlIGZ1bmN0aW9uIHNpbGhvdWV0dGUoKSBhcyBmb2xsb3c6CmBgYHtyfQpuZWdfc2lsX2luZGV4IDwtIHdoaWNoKHNpbFssICJzaWxfd2lkdGgiXSA8IDApCnNpbFtuZWdfc2lsX2luZGV4LCAsIGRyb3AgPSBGQUxTRV0KYGBgCgoKRW5oYW5jZWQgQ2x1c3RlcmluZyBBbmFseXNpcwpUaGUgZnVuY3Rpb24gZWNsdXN0KCkgW2luIGZhY3RvZXh0cmFdIHByb3ZpZGVzIHNldmVyYWwgYWR2YW50YWdlcyBjb21wYXJlZCB0byB0aGUgc3RhbmRhcmQgcGFja2FnZXMgdXNlZCBmb3IgY2x1c3RlcmluZyBhbmFseXNpczoKCkl0IHNpbXBsaWZpZXMgdGhlIHdvcmtmbG93IG9mIGNsdXN0ZXJpbmcgYW5hbHlzaXMKSXQgY2FuIGJlIHVzZWQgdG8gY29tcHV0ZSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBhbmQgcGFydGl0aW9uaW5nIGNsdXN0ZXJpbmcgaW4gYSBzaW5nbGUgbGluZSBmdW5jdGlvbiBjYWxsClRoZSBmdW5jdGlvbiBlY2x1c3QoKSBjb21wdXRlcyBhdXRvbWF0aWNhbGx5IHRoZSBnYXAgc3RhdGlzdGljIGZvciBlc3RpbWF0aW5nIHRoZSByaWdodCBudW1iZXIgb2YgY2x1c3RlcnMuCkl0IGF1dG9tYXRpY2FsbHkgcHJvdmlkZXMgc2lsaG91ZXR0ZSBpbmZvcm1hdGlvbgpJdCBkcmF3cyBiZWF1dGlmdWwgZ3JhcGhzIHVzaW5nIGdncGxvdDIKCjEuIFVzaW5nIEsgLU1lYW5zCmBgYHtyfQojIENvbXB1dGUgay1tZWFucwpyZXMua20gPC0gZWNsdXN0KGRmLCAia21lYW5zIikKYGBgCgpgYGB7cn0KIyBHYXAgc3RhdGlzdGljIHBsb3QKZnZpel9nYXBfc3RhdChyZXMua20kZ2FwX3N0YXQpCmBgYAoKCmBgYHtyfQojIFNpbGhvdWV0dGUgcGxvdApmdml6X3NpbGhvdWV0dGUocmVzLmttKQpgYGAKCjIuSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcKYGBge3J9CiAjIEVuaGFuY2VkIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCnJlcy5oYyA8LSBlY2x1c3QoZGYsICJoY2x1c3QiKSAjIGNvbXB1dGUgaGNsdXN0CmZ2aXpfZGVuZChyZXMuaGMsIHJlY3QgPSBUUlVFKSAjIGRlbmRyb2dhbQpgYGAKClRoZSBSIGNvZGUgYmVsb3cgZ2VuZXJhdGVzIHRoZSBzaWxob3VldHRlIHBsb3QgYW5kIHRoZSBzY2F0dGVyIHBsb3QgZm9yIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLgpgYGB7cn0KZnZpel9zaWxob3VldHRlKHJlcy5oYykgIyBzaWxob3VldHRlIHBsb3QKZnZpel9jbHVzdGVyKHJlcy5oYykgIyBzY2F0dGVyIHBsb3QKYGBgCgoKCgoKCg==