6.5 Ejemplo con Datos demográficos

Warning

Este cuaderno está realizando con código R

  • Datos: censo irlandés, area de Dublín, año 2011

  • Algoritmo SOM en R

  • Visualización de los resultados

Referencia online

Los códigos R y los datos originales están en el repositorio GIT

Los datos cocinados en: dataIrelandPopulationSOM.csv

Carga y descripción de los datos

require(kohonen)
Loading required package: kohonen
data<-read.csv("./data/dataIrelandPopulationSOM.csv")
typeof(data)
'list'
head(data,10)
A data.frame: 10 × 15
Xidavr_ageavr_household_sizeavr_education_levelavr_num_carsavr_healthrented_percentunemployment_percentinternet_percentsingle_percentmarried_percentseparated_percentdivorced_percentwidow_percent
<int><chr><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
11568726712302340.028112.5247523.0384621.03960404.385542 6.93069315.34391571.0000053.4136533.333334.81927712.81124505.6224900
21389526701600135.673663.3206113.5977011.98373984.509434 4.87804912.46105972.9508249.4172544.988341.39860140.23310023.9627040
31389626701600235.882353.3243244.2953021.90540544.596639 1.35135110.40462483.7837847.4789943.697483.36134450.00000005.4621849
41372926700203438.516673.0886083.8717951.73076924.530172 3.896104 8.10810878.9473747.0833348.333330.83333331.25000002.5000000
51372426700202924.678003.5120003.9337351.11200004.51034520.80000021.81070081.3008167.1201826.984132.26757371.58730162.0408163
61373926700204425.139533.1363644.4968551.10909094.41916229.35779824.15458981.9047664.2441930.232561.45348843.48837210.5813953
71372526700203025.058823.4411764.7268721.55882354.68988823.529412 8.68055694.1176555.5555636.819173.70370372.83224401.0893246
81371026700201524.437332.8615383.9735100.96923084.580556 4.68750019.45701481.2500070.4000021.333332.93333333.46666671.8666667
91371226700201723.505302.9587634.6742421.39175264.68592120.61855714.04494491.7525862.5441732.862192.47349821.76678450.3533569
101371326700201824.673552.9285714.5307691.40476194.57983214.81481510.89743682.9268366.5289329.752071.65289261.65289260.4132231
names(data)
  1. 'X'
  2. 'id'
  3. 'avr_age'
  4. 'avr_household_size'
  5. 'avr_education_level'
  6. 'avr_num_cars'
  7. 'avr_health'
  8. 'rented_percent'
  9. 'unemployment_percent'
  10. 'internet_percent'
  11. 'single_percent'
  12. 'married_percent'
  13. 'separated_percent'
  14. 'divorced_percent'
  15. 'widow_percent'
dim(data)
  1. 4806
  2. 15

Selección de datos y tratamiento

Hacemos una subselección de las variables al conjunto 2,4,5,8.

Centramos y escalamos las variables para que tengan igual importancia durante el proceso SOM

Convertimos los datos a una matriz

c(2,4,5,8)+1
  1. 3
  2. 5
  3. 6
  4. 9
names(data)[c(2,4,5,8)+1]
  1. 'avr_age'
  2. 'avr_education_level'
  3. 'avr_num_cars'
  4. 'unemployment_percent'
data_train <- data[,c(2,4,5,8)+1]
head(data_train,10)
A data.frame: 10 × 4
avr_ageavr_education_levelavr_num_carsunemployment_percent
<dbl><dbl><dbl><dbl>
140.028113.0384621.039604015.343915
235.673663.5977011.983739812.461059
335.882354.2953021.905405410.404624
438.516673.8717951.7307692 8.108108
524.678003.9337351.112000021.810700
625.139534.4968551.109090924.154589
725.058824.7268721.5588235 8.680556
824.437333.9735100.969230819.457014
923.505304.6742421.391752614.044944
1024.673554.5307691.404761910.897436
data_train_matrix <- as.matrix(scale(data_train))
#''''''

pairs(~avr_age+avr_education_level+avr_num_cars+unemployment_percent,data=data,
   main="Simple Scatterplot Matrix")
_images/06.05_SOM_EjemploPoblacionIrlanda_13_0.png

Definir y entrenar el SOM

Creamos el SOM grid con topología hexagonal, y tamaño \(20\times20\).

Entrenamos el SOM usando el grid anterior. Usamos las opciones por defecto del paquete.

som_grid <- somgrid(xdim = 20, ydim=20, topo="hexagonal")
som_model <- som(data_train_matrix, 
    grid=som_grid, 
    rlen=500, 
    alpha=c(0.05,0.01), 
    keep.data = TRUE )

Visualizar los datos

coolBlueHotRed <- function(n, alpha = 1) {
  rainbow(n, end=4/6, alpha=alpha)[n:1]
}

pretty_palette <- c("#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')

Progreso del entrenamiento

A medida que avanza el entrenamiento, la distancia de los pesos de cada nodo a los inputs asociados a dicho nodo se reduce. Idealmente esta distancia debería alcanzar un plateau mínimo.

Si la curva sigue decreciendo quizás son necesarias más iteraciones o cambiar los parámetros de las funciones de aprendizaje y núcleo de vecindad.

plot(som_model, type = "changes")
_images/06.05_SOM_EjemploPoblacionIrlanda_20_0.png

Conteo por nodo

Visualizar el número de datos de entrada que están asociados a cada nodo en el mapa.

Esto puede ser usado como una medida de la calidad del mapa:

  • valores altos en ciertas áreas del mapa sugieren que un mapa más grande puede ser adecuado

  • muchos nodos vacíos indican que el mapa es demasiado grande

plot(som_model, type = "counts", main="Node Counts", palette.name=coolBlueHotRed)
_images/06.05_SOM_EjemploPoblacionIrlanda_22_0.png
plot(som_model, type="mapping", main = "Mapeo de puntos", palette.name=coolBlueHotRed)
_images/06.05_SOM_EjemploPoblacionIrlanda_23_0.png
plot(som_model, type = "quality", main="Node Quality/Distance", palette.name=coolBlueHotRed)
_images/06.05_SOM_EjemploPoblacionIrlanda_24_0.png

U-matrix

La \(U\)-matrix es una representación de las distancias entre los nodos.

Áreas donde la distancia entre vecinos sea baja indican grupos de nodos que son similares.

Áreas con grandes distancias entre nodos marcan diferencias e indican fronteras naturales entre los clusters de nodos.

plot(som_model, type="dist.neighbours", main = "SOM neighbour distances", palette.name=grey.colors)
_images/06.05_SOM_EjemploPoblacionIrlanda_26_0.png

Mápa de códigos

En el siguiente mapa, para cada nodo, se representa un diagrama con información de su vector prototipo. En este caso son valors normalizados de los valores originales y, por tanto, se pueden comparar.

Cada vector de pesos de cada nodo es representativo (similar) a los de los datos que están asociados a dicho nodo.

Esta visualización permite enconrtar patrones en la distribución de muestras y variables.

Existe varias opciones para realizar esta presentación.

plot(som_model, type = "codes")
Warning message in par(opar):
“argument 1 does not name a graphical parameter”
_images/06.05_SOM_EjemploPoblacionIrlanda_28_1.png

Mapas de calor: heatmaps

Los mapas de calor adquieren una especial relevancia en los SOM. El enfoque anterior de presentar los mapas de códigos para cada nodo se vuelve inviable para datos en alta dimensión.

Un mapa de calor permite visualizar la distribución de una variable sobre el mapa.

Recordatorio: los datos de entrada están fijados a su nodo correspondiente, no se mueven. Lo que visualizamos es la distribución de cada uno de sus atributos en el mapa.

var <- 2
plot(som_model, 
     type = "property", 
     property = getCodes(som_model)[,var], 
     main=colnames(getCodes(som_model))[var], 
     palette.name=coolBlueHotRed
)
_images/06.05_SOM_EjemploPoblacionIrlanda_30_0.png

La visualización anterior es de la versión normalizada de la variable de interés. Podemos desnormalizarla.

var <- 2 
var_unscaled <- aggregate(as.numeric(data_train[,var]), by=list(som_model$unit.classif), FUN=mean, simplify=TRUE)[,2]
plot(som_model, type = "property", property=var_unscaled, main=names(data_train)[var], palette.name=coolBlueHotRed)
rm(var_unscaled)
_images/06.05_SOM_EjemploPoblacionIrlanda_32_0.png

Podemos hacer varios mapas de calor y acumularlos para compararlos visualmente

par(mfrow=c(2,2))
for(var in c(1,2,3,4)){
plot(som_model, 
     type = "property", 
     property = getCodes(som_model)[,var], 
     main=colnames(getCodes(som_model))[var], 
     palette.name=coolBlueHotRed
)       
}
_images/06.05_SOM_EjemploPoblacionIrlanda_34_0.png

Clustering de los resultados

Podemos realizar un algoritmo de clustering sobre el conjunto de los vectores prototipo de los nodos.

Realizamos una gráfica para averiguar el número de clusters a través de sucesivas aplicaciones de \(k-means\) usando la métrica dada por el WCSS

Después realizamos un hierarchical clustering hclust con el número óptimo que nos indica la gráfica.

Añadimos finalmente la información de los clusters al mapa SOM.

mydata <- getCodes(som_model)
wss <- (nrow(mydata)-1)*sum(apply(mydata,2,var))
for (i in 2:15) wss[i] <- sum(kmeans(mydata,
                                     centers=i)$withinss)
par(mar=c(5.1,4.1,4.1,2.1))
plot(1:15, wss, type="b", xlab="Number of Clusters",
     ylab="Within groups sum of squares", main="Within cluster sum of squares (WCSS)")
_images/06.05_SOM_EjemploPoblacionIrlanda_36_0.png
som_cluster <- cutree(hclust(dist(getCodes(som_model))), 6)
plot(som_model, type="mapping", bgcol = pretty_palette[som_cluster], main = "Clusters")
add.cluster.boundaries(som_model, som_cluster)
_images/06.05_SOM_EjemploPoblacionIrlanda_38_0.png
plot(som_model, type="codes", bgcol = pretty_palette[som_cluster], main = "Clusters")
add.cluster.boundaries(som_model, som_cluster)
Warning message in par(opar):
“argument 1 does not name a graphical parameter”
_images/06.05_SOM_EjemploPoblacionIrlanda_39_1.png

Clústeres sobre el mapa topográfico

Si los datos contienen información GIS, es posible representar qué puntos del mapa topográfico de Dublín están relacionados con qué clústeres.

fishy

Fig. 37 Geolocalización de los clústeres hallados