Contenido

Análisis de Grupos: Clasificación de Datos Binarios


Introduction

Introducción

El análisis de grupos (cluster analysis) es el problema estadístico de clasificar datos similares en uno o más grupos según la similitud. Por ejemplo, las aves se pueden clasificar en bandadas según su posición física y qué tan cerca están unas de otras. Existen diversas formas de clasificar los datos y decidir cuántos grupos deben formarse. Elegir una clasificación en lugar de otra puede ser un problema difícil. En este artículo, comparamos técnicas de análisis de grupos para datos que son binarios.

El análisis de grupos es una técnica común para el análisis exploratorio de datos y se utiliza en campos como el reconocimiento de patrones, el aprendizaje automático y la bioinformática. Los algoritmos fueron desarrollados con la intención de ser versátiles, adecuados para cualquier tipo de datos. En este artículo, mostraremos que no todos los algoritmos son adecuados para clasificar un conjunto de datos determinado y demostraremos cómo la creación de un conjunto de datos simulado puede ser útil para elegir un algoritmo adecuado.

Identificamos más de una docena de funciones de análisis de grupos en el lenguaje estadístico R, provenientes de distintos autores. Este artículo no pretende ser una descripción o comparación exhaustiva de esas funciones, sino que se centra en mostrar la construcción del conjunto de datos simulado y los resultados de tantas funciones como sea posible.

La simulación generó 3 grupos de aproximadamente 333 puntos cada uno; el conjunto de datos resultante tenía 1000 puntos definidos en un espacio de 20 dimensiones binarias. Luego, se ejecutaron las funciones R para el análisis de grupos. Algunos algoritmos, como es el caso de agnes, nos obligaron a especificar el número de grupos; otros algoritmos, como ekmeans, lograron determinar el número de grupos por sí mismos. También encontramos que la mayoría de los algoritmos como agnes y ekmeans clasificaron el conjunto de datos en grupos de tamaño adecuado, mientras que otros algoritmos como dbscan y hc produjeron una cantidad incorrecta de grupos o tamaños incorrectos. Finalmente, algunos de los algoritmos no fueron fáciles de procesar e interpretar, debido a que no existen herramientas estadísticas o gráficas apropiadas para el análisis. Ese es el caso de mona, un algoritmo diseñado para trabajar solo con variables binarias. Tuvimos que usar algunas conjeturas para determinar el tamaño de los grupos, y aun así el análisis produjo los resultados equivocados.

En conclusión, encontramos que varios algoritmos no son adecuados para clasificar datos binarios. Los investigadores interesados en clasificar datos binarios necesitan hacer ensayos con conjuntos de datos simulados para elegir un algoritmo eficiente e ilustrativo que claramente muestre una clasificación correcta de dichos datos.

Opciones Para Análisis de Grupos

Ésta es una lista de funciones para hacer análisis de grupos en R. El paquete cluster ofrece agnes, clara, diana, fanny, mona, y pam. El paquete fpc ofrece dbscan. El paquete mclust ofrece mclust, hc, y hcvvv. El paquete pvclust ofrece pvclust, y el paquete stats ofrece hclust y kmeans.

El paquete factoextra ofrece versiones mejoradas de las funciones mencionadas anteriormente, con gráficos adicionales y determinación automática del número de grupos.

Requisitos e instalación

Ésta comparación requiere solamente una instalación de R con los paquetes mencionados anteriormente y unas utilidades adicionales. Puedes usar el siguiente comando para instalarlos:

1
2
install_packages(c("rlang", "data.table", "ggplot2", "Cairo",
"cowplot", "cluster", "factoextra", "fpc", "mclust", "pvclust"))

Comencemos el Análisis

Primero generamos una tabla de datos simulados y luego hacemos todos los análisis de grupos en ella, guardando los resultados para después.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
for (package_name in c("rlang", "data.table", "ggplot2", "Cairo",
"cowplot", "cluster", "factoextra", "fpc", "mclust", "pvclust")) {
  require(package_name, character.only = TRUE)
  message("Package ", package_name, " version ", packageVersion(package_name),
  " released ", packageDate(package_name))
}

# Ejemplo concreto para la tabla de datos:
# Nuestra tabla representa una colección de estudios científicos.
# Cada fila de la tabla representa una publicación.
# La publicación es un punto en el espacio de los datos.
# Cada columna de la tabla representa cierta metodología de investigación.
# Cada columna es una dimensión en el espacio de los datos.
# Cada valor de la tabla es un 0 o un 1:
#  1: La publicación utiliza la metodología dada
#  0: La publicación no usa dicha metodología.

message("Paso 1: Definiciones y preparación")
set.seed(1000)
ca <- list()
# Número de filas: número de publicaciones; puntos en el espacio de datos.
ca$n_papers <- 1000
ca$n_methodologies <- 20 # Número de columnas; dimensiones del espacio de datos
ca$n_clusters <- 3 # Número de grupos a generar
# Mínimo radio de un grupo en el espacio de los datos.
# Debe ser menos que sqrt(ca$n_methodologies).
ca$min_cluster_radius <- 1.0
# Máximo radio de un grupo en el espacio de los datos.
# Debe ser menos que sqrt(ca$n_methodologies).
ca$max_cluster_radius <- 1.6
tmp <- list()

stopifnot(ca$n_papers > 100)
stopifnot(ca$n_methodologies > 5)
stopifnot(ca$n_clusters > 1)
stopifnot(ca$min_cluster_radius >= 1)
stopifnot(ca$min_cluster_radius < ca$max_cluster_radius)
if (2 * ca$max_cluster_radius > sqrt(ca$n_methodologies)) {
  cat("Alerta: El radio del grupo debe ser pequeño. ",
  "Por favor revisar:\nca$radius =",
  ca$radius, "| Recomendación < 0.5 * sqrt(ca$n_methodologies =",
  0.5 * sqrt(ca$n_methodologies), "\n")
}

message("Paso 2: La tabla de datos comienza en ceros")
tmp$zeroes <- rep(0, ca$n_papers)
# Encabezado: methodología1, methodología2, etc.
tmp$methodologies <- paste0("m", 1:(ca$n_methodologies))
ca$dataset <- data.table()
ca$dataset[, tmp$methodologies := tmp$zeroes]

message(
  "Paso 3: Construimos grupos alrededor de los centros en el espacio de datos.")
ca$cluster_radii <-  runif(n = ca$n_clusters, min = ca$min_cluster_radius,
max = ca$max_cluster_radius)
ca$cluster_centers <- as.data.table(matrix(rbinom(n = ca$n_clusters *
ca$n_methodologies, size = 1, prob = 0.5), nrow = ca$n_clusters,
ncol = ca$n_methodologies))
setnames(ca$cluster_centers, tmp$methodologies)
tmp$limits <- 1 + as.integer(0:(ca$n_clusters - 1) * ca$n_papers /
ca$n_clusters) # Paper index at the start of each cluster
stopifnot(tmp$limits < ca$n_papers)
tmp$cluster_sizes <- diff(c(tmp$limits, ca$n_papers + 1))
ca$cluster_sizes <- data.table(Original =
sort(tmp$cluster_sizes))
# Esta función ordena la columna dada x y la agrega a ca$cluster_sizes,
# cambiando el tamaño si es necesario, evitando el reciclado de la tabla.
new_sizes <- function(x_name, x) {
  sx <- seq_along(x)
  if (length(x) > nrow(ca$cluster_sizes)) {
    ca$cluster_sizes <<- ca$cluster_sizes[sx]
  }
  ca$cluster_sizes[sx, c(x_name) := sort(x)]
}

tmp$limits <- c(tmp$limits, 0)
tmp$random_index <- sample.int(ca$n_papers)
tmp$limit <- 1
tmp$cluster_index <- 0
for (paper_index in 1:(ca$n_papers)) {
  if (paper_index == tmp$limit) {
    tmp$cluster_index <- tmp$cluster_index + 1
    if (tmp$cluster_index <= ca$n_clusters) {
      tmp$limit <- tmp$limits[tmp$cluster_index + 1]
      tmp$prob <- ca$cluster_radii[tmp$cluster_index] *
      ca$cluster_radii[tmp$cluster_index] / ca$n_methodologies
      tmp$center <- ca$cluster_centers[tmp$cluster_index]
      message("El grupo ", tmp$cluster_index, " tiene el tamaño ",
      tmp$cluster_sizes[tmp$cluster_index], ", radio ", tmp$prob,
      " y centro en el siguiente punto:")
      show(tmp$center)
    }
  }

  ca$dataset[tmp$random_index[paper_index], ] <- (tmp$center +
      rbinom(n = ca$n_methodologies, size = 1, prob = tmp$prob)
      # Un punto aleatorio cerca del centro se hace cambiando aleatoriamente algunas de las coordenadas
    ) %% 2
}
message("Aquí hay una pequeña parte de la tabla de datos:")
show(ca$dataset)

message("Paso 4: Hacemos los diferentes análisis de grupos y guardamos los resultados.")
ca$p_cluster <- list()
ca$p_fpc <- list()
ca$p_mclust <- list()
ca$p_pvclust <- list()
ca$p_stats <- list()

ca$p_cluster$agnes <- agnes(ca$dataset)
ca$p_cluster$agnes_cluster_sizes <- table(cutree(ca$p_cluster$agnes, k = 3))
new_sizes("agnes", ca$p_cluster$agnes_cluster_sizes)
ca$p_cluster$eagnes <- eclust(ca$dataset, "agnes", graph = FALSE)
ca$p_cluster$clara <- clara(ca$dataset, k = 3, samples = 100)
ca$p_cluster$clara_cluster_sizes <- ca$p_cluster$clara$clusinfo[, "size"]
new_sizes("clara", ca$p_cluster$clara_cluster_sizes)
ca$p_cluster$eclara <- eclust(ca$dataset, "clara", graph = FALSE, k = 3,
samples = 100)
ca$p_cluster$diana <- diana(ca$dataset)
ca$p_cluster$diana_cluster_sizes <- table(cutree(ca$p_cluster$diana, k = 3))
new_sizes("diana", ca$p_cluster$diana_cluster_sizes)
ca$p_cluster$ediana <- eclust(ca$dataset, "diana", graph = FALSE)
ca$p_cluster$fanny <- fanny(ca$dataset, k = 3, tol = 1e-6)
ca$p_cluster$fanny_cluster_sizes <- table(ca$p_cluster$fanny$clustering)
new_sizes("fanny", ca$p_cluster$fanny_cluster_sizes)
ca$p_cluster$efanny <- eclust(ca$dataset, "fanny", graph = FALSE, k = 3,
tol = 1e-6)
ca$p_cluster$mona <- mona(ca$dataset)
tmp$mona <- list()
tmp$mona$raw <- which(ca$p_cluster$mona$step == 20)
tmp$mona$indices <- c(0, tmp$mona$raw[4:5] - tmp$mona$raw[1], 1001)
ca$p_cluster$mona_cluster_sizes <- diff(tmp$mona$indices)
new_sizes("mona", ca$p_cluster$mona_cluster_sizes)
ca$p_cluster$pam <- pam(ca$dataset, k = 3)
ca$p_cluster$pam$pam_cluster_sizes <- ca$p_cluster$pam$clusinfo[, "size"]
new_sizes("pam", ca$p_cluster$pam$pam_cluster_sizes)
ca$p_cluster$epam <- eclust(ca$dataset, "pam", graph = FALSE, k = 3)
ca$p_fpc$dbscan <- dbscan(ca$dataset, eps = 0.000001, MinPts = 10)
ca$p_fpc$dbscan_cluster_sizes <- table(ca$p_fpc$dbscan$cluster)
new_sizes("dbscan", ca$p_fpc$dbscan_cluster_sizes)
ca$p_mclust$mclust <- Mclust(ca$dataset)
ca$p_mclust$mclust_cluster_sizes <- table(ca$p_mclust$mclust$classification)
new_sizes("mclust", ca$p_mclust$mclust_cluster_sizes)
ca$p_mclust$clust_combi <- clustCombi(ca$p_mclust$mclust, ca$dataset)
ca$p_mclust$hc <- hc(ca$dataset)
ca$p_mclust$hc_cluster_sizes <- table(hclass(ca$p_mclust$hc, 3))
new_sizes("hc", ca$p_mclust$hc_cluster_sizes)
ca$p_mclust$hcvvv <- hcVVV(ca$dataset)
ca$p_mclust$hcvvv_cluster_sizes <- table(hclass(ca$p_mclust$hcvvv, 3))
new_sizes("hcvvv", ca$p_mclust$hcvvv_cluster_sizes)
ca$p_pvclust$pvclust <- pvclust(t(ca$dataset), parallel = TRUE)
ca$p_pvclust$pvclust_cluster_sizes <- table(cutree(ca$p_pvclust$pvclust$hclust,
k = 3))
new_sizes("pvclust", ca$p_pvclust$pvclust_cluster_sizes)
# ca$p_stats$hclust <- hclust(ca$dataset) # No funcionó con nuestra tabla de datos
ca$p_stats$ehclust <- eclust(ca$dataset, "hclust", graph = FALSE)
ca$p_stats$ehclust_cluster_sizes <- table(cutree(ca$p_stats$ehclust, k = 3))
new_sizes("ehclust", ca$p_stats$ehclust_cluster_sizes)
# eclust("kmeans") puede producir alertas como "did not converge in 10 iterations"
ca$p_stats$ekmeans <- eclust(ca$dataset, "kmeans", graph = FALSE)
ca$p_stats$ekmeans_cluster_sizes <- table(ca$p_stats$ekmeans$cluster)
new_sizes("ekmeans", ca$p_stats$ekmeans_cluster_sizes)
ca$p_stats$kmeans <- kmeans(ca$dataset, centers = ca$cluster_centers)
ca$p_stats$kmeans_cluster_sizes <- table(ca$p_stats$kmeans$cluster)
new_sizes("kmeans", ca$p_stats$kmeans_cluster_sizes)

save(ca, file = "data.RData")
tmp <- NULL

Aquí están los resultados de nuestro código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
Loading required package: rlang
Package rlang version 1.0.2 released 2022-03-04
Loading required package: data.table
data.table 1.14.2 using 4 threads (see ?getDTthreads).  Latest news: r-datatable.com

Attaching package: 'data.table'

The following object is masked from 'package:rlang':

    :=

Package data.table version 1.14.2 released 2021-09-23
Loading required package: ggplot2
Package ggplot2 version 3.3.6 released 2022-04-27
Loading required package: Cairo
Package Cairo version 1.5.15 released 2022-03-16
Loading required package: cowplot
Package cowplot version 1.1.1 released 2020-12-15
Loading required package: cluster
Package cluster version 2.1.3 released 2022-03-28
Loading required package: factoextra
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
Package factoextra version 1.0.7 released 2020-04-01
Loading required package: fpc
Package fpc version 2.2.9 released 2020-12-06
Loading required package: mclust
    __  ___________    __  _____________
   /  |/  / ____/ /   / / / / ___/_  __/
  / /|_/ / /   / /   / / / /\__ \ / /
 / /  / / /___/ /___/ /_/ /___/ // /
/_/  /_/\____/_____/\____//____//_/    version 5.4.9
Type 'citation("mclust")' for citing this R package in publications.
Package mclust version 5.4.9 released 2021-12-17
Loading required package: pvclust
Package pvclust version 2.2.0 released 2019-11-19
Paso 1: Definiciones y preparación
Paso 2: La tabla de datos comienza en ceros
Paso 3: Construimos grupos alrededor de los centros en el espacio de datos.

El grupo 1 tiene el tamaño 333, radio 0.0716078026473276 y centro en el siguiente punto:
   m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
1:  1  1  0  0  0  0  1  1  1   1   1   0   0   0   1   0   1   1   1   0

El grupo 2 tiene el tamaño 333, radio 0.105896052986884 y centro en el siguiente punto:
   m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
1:  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0

El grupo 3 tiene el tamaño 334, radio 0.0570698508073141 y centro en el siguiente punto:
   m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
1:  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1

Aquí hay una pequeña parte de la tabla de datos:
      m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
   1:  1  1  1  1  0  1  0  1  0   1   1   1   1   1   1   1   0   1   0   0
   2:  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
   3:  1  1  0  0  0  0  1  1  1   1   1   0   1   0   0   0   1   1   1   1
   4:  1  1  0  0  0  0  1  1  1   1   0   0   0   0   1   0   1   1   1   0
   5:  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
  ---                                                                       
 996:  1  1  0  1  1  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
 997:  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1
 998:  1  1  1  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   0   1
 999:  1  1  0  1  0  1  0  1  0   0   1   0   1   1   0   1   1   1   1   0
1000:  1  1  0  0  0  0  1  1  1   1   1   0   0   1   1   0   1   0   1   0
Paso 4: Hacemos los diferentes análisis de grupos y guardamos los resultados.
Clustering k = 1,2,..., K.max (= 10): .. done
Bootstrapping, b = 1,2,..., B (= 100)  [one "." per sample]:
.................................................. 50 
.................................................. 100 
Clustering k = 1,2,..., K.max (= 10): .. done
Bootstrapping, b = 1,2,..., B (= 100)  [one "." per sample]:
.................................................. 50 
.................................................. 100 
fitting ...
  |====================================================================================================| 100%
Bootstrap (r = 0.5)... Done.
Bootstrap (r = 0.6)... Done.
Bootstrap (r = 0.7)... Done.
Bootstrap (r = 0.8)... Done.
Bootstrap (r = 0.9)... Done.
Bootstrap (r = 1.0)... Done.
Bootstrap (r = 1.1)... Done.
Bootstrap (r = 1.2)... Done.
Bootstrap (r = 1.3)... Done.
Bootstrap (r = 1.4)... Done.
Clustering k = 1,2,..., K.max (= 10): .. done
Bootstrapping, b = 1,2,..., B (= 100)  [one "." per sample]:
.................................................. 50 
.................................................. 100 
Clustering k = 1,2,..., K.max (= 10): .. done
Bootstrapping, b = 1,2,..., B (= 100)  [one "." per sample]:
.................................................. 50 
.................................................. 100 

We first compare the cluster analysis methods in the following table:

1
ca$cluster_sizes
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
   Original agnes clara diana fanny mona pam dbscan mclust  hc hcvvv pvclust
1:      333   333   333   311   333  262 333     10     87  76    76     333
2:      333   333   333   334   333  339 333     10    121 105   105     333
3:      334   334   334   355   334  400 334     10    121 819   819     334
4:       NA    NA    NA    NA    NA   NA  NA     12    132  NA    NA      NA
5:       NA    NA    NA    NA    NA   NA  NA     34    146  NA    NA      NA
6:       NA    NA    NA    NA    NA   NA  NA     76    195  NA    NA      NA
7:       NA    NA    NA    NA    NA   NA  NA    105    198  NA    NA      NA
   ehclust ekmeans kmeans
1:     332     333    333
2:     333     333    333
3:     335     334    334
4:      NA      NA     NA
5:      NA      NA     NA
6:      NA      NA     NA
7:      NA      NA     NA

Esta tabla muestra que los algoritmos dbscan y mclust no son apropiados para la clasificación de nuestros datos simulados, dado que identifican una gran cantidad de clústeres. Los algoritmos diana, mona, hc y hcvvv producen tamaños de grupo incorrectos.

Los otros algoritmos parecen producir grupos de tamaños apropiados. Tengamos en cuenta que en algunos casos el paquete factoextra fue útil para determinar automáticamente el número de clústeres, pero en algunos casos tuvimos que proporcionar el número de clústeres.

En las próximas secciones, mostraremos la salida gráfica de los diferentes tipos de análisis de conglomerados.

Paquete cluster

agnes - Anidado Aglomerativo (Agrupamiento Jerárquico)

1
2
3
4
5
ca$p_cluster$agnes_cluster_sizes
plot(ca$p_cluster$agnes)
fviz_cluster(ca$p_cluster$eagnes)    # Del paquete factoextra
fviz_dend(ca$p_cluster$eagnes)       # Del paquete factoextra
fviz_silhouette(ca$p_cluster$eagnes) # Del paquete factoextra
1
2
  1   2   3 
334 333 333

clara - Agrupamiento de Datos en k Grupos

1
2
3
ca$cluster_centers
ca$p_cluster$clara$medoids
ca$p_cluster$clara$clusinfo
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
   m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
1:  1  1  0  0  0  0  1  1  1   1   1   0   0   0   1   0   1   1   1   0
2:  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
3:  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1

     m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
[1,]  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
[2,]  1  1  0  0  0  0  1  1  1   1   1   0   0   0   1   0   1   1   1   0
[3,]  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1

     size max_diss   av_diss isolation
[1,]  333 2.645751 1.3260589 0.8366600
[2,]  333 2.236068 1.0075124 0.7071068
[3,]  334 2.449490 0.8338874 0.7745967
1
2
3
plot(ca$p_cluster$clara)
fviz_cluster(ca$p_cluster$eclara)    # Del paquete factoextra
fviz_silhouette(ca$p_cluster$eclara) # Del paquete factoextra

diana - Agrupamiento por Análisis Divisorio

1
2
3
4
5
6
ca$cluster_centers
ca$p_cluster$diana_cluster_sizes
plot(ca$p_cluster$diana)
fviz_cluster(ca$p_cluster$ediana)    # Del paquete factoextra
fviz_dend(ca$p_cluster$ediana)       # Del paquete factoextra
fviz_silhouette(ca$p_cluster$ediana) # Del paquete factoextra
1
2
3
4
5
6
7
   m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
1:  1  1  0  0  0  0  1  1  1   1   1   0   0   0   1   0   1   1   1   0
2:  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
3:  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1

  1   2   3 
355 311 334

fanny - Agrupamiento Difuso de Datos en k Grupos.

1
2
3
4
5
ca$cluster_centers
ca$p_cluster$fanny_cluster_sizes
plot(ca$p_cluster$fanny)
fviz_cluster(ca$p_cluster$efanny)    # Del paquete factoextra
fviz_silhouette(ca$p_cluster$efanny) # Del paquete factoextra
1
2
3
4
5
6
7
   m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
1:  1  1  0  0  0  0  1  1  1   1   1   0   0   0   1   0   1   1   1   0
2:  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
3:  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1

  1   2   3 
334 333 333

mona - Agrupamiento de Variables Binarias por Análisis Monotético

1
2
ca$p_cluster$mona_cluster_sizes
plot(ca$p_cluster$mona)
1
339 400 262

pam - Partición Alrededor de Medoides

1
2
3
4
5
6
ca$cluster_centers
ca$p_cluster$pam$medoids
ca$p_cluster$pam$clusinfo
plot(ca$p_cluster$pam)
fviz_cluster(ca$p_cluster$epam) # Del paquete factoextra
fviz_silhouette(ca$p_cluster$epam) # Del paquete factoextra
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
   m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
1:  1  1  0  0  0  0  1  1  1   1   1   0   0   0   1   0   1   1   1   0
2:  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
3:  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1

     m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
[1,]  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
[2,]  1  1  0  0  0  0  1  1  1   1   1   0   0   0   1   0   1   1   1   0
[3,]  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1

     size max_diss   av_diss diameter separation
[1,]  333 2.645751 1.3260589 3.464102   1.732051
[2,]  333 2.236068 1.0075124 3.162278   2.000000
[3,]  334 2.449490 0.8338874 3.162278   1.732051

Paquete fpc

dbscan - Agrupamiento por densidad, cercanía, y conectividad

1
2
3
4
ca$p_fpc$dbscan
summary(ca$p_fpc$dbscan)
plot(ca$p_fpc$dbscan, ca$dataset)
fviz_cluster(ca$p_fpc$dbscan, ca$dataset) # Del paquete factoextra
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
dbscan Pts=1000 MinPts=10 eps=1e-06

         0  1   2  3  4  5  6  7
border 743  0   0  0  0  0  0  0
seed     0 34 105 10 76 12 10 10
total  743 34 105 10 76 12 10 10

        Length Class  Mode   
cluster 1000   -none- numeric
eps        1   -none- numeric
MinPts     1   -none- numeric
isseed  1000   -none- logical

Paquete mclust

Mclust - Agrupamiento Basado en Modelos

1
2
3
4
5
6
ca$cluster_centers
summary(ca$p_mclust$mclust)
plot(ca$p_mclust$mclust)
fviz_cluster(ca$p_mclust$mclust) # Del paquete factoextra
fviz_mclust(ca$p_mclust$mclust) # Del paquete factoextra
plot(ca$p_mclust$clust_combi)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
   m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
1:  1  1  0  0  0  0  1  1  1   1   1   0   0   0   1   0   1   1   1   0
2:  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
3:  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1

---------------------------------------------------- 
Gaussian finite mixture model fitted by EM algorithm 
---------------------------------------------------- 

Mclust EEV (ellipsoidal, equal volume and shape) model with 7 components: 

 log-likelihood    n   df       BIC      ICL
       4143.117 1000 1496 -2047.769 -2193.05

Clustering table:
  1   2   3   4   5   6   7 
132 146  87 198 195 121 121

hc - Agrupamiento Jerárquico Aglomerativo Basado en Modelos

1
2
3
ca$cluster_centers
ca$p_mclust$hc_cluster_sizes
plot(ca$p_mclust$hc)
1
2
3
4
5
6
7
   m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
1:  1  1  0  0  0  0  1  1  1   1   1   0   0   0   1   0   1   1   1   0
2:  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
3:  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1

  1   2   3 
819 105  76

hcVVV - Agrupamiento Jerárquico Basado en Modelos

1
2
3
ca$cluster_centers
ca$p_mclust$hcvvv_cluster_sizes
plot(ca$p_mclust$hcvvv)
1
2
3
4
5
6
7
   m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
1:  1  1  0  0  0  0  1  1  1   1   1   0   0   0   1   0   1   1   1   0
2:  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
3:  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1

  1   2   3 
819 105  76

Paquete pvclust

pvclust - Agrupamiento Jerárquico con Valores p

1
2
3
4
ca$cluster_centers
ca$p_pvclust$pvclust_cluster_sizes
summary(ca$p_pvclust$pvclust)
plot(ca$p_pvclust$pvclust)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
   m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
1:  1  1  0  0  0  0  1  1  1   1   1   0   0   0   1   0   1   1   1   0
2:  1  1  0  1  0  1  0  1  0   0   1   1   1   1   1   1   0   1   1   0
3:  0  0  1  1  1  0  0  0  0   1   1   1   1   1   1   1   1   1   0   1

  1   2   3 
333 333 334

        Length Class           Mode   
hclust    7    hclust          list   
edges     9    data.frame      list   
count    10    data.frame      list   
msfit   999    mslist          list   
nboot    10    -none-          numeric
r        10    -none-          numeric
store    10    -none-          list   
version   1    package_version list

Paquete stats

hclust - Agrupamiento Jerárquico por Diferencias

1
2
3
4
5
ca$p_stats$ehclust_cluster_sizes
plot(ca$p_stats$ehclust)
fviz_cluster(ca$p_stats$ehclust)    # Del paquete factoextra
fviz_dend(ca$p_stats$ehclust)       # Del paquete factoextra
fviz_silhouette(ca$p_stats$ehclust) # Del paquete factoextra
1
2
  1   2   3 
332 333 335

kmeans - Agrupamiento por k Promedios

1
2
ca$p_stats$kmeans_cluster_sizes
fviz_cluster(ca$p_stats$kmeans, data = ca$dataset)    # Del paquete factoextra
1
2
  1   2   3 
333 333 334

1
2
3
ca$p_stats$ekmeans_cluster_sizes
fviz_cluster(ca$p_stats$ekmeans, data = ca$dataset)    # Del paquete factoextra
fviz_silhouette(ca$p_stats$ekmeans) # Del paquete factoextra
1
2
  1   2   3 
333 334 333

Discusión

Los diagramas de cluster presentados anteriormente, que se basan en técnicas de análisis de componentes principales, parecen ser herramientas visuales útiles para ayudarnos a comprender la clasificación de los datos en grupos. Dado que no es fácil imaginar un espacio de 20 dimensiones, la computadora se encarga de organizar los datos en un nuevo sistema de coordenadas (componentes principales), donde en la figura solo se muestran las dos dimensiones más importantes. Estas son las dos dimensiones a lo largo de las cuales la variabilidad de los datos es más prominente, lo que hace que las diferencias entre los 3 grupos sean más evidentes. Sugeriría que esta forma de visualizar los datos es más útil que los dendogramas y los gráficos de silueta, que intentan mostrar las diferencias entre los conglomerados de otras formas.

La salida de mona fue la más difícil de interpretar en mi opinión. Hay que identificar los tres grupos por nosotros mismos, después de asumir que la figura de salida se envuelve en la dirección vertical, lo cual no es intuitivo. Los índices de los datos están organizados de arriba a abajo, y debemos usar algunas conjeturas para identificar los índices que corresponden a los puntos límite a la derecha, que son los que se usan para saber dónde termina un grupo y comienza el siguiente. Lamentablemente, mona y factoextra no proporcionan gráficos de conglomerados, gráficos de banner, dendogramas o gráficos de silueta.

Conclusión

En lugar de limitarnos a las conjeturas involucradas en el análisis de mona, lo mejor que podemos hacer es dejar que R trate nuestro conjunto de datos binarios como datos de valor real y realice los estudios visuales enriquecidos que proporciona factoextra.

Los mejores métodos para nuestro conjunto de datos simulado son agnes, diana, hclust y kmeans mejorados a través de la función eclust del paquete factoextra. Diría que son los mejores porque determinan correctamente que la tabla de datos debe clasificarse en 3 grupos de aproximadamente 333 puntos cada uno, y también porque todos los métodos vienen con diagramas de cluster que facilitan el análisis y la discusión de la clasificación.

Créditos

Foto destacada: Bandada de aves, nubes, cielo por Mohamed Hassandisponible aquí. Con licencia de dominio público CC0, sin costo para uso personal y comercial, sin requerimientos de atribución.