Clustering jerárquico y dendógramas

Un clustering jerárquico intenta construir una jerarquía a través de divisiones de nuestros datos de forma aglomerativa o dividiendo los datos. Aglomerativa hace agrupacionos de form individual o de forma que se dividan los datos. Estos elementos usan las distáncias para decdiri que clusters deben agruparse y cuáles deben dividirse. Es un proceso recursivo que continua hasta que solo queda un cluster en el caso del aglomerativo, o bien en el caso del divisitivo, hasta que no queda nada más que dividir o repartir en un nuevo cluster.

Usamos el dendograma para representar la jerarquía de clusers.

Usaremos el consumo de proteinas en europa:

protein <- read.csv("../DataSets//protein.csv")

Ignoraremos la varaible categorica de country al ser categórica. Vamos a normalizar y escalar los datos para poder hacer el dendograma:

data <- as.data.frame(scale(protein[,-1])) # quitamos la primera columna
data$Country = protein$Country # volvemos a añadir el país 

Vamos a hacer un clustering aglomerativo para ver los paíssi que se acercan con el consumo de proteinas:

rownames(data) = data$Country # damos nombre a las filas con los países
hc <- hclust(dist(data, method = "euclidean"), # con dist indicaremos la distáncia del clustering, usaremos el metodo euclidean, la distancia  más usada
             method = "ward.D2") # metodo de clusterización para decidir como se hora el nuevo cluster a partir del anterior, es le método de la mínima varianza para hacer un cluster aglomerativo y el que mejor funciona, dado que pondera según el número de elementos del dataset
## Warning in dist(data, method = "euclidean"): NAs introducidos por coerción
hc
## 
## Call:
## hclust(d = dist(data, method = "euclidean"), method = "ward.D2")
## 
## Cluster method   : ward.D2 
## Distance         : euclidean 
## Number of objects: 25

Podemos ver que se han clasificado nuestros 25 países. Vamos a visualizar el cluster:

plot(hc, hang = -0.01, cex = 0.7)

Podemos usar otros métodos, como el simple:

hc2 <- hclust(dist(data, method = "euclidean"),
              method = "single") # usamos el metodo simple, método aglometarivo
## Warning in dist(data, method = "euclidean"): NAs introducidos por coerción
plot(hc2, hang=-0.01, cex = 0.7) # hang nos permite poner las etiquetas al final

El método de construcción del cluster determina la forma del dendograma.

EL clustering jerárquico aglomerativo se toma un enfoque de abajo hacia arriba, cada observación del dataset es un cluster en si mismo, y luego se van computando las similitudes o las distancias de cada uno de esos elementos al resto. Juntan aquellos que tengan mas elemento en común o esten a menos distancia, hasta que solo quede un solo cluster.

El método divisitivo empieza desde arriba hacia abajo, todas las observaciones empiezan en un solo cluster, y luego van haciendo divisiones sucesivas en dos o más clusters recursivamente, hasta que solo queda un cluster para cada una de las observaciones.

Las distáncias y el método de generación de clusters

d <- dist(data, method = "euclidean")
## Warning in dist(data, method = "euclidean"): NAs introducidos por coerción

La anterior matriz nos ha calculado la distancia para cada uno de los países, en cuando al consumo de proteinas. La distáncia euclidia nos compara las coordenadas de las variables.

Vemos el vector de albania:

alb<-data["Albania",-10]
alb
aus<-data["Austria",-10]
aus
# Miramos las distancias entre los dos países haciendo el teorema  de pitágoras
sqrt(sum((alb-aus)^2))
## [1] 6.136051
# Calculamos 
sum(abs(alb-aus))
## [1] 15.97134

Otros ejemplos de métodos:

hc3 <- hclust(dist(data, method = "euclidean"),
              method = "complete")
plot(hc3, hang=-0.01, cex = 0.7)

Para entender las distáncias hemos de entender la variable “merge”, que nos idnica con quién ha sido juntado y en que orden:

head(hc3$merge, 10)
##       [,1] [,2]
##  [1,]  -18  -25
##  [2,]   -2  -14
##  [3,]   -6  -20
##  [4,]   -3  -24
##  [5,]  -12    4
##  [6,]   -5   -7
##  [7,]   -4    1
##  [8,]  -15    3
##  [9,]  -10  -13
## [10,]  -21    2

En primer lugar nos junta el país número 18 con el número 25, el valor (-) nos indica los objetos básicos. Ejemplo:

  1. Rumania(-18) y Yugoslavia(-25)

  2. Austria(-2) y Países Bajos(-14)

Cuando empezamos a tener números positivos es cuando se emmpiezan a juntar diferentes clusters, en este caso el 4 cluster(Belgia y Alemania) se junta con Irlanda.

Clustering divisitivos y cortes en el dendograma

library(cluster)

Haremos uso de la función diana, que perite hacer clusterings divisitivos en lulgar de los aglomeravitos, le debemos pasar le dataset original y la distáncia para que haga las divisiones:

dv <- diana(data, metric = "euclidean")
par(mfrow=c(1,2))
plot(dv)

Podemos ver las divisiones que se han llevado gracias a las distancias que imprime por pantalla para cada país. Pdemos ver la profundidad en la quese juntan los diferentes países. Irlanda e Italia no se juntan hasta el final.

Haciendo cortes del dendograma para separ los cluster con la función cut tree:

fit <- cutree(hc, k=4) # con esta función agrupamos los países en 4 categorías

table(fit) # podemos ver los 4 clusters en la tabla
## fit
##  1  2  3  4 
##  4 12  5  4
plot(hc, hang = -0.01, cex = 0.7)
rect.hclust(hc, k=4, border="red") # representa con cajitas encuma del plot 

Podemos ver que hemos cortado entre 6 y 7. Para k = 5 nos saldría más bajo.

Clustering partitivos con k-means

Es una técnica de partición de clustering que se enfoca en dividir n observaciones iniciales en k clusters, de modo que cada observación pertenece al cluster cuyo promedio esté más cercano, sirivendo este como el prototipo del cluster.

Es un método menos cosotos computacionalmente que los anteriores y también más rápido.

Usaremos el mismo dataset de proteinas que hemos usado anteriormente:

protein <- read.csv("../DataSets/protein.csv")
rownames(protein) = protein$Country
protein$Country = NULL
protein.scaled = as.data.frame(scale(protein))

Usaremos la libreria devtools, una librería de git_hub(). Esta librería nos permitirá visualizar los clusters de k means, en el caso que tengamos muchas variables.

library(devtools)
# devtools::install_github("kassambara/factoextra") # ponemos el usuario de git hub dentro de kassambara

Esta libreria nos hace un PCA de forma automática, es una básica para cualquier analista de datos.

Creamos el k-mean:

km <- kmeans(protein.scaled,5) # le pasamos el dataset escalado
km
## K-means clustering with 5 clusters of sizes 2, 5, 10, 2, 6
## 
## Cluster means:
##       RedMeat  WhiteMeat        Eggs       Milk       Fish    Cereals
## 1 -0.94948480 -1.1764767 -0.74802044 -1.4583242  1.8562639 -0.3779572
## 2  1.59900650  0.2988565  0.93413079  0.6091128 -0.1422470 -0.5948180
## 3 -0.12189736  0.6101653  0.39727402  0.5711137  0.2486383 -0.6175974
## 4 -0.06811911 -1.0411250 -0.07694947 -0.2057585  0.1075669  0.6380079
## 5 -0.79014185 -0.5267887 -1.16557572 -0.9047559 -0.9504683  1.4383272
##       Starch       Nuts     Fr.Veg
## 1  0.9326321  1.1220326  1.8925628
## 2  0.3451473 -0.3484949  0.1020010
## 3  0.3573866 -0.8823165 -0.3802865
## 4 -1.3010340  1.4997366  1.3659270
## 5 -0.7604664  0.8870168 -0.5373533
## 
## Clustering vector:
##        Albania        Austria        Belgium       Bulgaria Czechoslovakia 
##              5              3              2              5              3 
##        Denmark      E Germany        Finland         France         Greece 
##              3              3              3              2              4 
##        Hungary        Ireland          Italy    Netherlands         Norway 
##              5              2              4              3              3 
##         Poland       Portugal        Romania          Spain         Sweden 
##              3              1              5              1              3 
##    Switzerland             UK           USSR      W Germany     Yugoslavia 
##              2              2              5              3              5 
## 
## Within cluster sum of squares by cluster:
## [1]  4.300578 12.069794 37.502503  2.311516 24.091128
##  (between_SS / total_SS =  62.8 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"    
## [5] "tot.withinss" "betweenss"    "size"         "iter"        
## [9] "ifault"

El objeto nos devuelve como estan divididos los 4 clusters. Importante fijarse en el porcentaje de los datos que quedan explicados con este número de clusters: en este caso seá un 66.7%

Podemos saber la media del consumo de cada una de las variables:

aggregate(protein.scaled, by = list(cluster = km$cluster), mean)

Podemos ver la información de cada clusters, para entender las variables escaladas para cada una de las variables. Vamos a visualizar los datos:

library(factoextra)
## Loading required package: ggplot2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
fviz_cluster(km, data = protein.scaled)

Importante para clusterización, debemos fijarnos en los outliers porque pueden afectar mucho a la creación de los clusters.

Mini Batch k-mean para segmentación de imágenes

Esta técnica es una variante muy útil para e tratamiento de imágenes. Randomiza, coe muestras aleatorias, para optimizar la función objetivo en su lugar.

library(OpenImageR)
library(ClusterR)
## Loading required package: gtools
img <- readImage("../Images/bird.jpg")
class(img)
## [1] "array"

Podemos redimiensionar la imagen para que sea más óptima de analizar:

img.resize <- resizeImage(img, 350, 350, 
                          method = "bilinear") # hay diferentes metodos, a que usaremos billinear que reduce el efecto de pixelado
imageShow(img.resize) # nos mostraría la imagen si lo intoducimos en a consola de R

Ahora vamos a ver la dimension de la misma:

img.vector <- apply(img, 3, as.vector) # creamos una matriz de 3 en 3
dim(img.vector) # nos muestra cada uno de los pixeles en 3 columnas
## [1] 113232      3

Ahora usaremos el método mini batch k means para predecir el color de un pixel en relación al pixel más cercano:

kmmb<-MiniBatchKmeans(img.vector, clusters = 10, # le pasamos el vector  que hemos creado, optaremos por poner 10 cluester, que representan el númer de colores que hay en la imagen (elegimos a ojo humano)
                      batch_size = 20, num_init = 5,
                      max_iters = 100, init_fraction = 0.2,
                      initializer = "kmeans++",
                      early_stop_iter = 10, verbose = F) # que no se pare núnca hasta la iteración 10
kmmb
## $centroids
##             [,1]      [,2]      [,3]
##  [1,] 0.45882353 0.4352941 0.3921569
##  [2,] 0.98431373 0.2352941 0.2980392
##  [3,] 0.08627451 0.2627451 0.3529412
##  [4,] 0.40000000 0.5411765 0.1490196
##  [5,] 0.09215686 0.2568627 0.4352941
##  [6,] 0.21288515 0.2957983 0.1697479
##  [7,] 0.14852941 0.1946078 0.1142157
##  [8,] 0.87450980 0.6078431 0.1019608
##  [9,] 0.57647059 0.6862745 0.5784314
## [10,] 0.36078431 0.4980392 0.7176471
## 
## $WCSS_per_cluster
##             [,1]        [,2] [,3] [,4]       [,5]       [,6]       [,7]
## [1,] 0.004398308 0.004183007    0    0 0.01051903 0.07852364 0.05783929
##      [,8] [,9] [,10]
## [1,]    0    0     0
## 
## $best_initialization
## [1] 1
## 
## $iters_per_initialization
##      [,1] [,2] [,3] [,4] [,5]
## [1,]   29   16   10   11   17
## 
## attr(,"class")
## [1] "k-means clustering"

Ahora usaremos el array veccotrial para ver la predicción:

prmb <- predict_MBatchKMeans(img.vector, kmmb$centroids) # añadimos los centroides que el modelo ha predecido

Vemos los resultados de la predicción:

get.cent.mb <- kmmb$centroids
new.img <- get.cent.mb[prmb,] # nueva imagen en función a los valores predichos
dim(new.img) <- c(nrow(img), ncol(img),3) # redimensionamos l aimagen  con el número de  filas de la imagen original, y número de columnas  de 3 en 3
imageShow(new.img) #  muestrame la imagen en consola

Particiós de los datos alrededor de los medoides

Es una variante de k-mean, intenta agrupar los datos alrededor de los medoides. Es una mejora al algoritmo. Es un algoritmo que busca minamizar la distancia entre los puntos que se añaden en el grupo respecto al centro. El centro es un medoide, un objeto del grupo que más se parece al resto del grupo, se acostumbre a encontrar más al centro geometricamente.

importante siempre escalamos para evitar outliers y que ninguna variable domine sobre el resto

Volveremos a hacer uso del dataset anterior:

protein <- read.csv("../DataSets/protein.csv")
rownames(protein) = protein$Country
protein$Country = NULL
protein.scaled <- as.data.frame(scale(protein))

Usaremos la libreria cluster y facotrextra del repositorio de github, y la punción PAM (Partition Around Medoids):

library(cluster)
library(factoextra)

km <- pam(protein.scaled, 4) # evitamos usar la media como centro geometrica, se basa en buscar los k medoides entre todos los datos del dataset
km
## Medoids:
##           ID    RedMeat   WhiteMeat       Eggs       Milk       Fish
## Romania   18 -1.0839304 -0.43204252 -1.2848772 -0.8461152 -0.9651632
## W Germany 24  0.4696633  1.24631815  1.0415022  0.2375653 -0.2598064
## Sweden    20  0.0215113 -0.02598752  0.5046454  1.0679178  0.9451781
## Spain     19 -0.8150392 -1.21708219  0.1467409 -1.1979595  0.7982288
##              Cereals     Starch       Nuts     Fr.Veg
## Romania    1.5810786 -0.7196689  1.1220326 -0.7406162
## W Germany -1.2435778  0.5654541 -0.7916675 -0.1862628
## Sweden    -1.1615716 -0.3524909 -0.8420280 -1.1840990
## Spain     -0.2777275  0.8714358  1.4241958  1.6985391
## Clustering vector:
##        Albania        Austria        Belgium       Bulgaria Czechoslovakia 
##              1              2              2              1              2 
##        Denmark      E Germany        Finland         France         Greece 
##              3              2              3              2              4 
##        Hungary        Ireland          Italy    Netherlands         Norway 
##              1              2              4              2              3 
##         Poland       Portugal        Romania          Spain         Sweden 
##              2              4              1              4              3 
##    Switzerland             UK           USSR      W Germany     Yugoslavia 
##              2              2              1              2              1 
## Objective function:
##    build     swap 
## 1.847172 1.839215 
## 
## Available components:
##  [1] "medoids"    "id.med"     "clustering" "objective"  "isolation" 
##  [6] "clusinfo"   "silinfo"    "diss"       "call"       "data"

Podemos ver que a diferencia de antes no tenemos las medias, sino los medoides. Los medoides son los paises como Romania, W germany, Sweden y Spain.

Vamos a representar los datos:

fviz_cluster(km)

Podemos ver como los países que se suponen medoides estan más en el centro.

Clustering large application (clara)

Es una técnica muy útil en el caso que tengamos datasets granes para reducir el costed de computación.

Aplica la partición alrededor de medoides, como el anterior algortimo.

Seguimos con el dataset anterior:

clarafit <- clara(protein.scaled, 4, samples = 6) # especificamos le número de k y el número de muestras que queremos que nos haga
clarafit
## Call:     clara(x = protein.scaled, k = 4, samples = 6) 
## Medoids:
##              RedMeat   WhiteMeat       Eggs       Milk       Fish
## Romania   -1.0839304 -0.43204252 -1.2848772 -0.8461152 -0.9651632
## W Germany  0.4696633  1.24631815  1.0415022  0.2375653 -0.2598064
## Sweden     0.0215113 -0.02598752  0.5046454  1.0679178  0.9451781
## Spain     -0.8150392 -1.21708219  0.1467409 -1.1979595  0.7982288
##              Cereals     Starch       Nuts     Fr.Veg
## Romania    1.5810786 -0.7196689  1.1220326 -0.7406162
## W Germany -1.2435778  0.5654541 -0.7916675 -0.1862628
## Sweden    -1.1615716 -0.3524909 -0.8420280 -1.1840990
## Spain     -0.2777275  0.8714358  1.4241958  1.6985391
## Objective function:   1.839215
## Clustering vector:    Named int [1:25] 1 2 2 1 2 3 2 3 2 4 1 2 4 2 3 2 4 1 ...
##  - attr(*, "names")= chr [1:25] "Albania" "Austria" "Belgium" "Bulgaria" "Czechoslovakia" "Denmark" "E Germany" ...
## Cluster sizes:            6 11 4 4 
## Best sample:
##  [1] Albania        Austria        Belgium        Bulgaria      
##  [5] Czechoslovakia Denmark        E Germany      Finland       
##  [9] France         Greece         Hungary        Ireland       
## [13] Italy          Netherlands    Norway         Poland        
## [17] Portugal       Romania        Spain          Sweden        
## [21] Switzerland    UK             USSR           W Germany     
## [25] Yugoslavia    
## 
## Available components:
##  [1] "sample"     "medoids"    "i.med"      "clustering" "objective" 
##  [6] "clusinfo"   "diss"       "call"       "silinfo"    "data"

Tiene la misma estructira que PAM. Visualizamos los datos y vemos los mismos resultados que antes.

fviz_cluster(clarafit)

Validando los datos de un clustering

El clustering puede tenre tendencia a generar clusters donde no debería o ser muy sensible a los outliers. Para ello es importante validar los clusters creados, a través de:

Usarmos diferentes librerias:

library(factoextra)
library(cluster)
library(fpc)
library(NbClust)

Cargamos le dataset:

protein <- read.csv("../DataSets/protein.csv")
rownames(protein) = protein$Country
protein$Country = NULL
protein.scaled <- as.data.frame(scale(protein))

Usaremos la función nbClust para encontrar el número óptimo de clusters:

nb <- NbClust(protein.scaled, distance = "euclidean", # usamos la distancia euclidia
              min.nc = 2, max.nc = 12, # mínimo y máximo nº de clusters
              method = "ward.D2", index = "all") # que téncica usaremos para el cluster aglomerativo
## Warning in pf(beale, pp, df2): NaNs produced

## *** : The Hubert index is a graphical method of determining the number of clusters.
##                 In the plot of Hubert index, we seek a significant knee that corresponds to a 
##                 significant increase of the value of the measure i.e the significant peak in Hubert
##                 index second differences plot. 
## 

## *** : The D index is a graphical method of determining the number of clusters. 
##                 In the plot of D index, we seek a significant knee (the significant peak in Dindex
##                 second differences plot) that corresponds to a significant increase of the value of
##                 the measure. 
##  
## ******************************************************************* 
## * Among all indices:                                                
## * 6 proposed 2 as the best number of clusters 
## * 7 proposed 3 as the best number of clusters 
## * 3 proposed 6 as the best number of clusters 
## * 1 proposed 7 as the best number of clusters 
## * 1 proposed 10 as the best number of clusters 
## * 5 proposed 12 as the best number of clusters 
## 
##                    ***** Conclusion *****                            
##  
## * According to the majority rule, the best number of clusters is  3 
##  
##  
## *******************************************************************

Entiendiendo los resultados:

Para estar más seguros, podemos visualizar los resultados:

fviz_nbclust(nb) + theme_minimal()
## Among all indices: 
## ===================
## * 2 proposed  0 as the best number of clusters
## * 1 proposed  1 as the best number of clusters
## * 6 proposed  2 as the best number of clusters
## * 7 proposed  3 as the best number of clusters
## * 3 proposed  6 as the best number of clusters
## * 1 proposed  7 as the best number of clusters
## * 1 proposed  10 as the best number of clusters
## * 5 proposed  12 as the best number of clusters
## 
## Conclusion
## =========================
## * According to the majority rule, the best number of clusters is  3 .

Ya nos muestra los resultados de k = 3. Ahora podemos usar 3 como la variables k:

km.res <- kmeans(protein.scaled, 3)
sil.km <- silhouette(km.res$cluster, 
                     dist(protein.scaled))# calculanos la distancia de los objetos iniciales
sil.sum <- summary(sil.km)
sil.sum
## Silhouette of 25 units in 3 clusters from silhouette.default(x = km.res$cluster, dist = dist(protein.scaled)) :
##  Cluster sizes and average silhouette widths:
##         4        10        11 
## 0.4187964 0.1936819 0.2068237 
## Individual silhouette widths:
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -0.03801  0.14773  0.23808  0.23548  0.32609  0.46940

Nos muestra la silueta de los clusters, vamos a visualizar los resultados:

fviz_silhouette(sil.km)
##   cluster size ave.sil.width
## 1       1    4          0.42
## 2       2   10          0.19
## 3       3   11          0.21

Podemos ver tantas barras como países en el dataset. Podemos ver variables relavantes como:

Esto lo podemos ver con el análisis de la silueta del k-means, y el valor de “dunn”.

Cuando vemos que la mayoría de países están más lejos de 0, mejor. Los que son negativos o alrededor de 0 quiere decir que estan muy cerca de otros clusters. La linea roja muestra el promdeio de las distancias entre los distintos clusters.

En el caso de valores negativos, puede que ese país debería estar en otro cluster.

fviz_cluster(km.res, data = protein.scaled)

Seguimos con el análisis de la métrica “dunn”:

dd <- dist(protein.scaled, method = "euclidean") # cremaos una variables para analizar la distancia
km_stats <- cluster.stats(dd, km.res$cluster) # calculamos lla distancia respecto los clusters
km_stats$within.cluster.ss # suma de los cuadrados internos del cluster y se suman todos
## [1] 114.7435
km_stats$clus.avg.silwidths # distancia dentro de cada uno de los clusters (promedio)
##         1         2         3 
## 0.4187964 0.1936819 0.2068237
km_stats$dunn # Este índice es el cociente entre la distancia más pequeña entre obervaciones que no estan en el mismo cluster entre la distancia de cada elemento de cada cluster. Este valor oude acercarse a 0 o ir hasta infinito, y el objetivo es hacer crecer esta distancia para saber que se ha hecho una buena separación. 
## [1] 0.3618008

Vamos a intentar mazimezar el índicie de “dunn”:

kmed <- pam(protein.scaled, 3)

sil.kmed <- silhouette(kmed$clustering, 
                     dist(protein.scaled))
fviz_cluster(kmed, data = protein.scaled)

fviz_silhouette(sil.kmed)
##   cluster size ave.sil.width
## 1       1    6          0.31
## 2       2   15          0.38
## 3       3    4          0.21

kmed_stats <- cluster.stats(dd, kmed$clustering)
kmed_stats$within.cluster.ss
## [1] 105.9863
kmed_stats$clus.avg.silwidths
##         1         2         3 
## 0.3062056 0.3811136 0.2063242
kmed_stats$dunn
## [1] 0.5678917

Vemos que el indice ha mejorado mucho usando el algorimto de cluusterización de pam. Vemos que la USSR, su silueta es cercana a 0, indicando que podría formar parte de otro cluster.

Podemos analizar otras métricas relevantes:

# comparamos dos algoritmos con las siguientes metricas:
res.com <- cluster.stats(dd, km.res$cluster, 
                         kmed$clustering)

# Indice de corrección aleatoria, indica la similitud entre los dos clusters. El objetivo es maximizarlo:
res.com$corrected.rand
## [1] 0.5243425
# Variación de la información: distancia que existe entre los dos clusters, es una metrica que debe ser minimizada
res.com$vi
## [1] 0.6171538

Clustering avanzados

Los clusters analizados anteriormente encuentran clusters de forma esfeica o convexa, y usan la eurítica para crear los grupos. Esto sistema se ve muy afectado por el ruido en la información, outliers. Vamos a ver dos técnicas más avanzadas:

Clustering basados en densidad de puntos

Usaremos los puntos de un data set como puntos del cluster, del oborde o de ruido, segun si son alcanzables a cierta distancia, a cierta densidad o si estan muy conectados.

Usaremos el dataset multishapes del paquete factoextra:

library(fpc)
library(factoextra)
data("multishapes", package = "factoextra")

# Seleccionamos los datos
par(mfrow=c(1,1))
head(multishapes)
dataPoints <- multishapes[,1:2]
plot(dataPoints)

Vemos que estos puntos siguen una distribución difícil de definir con formas a través de un k-means. Probemos como sería usando un k-means:

km <- kmeans(dataPoints, 5)
fviz_cluster(km, data= dataPoints)

Podemos ver que separa el círculo interior cuando debería juntarlo. El poblema de este dataset es que tiene información que sobra. Usamos otra medotología:

dsFit <- dbscan(dataPoints, 
                eps = 0.15, MinPts = 5) # parámetro epsilon, que es el radio más pequeño entre vecinos, y puntos mínimos, no crear un cluster a mínimo que hayan 5 punto en el vecindario
dsFit
## dbscan Pts=1100 MinPts=5 eps=0.15
##         0   1   2   3  4  5
## border 31  24   1   5  7  1
## seed    0 386 404  99 92 50
## total  31 410 405 104 99 51

Cuando hacemo sun clustering basados en densidades intentamos llevar a cabo representaciones más allá de las lineales.

Se llama punto del borde aquellos que sus vecinos son inferiores a minimum points. Después tenemos los puntos que estan solos.

Representamos los clusers creados:

fviz_cluster(dsFit, dataPoints, geom = "point")

Clustering basados en modelos

library(mclust)
## Package 'mclust' version 5.4.2
## Type 'citation("mclust")' for citing this R package in publications.
mclust <- Mclust(dataPoints) # usamos el dataset anterior
plot(mclust)

summary(mclust)
## ---------------------------------------------------- 
## Gaussian finite mixture model fitted by EM algorithm 
## ---------------------------------------------------- 
## 
## Mclust VEV (ellipsoidal, equal shape) model with 9 components: 
## 
##  log.likelihood    n df       BIC       ICL
##       -2062.757 1100 45 -4440.653 -4577.743
## 
## Clustering table:
##   1   2   3   4   5   6   7   8   9 
## 134 187 112 132 128 142 108 104  53

Reducir las dimensiones de ACP

Este proceso es ideal para elegir variables relevantes para entender los datos:

bh <- read.csv("../DataSets//BostonHousing.csv")

library(corrplot)
## corrplot 0.84 loaded
corr <- cor(bh[,-14]) 
corrplot(corr, method = "circle")

Con la matriz de correlaciones podremos reducir el tamaño del dataset conservando solo las variables relevantes.

Hacemos el análisis de componentes principales:

bh.acp <- prcomp(bh[,-14], scale = T) # con scale igual a true creara el acp a través del modelo de correlaciones:
#scale = T, matriz de correlaciones
#scale = F, matriz de covarianzas
summary(bh.acp)
## Importance of components:
##                           PC1    PC2     PC3     PC4     PC5     PC6
## Standard deviation     2.4752 1.1972 1.11473 0.92605 0.91368 0.81081
## Proportion of Variance 0.4713 0.1103 0.09559 0.06597 0.06422 0.05057
## Cumulative Proportion  0.4713 0.5816 0.67713 0.74310 0.80732 0.85789
##                            PC7     PC8    PC9    PC10    PC11    PC12
## Standard deviation     0.73168 0.62936 0.5263 0.46930 0.43129 0.41146
## Proportion of Variance 0.04118 0.03047 0.0213 0.01694 0.01431 0.01302
## Cumulative Proportion  0.89907 0.92954 0.9508 0.96778 0.98209 0.99511
##                           PC13
## Standard deviation     0.25201
## Proportion of Variance 0.00489
## Cumulative Proportion  1.00000

Podemos ver como con pc1 explicamos el 47% de los datos, para pc2 será 58% y subiendo.

plot(bh.acp, type = "lines")

Otras forma de visualización:

biplot(bh.acp, col = c("gray", "red"))

Ahora podemos ver para cada una de las componentes que peso tienen cad auna de las variables.

head(bh.acp$x,5)
##            PC1          PC2         PC3       PC4        PC5        PC6
## [1,] -2.096223  0.772348426  0.34260368 0.8908924  0.4226521 -0.3150264
## [2,] -1.455811  0.591399952 -0.69451201 0.4869766 -0.1956820  0.2639620
## [3,] -2.072547  0.599046578  0.16695638 0.7384734 -0.9336102  0.4476516
## [4,] -2.608922 -0.006863826 -0.10018499 0.3433814 -1.1038636  0.6639924
## [5,] -2.455755  0.097615346 -0.07527372 0.4274838 -1.0648705  0.6164372
##            PC7        PC8         PC9        PC10        PC11        PC12
## [1,] 0.3183257 -0.2955393 -0.42451671 -0.63957348 -0.03296774 -0.01942101
## [2,] 0.5533137  0.2234488 -0.16679701 -0.08415319 -0.64017631  0.12567304
## [3,] 0.4840809 -0.1050622  0.06970615  0.18020170 -0.48707471 -0.13319472
## [4,] 0.6220253 -0.2556877 -0.34190738 -0.04585594 -0.35985263 -0.50817472
## [5,] 0.7043889  0.1343911 -0.41725486  0.14074052 -0.39475923 -0.49723967
##              PC13
## [1,]  0.365613514
## [2,] -0.070649578
## [3,] -0.014007936
## [4,]  0.007839164
## [5,]  0.014260153

Nos muestra para cada una de las obervacioens sus componentes principales.

bh.acp$rotation
##                  PC1         PC2         PC3         PC4          PC5
## CRIM     0.250951397 -0.31525237  0.24656649 -0.06177071  0.082156919
## ZN      -0.256314541 -0.32331290  0.29585782 -0.12871159  0.320616987
## INDUS    0.346672065  0.11249291 -0.01594592 -0.01714571 -0.007811194
## CHAS     0.005042434  0.45482914  0.28978082 -0.81594136  0.086530945
## NOX      0.342852313  0.21911553  0.12096411  0.12822614  0.136853557
## RM      -0.189242570  0.14933154  0.59396117  0.28059184 -0.423447195
## AGE      0.313670596  0.31197778 -0.01767481  0.17520603  0.016690847
## DIS     -0.321543866 -0.34907000 -0.04973627 -0.21543585  0.098592247
## RAD      0.319792768 -0.27152094  0.28725483 -0.13234996 -0.204131621
## TAX      0.338469147 -0.23945365  0.22074447 -0.10333509 -0.130460565
## PTRATIO  0.204942258 -0.30589695 -0.32344627 -0.28262198 -0.584002232
## B       -0.202972612  0.23855944 -0.30014590 -0.16849850 -0.345606947
## LSTAT    0.309759840 -0.07432203 -0.26700025 -0.06941441  0.394561129
##                 PC6          PC7          PC8         PC9         PC10
## CRIM    -0.21965961  0.777607207 -0.153350477  0.26039028 -0.019369130
## ZN      -0.32338810 -0.274996280  0.402680309  0.35813749 -0.267527234
## INDUS   -0.07613790 -0.339576454 -0.173931716  0.64441615  0.363532262
## CHAS     0.16749014  0.074136208  0.024662148 -0.01372777  0.006181836
## NOX     -0.15298267 -0.199634840 -0.080120560 -0.01852201 -0.231056455
## RM       0.05926707  0.063939924  0.326752259  0.04789804  0.431420193
## AGE     -0.07170914  0.116010713  0.600822917 -0.06756218 -0.362778957
## DIS      0.02343872 -0.103900440  0.121811982 -0.15329124  0.171213138
## RAD     -0.14319401 -0.137942546 -0.080358311 -0.47089067 -0.021909452
## TAX     -0.19293428 -0.314886835 -0.082774347 -0.17656339  0.035168348
## PTRATIO  0.27315330  0.002323869  0.317884202  0.25442836 -0.153430488
## B       -0.80345454  0.070294759  0.004922915 -0.04489802  0.096515117
## LSTAT   -0.05321583  0.087011169  0.424352926 -0.19522139  0.600711409
##                PC11         PC12         PC13
## CRIM    -0.10964435 -0.086761070  0.045952304
## ZN       0.26275629  0.071425278 -0.080918973
## INDUS   -0.30316943  0.113199629 -0.251076540
## CHAS     0.01392667  0.003982683  0.035921715
## NOX      0.11131888 -0.804322567  0.043630446
## RM       0.05316154 -0.152872864  0.045567096
## AGE     -0.45915939  0.211936074 -0.038550683
## DIS     -0.69569257 -0.390941129 -0.018298538
## RAD      0.03654388  0.107025890 -0.633489720
## TAX     -0.10483575  0.215191126  0.720233448
## PTRATIO  0.17450534 -0.209598826  0.023398052
## B        0.01927490 -0.041723158 -0.004463073
## LSTAT    0.27138243 -0.055225960  0.024431677

Esta nos muestra el peso de cada variable para cada componente principal. Mostramos tambien la desviación standar:

bh.acp$sdev
##  [1] 2.4752472 1.1971947 1.1147272 0.9260535 0.9136826 0.8108065 0.7316803
##  [8] 0.6293626 0.5262541 0.4692950 0.4312938 0.4114644 0.2520104

Si nos quedamos con las 7 componentes (las 7 primeras), tenemos suficiente para explicar toda la información.