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