1 Presentación La idea intuitiva de conglomerar es poder organizar los casos (filas) en subconjuntos o grupos, tal que la similitud entre casos justifique que pertenezca a un grupo y no a otro.

Traigamos algunos datos de los paises del mundo para el ejemplo de esta sesión:

Índice de Democracia - IDE (link). Índice de desarrollo humano - IDH (link). Estos datos, vendrán desde su origen con una serie de problemitas. En el caso del IDE, veamos que viene de esta manera:

WhereIDH='https://github.com/Estadistica-AnalisisPolitico/DataFiles-estadistica/raw/main/HDR21-22_Statistical_Annex_HDI_Table.xlsx'

#carga
idh  = rio::import(WhereIDH,skip=4,.name_repair='minimal')
head(idh, 15)%>%kbl()%>%
  kable_styling(bootstrap_options = "striped", font_size = 10)
Human Development Index (HDI) Life expectancy at birth Expected years of schooling Mean years of schooling Gross national income (GNI) per capita GNI per capita rank minus HDI rank HDI rank
HDI rank Country Value NA (years) NA (years) NA (years) NA (2017 PPP $) NA NA NA NA
NA NA 2021 NA 2021 NA 2021 a 2021 a 2021 NA 2021 b 2020
NA VERY HIGH HUMAN DEVELOPMENT NA NA NA NA NA NA NA NA NA NA NA NA NA
1 Switzerland 0.96199999999999997 NA 83.987200000000001 NA 16.50029945 NA 13.85966015 NA 66933.004539999994 NA 5 NA 3
2 Norway 0.96099999999999997 NA 83.233900000000006 NA 18.185199740000002 c 13.00362968 NA 64660.106220000001 NA 6 NA 1
3 Iceland 0.95899999999999996 NA 82.678200000000004 NA 19.163059230000002 c 13.76716995 NA 55782.049809999997 NA 11 NA 2
4 Hong Kong, China (SAR) 0.95199999999999996 NA 85.473399999999998 d 17.278169630000001 NA 12.22620964 NA 62606.845399999998 NA 6 NA 4
5 Australia 0.95099999999999996 NA 84.526499999999999 NA 21.054590229999999 c 12.726819989999999 NA 49238.433349999999 NA 18 NA 5
6 Denmark 0.94799999999999995 NA 81.375299999999996 NA 18.714799880000001 c 12.96049023 NA 60364.785949999998 NA 6 NA 5
7 Sweden 0.94699999999999995 NA 82.9833 NA 19.418529509999999 c 12.609720230000001 NA 54489.37401 NA 9 NA 9
8 Ireland 0.94499999999999995 NA 81.997600000000006 NA 18.94522095 c 11.58222303 e 76168.984429999997 f -3 NA 8
9 Germany 0.94199999999999995 NA 80.630099999999999 NA 17.010139469999999 NA 14.090966910000001 e 54534.216820000001 NA 6 NA 7
10 Netherlands 0.94099999999999995 NA 81.687299999999993 NA 18.693165220000001 c,e 12.581629749999999 NA 55979.411 NA 3 NA 10
11 Finland 0.94 NA 82.0381 NA 19.051929470000001 c 12.87362003 NA 49452.166720000001 NA 11 NA 12
12 Singapore 0.93899999999999995 NA 82.754499999999993 NA 16.524320599999999 NA 11.924880030000001 NA 90918.644709999993 f -10 NA 10

1.1 Selección En ambas tablas debemos hacer diversas operaciones de selección, renombramiento, y limpieza. Luego de ello, verifiquemos los tipos de datos en IDE:

#seleccionando columns
idh=idh[,c(2,3,5,7,9,11)]
demo=demo[,-c(1,2,6)]

# recombrando columns
newDemo=c("Pais","RegimeType","Score","Electoral","Functioning","participation","culture",'Civilliberties')
newIDH=c('Pais','puntuacion','EsperanzaVida','EscolaridadDuracion','EscolaridadPromedio','PBI')
names(demo)=newDemo
names(idh)=newIDH

#seleccionando filas
idh=idh[c(1:202),]
idh=idh[!is.na(idh$Pais),]

# tipo de datos
str(demo)
## 'data.frame':    167 obs. of  8 variables:
##  $ Pais          : chr  " Norway" " New Zealand" " Finland" " Sweden" ...
##  $ RegimeType    : chr  "Full democracy" "Full democracy" "Full democracy" "Full democracy" ...
##  $ Score         : chr  "9.75" "9.37" "9.27" "9.26" ...
##  $ Electoral     : chr  "10.00" "10.00" "10.00" "9.58" ...
##  $ Functioning   : chr  "9.64" "8.93" "9.29" "9.29" ...
##  $ participation : chr  "10.00" "9.44" "8.89" "8.33" ...
##  $ culture       : chr  "10.00" "8.75" "8.75" "10.00" ...
##  $ Civilliberties: chr  "9.12" "9.71" "9.41" "9.12" ...

Y para el caso de IDH tenemos estos tipos de datos:

str(idh)
## 'data.frame':    201 obs. of  6 variables:
##  $ Pais               : chr  "Country" "VERY HIGH HUMAN DEVELOPMENT" "Switzerland" "Norway" ...
##  $ puntuacion         : chr  "Value" NA "0.96199999999999997" "0.96099999999999997" ...
##  $ EsperanzaVida      : chr  "(years)" NA "83.987200000000001" "83.233900000000006" ...
##  $ EscolaridadDuracion: chr  "(years)" NA "16.50029945" "18.185199740000002" ...
##  $ EscolaridadPromedio: chr  "(years)" NA "13.85966015" "13.00362968" ...
##  $ PBI                : chr  "(2017 PPP $)" NA "66933.004539999994" "64660.106220000001" ...

1.2 Formateo Ambas tablas requieren convertir columnas de tipo texto a numérico, y una columns de tipo texto a categórica ordinal. Muchas veces, esta etapa de formateo produce valores perdidos, cuando algun valor no puede transformarse al tipo de dato requerido. La data de IDH tuvo ese problema, y el warning se muestra así (un warning cada vez que no se pudo transformar):

# formateo: texto a ordinal
OrdinalVector=c('Authoritarian','Hybrid regime','Flawed democracy','Full democracy')
demo$RegimeType=factor(demo$RegimeType,
                          levels = OrdinalVector,
                          ordered = T)

# for mateo: texto a numero
idh[,-1]=lapply(idh[,-1], as.numeric)
## Warning in lapply(idh[, -1], as.numeric): NAs introducidos por coerción

## Warning in lapply(idh[, -1], as.numeric): NAs introducidos por coerción

## Warning in lapply(idh[, -1], as.numeric): NAs introducidos por coerción

## Warning in lapply(idh[, -1], as.numeric): NAs introducidos por coerción

## Warning in lapply(idh[, -1], as.numeric): NAs introducidos por coerción
demo[,3:8]=lapply(demo[,3:8],as.numeric)

1.3 Valores perdidos Sabemos que IDH ha generado valores perdidos durante el formateo. Es importante saber si podemos recuperar alguno de ellos. Veamos dónde tenemos estos valores perdidos (**NA*s):

idh[!complete.cases(idh[,-1]),]%>%kbl()%>%
  kable_styling(bootstrap_options = "striped", font_size = 10)
Pais puntuacion EsperanzaVida EscolaridadDuracion EscolaridadPromedio PBI
1 Country NA NA NA NA NA
3 VERY HIGH HUMAN DEVELOPMENT NA NA NA NA NA
70 HIGH HUMAN DEVELOPMENT NA NA NA NA NA
120 MEDIUM HUMAN DEVELOPMENT NA NA NA NA NA
165 LOW HUMAN DEVELOPMENT NA NA NA NA NA
198 OTHER COUNTRIES OR TERRITORIES NA NA NA NA NA
199 Korea (Democratic People’s Rep. of) NA 73.2845 10.78317 NA NA
200 Monaco NA 85.9463 NA NA NA
201 Nauru NA 63.6170 11.69042 NA 17729.741
202 Somalia NA 55.2803 NA NA 1017.968

Podemos comprobar que en efecto esas filas no se usarán. De ahí que podemos eliminar esas filas.

idh=idh[complete.cases(idh[,-1]),]
row.names(idh)=NULL # resetear numero de filas

1.4 Merging Tenemos dos tablas, con la misma unidad de análisis (Pais). Pasemos a integrarlas en una sola. Como el campo común (la “key”) es Pais, asegurémonos que no haya espacios en blanco en sus alrededores.

idh$Pais= trimws(idh$Pais,whitespace = "[\\h\\v]")
demo$Pais= trimws(demo$Pais,whitespace = "[\\h\\v]") 

Como queremos integrar ambas, debemos investigar si hay países tiene en común pero que se han escrito diferente en cada tabla. Estos son los que no tiene IDE:

sort(setdiff(idh$Pais,demo$Pais))
##  [1] "Andorra"                            "Antigua and Barbuda"               
##  [3] "Bahamas"                            "Barbados"                          
##  [5] "Belize"                             "Bolivia (Plurinational State of)"  
##  [7] "Brunei Darussalam"                  "Cabo Verde"                        
##  [9] "Congo"                              "Congo (Democratic Republic of the)"
## [11] "Côte d'Ivoire"                      "Czechia"                           
## [13] "Dominica"                           "Eswatini (Kingdom of)"             
## [15] "Grenada"                            "Hong Kong, China (SAR)"            
## [17] "Iran (Islamic Republic of)"         "Kiribati"                          
## [19] "Korea (Republic of)"                "Lao People's Democratic Republic"  
## [21] "Liechtenstein"                      "Maldives"                          
## [23] "Marshall Islands"                   "Micronesia (Federated States of)"  
## [25] "Moldova (Republic of)"              "Palau"                             
## [27] "Palestine, State of"                "Russian Federation"                
## [29] "Saint Kitts and Nevis"              "Saint Lucia"                       
## [31] "Saint Vincent and the Grenadines"   "Samoa"                             
## [33] "San Marino"                         "Sao Tome and Principe"             
## [35] "Seychelles"                         "Solomon Islands"                   
## [37] "South Sudan"                        "Syrian Arab Republic"              
## [39] "Tanzania (United Republic of)"      "Timor-Leste"                       
## [41] "Tonga"                              "Türkiye"                           
## [43] "Tuvalu"                             "Vanuatu"                           
## [45] "Venezuela (Bolivarian Republic of)" "Viet Nam"

Estos son los que no tiene IDH:

sort(setdiff(demo$Pais,idh$Pais))
##  [1] "Bolivia"                          "Cape Verde"                      
##  [3] "Czech Republic"                   "Democratic Republic of the Congo"
##  [5] "East Timor"                       "Eswatini"                        
##  [7] "Hong Kong"                        "Iran"                            
##  [9] "Ivory Coast"                      "Laos"                            
## [11] "Moldova"                          "North Korea"                     
## [13] "Palestine"                        "Republic of the Congo"           
## [15] "Russia"                           "South Korea"                     
## [17] "Syria"                            "Taiwan"                          
## [19] "Tanzania"                         "Turkey"                          
## [21] "Venezuela"                        "Vietnam"

Como hay paises que sí están en ambos, pero que no se están escribiendo igual, podemos renombrar esas celdas. Aquí no será un proceso automático, pero podría serlo.

idh[idh$Pais=="Bolivia (Plurinational State of)",'Pais']= "Bolivia"
idh[idh$Pais=="Cabo Verde",'Pais']= "Cape Verde"
idh[idh$Pais=="Czechia",'Pais']= "Czech Republic"
idh[idh$Pais=="Congo (Democratic Republic of the)",'Pais']= "Democratic Republic of the Congo"
idh[idh$Pais=="Timor-Leste",'Pais']=  "East Timor"
idh[idh$Pais=="Eswatini (Kingdom of)",'Pais']= "Eswatini"
idh[idh$Pais=="Hong Kong, China (SAR)",'Pais']= "Hong Kong"
idh[idh$Pais=="Iran (Islamic Republic of)",'Pais']= "Iran"
idh[idh$Pais=="Côte d'Ivoire",'Pais']= "Ivory Coast"
idh[idh$Pais=="Lao People's Democratic Republic" ,'Pais']= "Laos"
idh[idh$Pais=="Moldova (Republic of)",'Pais']= "Moldova"
idh[idh$Pais=="Palestine, State of",'Pais']= "Palestine"
idh[idh$Pais=="Congo",'Pais']= "Republic of the Congo"
idh[idh$Pais=="Russian Federation",'Pais']=  "Russia"
idh[idh$Pais=="Korea (Republic of)",'Pais']= "South Korea"
idh[idh$Pais=="Syrian Arab Republic",'Pais']="Syria"
idh[idh$Pais=="Tanzania (United Republic of)",'Pais']= "Tanzania"
idh[idh$Pais=="Türkiye" ,'Pais']= "Turkey"
idh[idh$Pais=="Venezuela (Bolivarian Republic of)",'Pais']="Venezuela"
idh[idh$Pais=="Viet Nam" ,'Pais']="Vietnam"

Ahora sí, el merge no perderá tantas filas (países).

idhdemo=merge(idh,demo)

Si los tipos de datos se corrigieron antes, eso sería todo el proceso para producir una tabla integrada.

1.5 Exploración de datos Ahora, pasemos a describirlos estadísticamente:

summary(idhdemo)
##      Pais             puntuacion     EsperanzaVida   EscolaridadDuracion
##  Length:165         Min.   :0.3940   Min.   :52.53   Min.   : 6.957     
##  Class :character   1st Qu.:0.5860   1st Qu.:65.27   1st Qu.:11.468     
##  Mode  :character   Median :0.7310   Median :71.91   Median :13.644     
##                     Mean   :0.7204   Mean   :71.22   Mean   :13.607     
##                     3rd Qu.:0.8480   3rd Qu.:76.94   3rd Qu.:15.765     
##                     Max.   :0.9620   Max.   :85.47   Max.   :21.055     
##  EscolaridadPromedio      PBI                     RegimeType     Score      
##  Min.   : 2.115      Min.   :  731.8   Authoritarian   :58   Min.   :0.320  
##  1st Qu.: 5.916      1st Qu.: 4566.3   Hybrid regime   :34   1st Qu.:3.220  
##  Median : 9.424      Median :12578.2   Flawed democracy:53   Median :5.610  
##  Mean   : 8.926      Mean   :20108.4   Full democracy  :20   Mean   :5.284  
##  3rd Qu.:11.654      3rd Qu.:30690.5                         3rd Qu.:7.060  
##  Max.   :14.091      Max.   :90918.6                         Max.   :9.750  
##    Electoral       Functioning    participation       culture      
##  Min.   : 0.000   Min.   :0.000   Min.   : 0.000   Min.   : 1.250  
##  1st Qu.: 1.500   1st Qu.:2.710   1st Qu.: 3.890   1st Qu.: 3.750  
##  Median : 7.000   Median :5.000   Median : 5.560   Median : 5.000  
##  Mean   : 5.636   Mean   :4.623   Mean   : 5.401   Mean   : 5.389  
##  3rd Qu.: 9.170   3rd Qu.:6.430   3rd Qu.: 6.670   3rd Qu.: 6.250  
##  Max.   :10.000   Max.   :9.640   Max.   :10.000   Max.   :10.000  
##  Civilliberties 
##  Min.   :0.000  
##  1st Qu.:3.240  
##  Median :5.590  
##  Mean   :5.378  
##  3rd Qu.:7.650  
##  Max.   :9.710

Noten que los rangos no son los mismos para los componentes del IDH. Es muy común que tengamos diferentes unidades, por lo que debemos transformar los datos para evitar confundir a los algoritmos de conglomeración.

1.6 Transformación de datos Para este ejercicio sólo usaremos los componentes del IDH. La distribución de los componentes del IDH podemos verla en la Figura 1.1.

boxplot(idhdemo[,c(3:6)],horizontal = F,las=2,cex.axis = 0.5)

Figure 1.1: Distribución de los componentes del IDH

Como primera estrategia cambiemos sus rangos. Elijamos un rango del 0 al 1, cuyo resultado se ve en la Figura ??

library(BBmisc)
## 
## Attaching package: 'BBmisc'
## The following object is masked from 'package:base':
## 
##     isFALSE
boxplot(normalize(idhdemo[,c(3:6)],method='range',range=c(0,10)))

Figure 1.2: Distribución de los componentes del IDH con nuevo rango (0-1)

Una segunda estrategia sería tipificarla 1. El resultado se muestra en la Figura 1.3.

boxplot(normalize(idhdemo[,c(3:6)],method='standardize'))

Figure 1.3: Distribución de los componentes del IDH tipificados

Nos quedaremos con la segunda opción.

idhdemo[,c(3:6)]=normalize(idhdemo[,c(3:6)],method='standardize')

1.7 Correlación Veamos correlaciones entre estas variables tipificadas:

cor(idhdemo[,c(3:6)])
##                     EsperanzaVida EscolaridadDuracion EscolaridadPromedio
## EsperanzaVida           1.0000000           0.8057425           0.7659252
## EscolaridadDuracion     0.8057425           1.0000000           0.8159101
## EscolaridadPromedio     0.7659252           0.8159101           1.0000000
## PBI                     0.7838335           0.7311884           0.7139462
##                           PBI
## EsperanzaVida       0.7838335
## EscolaridadDuracion 0.7311884
## EscolaridadPromedio 0.7139462
## PBI                 1.0000000

Si hubiera alguna correlación negativa sería bueno invertir el rango, tal que el menor sea el mayor y viceversa. Esto no sucede aquí, por lo que no se hace ningún ajuste.

2 Preparación de los datos para la clusterización No podemos usar la columna Pais en la clusterización, pero tampoco debemos perderla, por lo que se recomienda usar esos nombres en lugar del nombre de fila.

dataClus=idhdemo[,c(3:6)]
row.names(dataClus)=idhdemo$Pais

Ya con los datos en el objeto dataClus, calculemos la matriz de distancias entre los casos (paises):

library(cluster)
g.dist = daisy(dataClus, metric="gower")

Hay diversas maneras de calculas matrices de distancias entre casos. Cuando las variables son todas numéricas, es comun usar la distancia Euclideana). Hay otras técnicas útiles como la Mahattan (revisar este debate). En nuestro caso, usaremos la distancia Gower útil cuando las variables (columnas) están de diversos tipos de escalas.

3 Procesos de clusterización Hay diversas estrategías de clusterización. Veremos dos de ellas:

La técnica de Partición La técnica de Jerarquización Jerarquización Aglomerativa Jerarquización Divisiva 3.1 Estrategia de Partición Como su nombre lo indica, la estrategia de partición busca partir los casos en grupos. El algoritmo básico establece puntos que deben atraer a los casos, tal que estos se separen. Claro está, que estos puntos atractores van moviendose conforme los grupos se van formando, hasta que al final se han partido todos los casos.

Hay diversos algoritmos que buscan una implementación de estos principios básicos. El más conocido es el de K-medias, pero para ciencias sociales tiene la desventaja que requiere que todas las variables sean numéricas, no siendo muy adecuado ante categorías. La alternativa a las necesidades en ciencias sociales es la técnica de k-medoides.

3.1.1 Decidir cantidad de clusters: La Figura 3.1 sirve para determinar la cantidad de clusters a solicitar (usando el estadístico gap).

## para PAM

library(factoextra)
## Loading required package: ggplot2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
fviz_nbclust(dataClus, pam,diss=g.dist,method = "gap_stat",k.max = 10,verbose = F)

Figure 3.1: Clusters sugeridos para algoritmo PAM.

3.1.2 Clusterizar via PAM: La técnica de k-medoides se implementa en la función pam. Esta función retorna diversos valores, en este caso crearemos una columna con la etiqueta del cluster. Usemos la sugerencia de la Figura 3.1, y hallamos:

set.seed(123)
res.pam=pam(g.dist,3,cluster.only = F)

#nueva columna
dataClus$pam=res.pam$cluster

# ver

head(dataClus,15)%>%kbl()%>%kable_styling()
EsperanzaVida EscolaridadDuracion EscolaridadPromedio PBI pam
Afghanistan -1.1720163 -1.1223433 -1.7899045 -0.9036118 1
Albania 0.6655042 0.2825071 0.7113515 -0.2954005 2
Algeria 0.6546035 0.3425723 -0.2580009 -0.4600137 2
Angola -1.2150350 -0.4816372 -1.0570320 -0.7236515 1
Argentina 0.5293798 1.4330952 0.6694140 0.0403686 2
Armenia 0.1046748 -0.1644630 0.7245624 -0.3434922 2
Australia 1.6888036 2.5007014 1.1453419 1.4396130 3
Austria 1.3148582 0.8062709 1.0036890 1.6560856 3
Azerbaijan -0.2350715 -0.0368326 0.4873350 -0.2891916 2
Bahrain 0.9571050 0.9035056 0.6390787 0.9582010 3
Bangladesh 0.1475666 -0.3910236 -0.4659696 -0.7233309 2
Belarus 0.1547871 0.5249118 0.9696084 -0.0622427 2
Belgium 1.3528009 2.0137324 1.0395414 1.5905903 3
Benin -1.4462954 -0.9535138 -1.3922662 -0.8252918 1
Bhutan 0.0757291 -0.1281194 -1.1314933 -0.5273580 1

3.1.3 Evaluando el uso de PAM Una manera práctica de ver el desempeño del algoritmo es calcular las silhouettes. Para el caso reciente, veamos la Figura 3.2.

fviz_silhouette(res.pam,print.summary = F)

Figure 3.2: Evaluando resultados de PAM

La Figura 3.2 muestra barras, donde cada una es un país (caso). Mientras más alta la barra, la pertenencia a ese cluster es clara. La barra negativa indica un país mal clusterizado. Para este caso, estos serían los mal clusterizados:

silPAM=data.frame(res.pam$silinfo$widths)
silPAM$country=row.names(silPAM)
poorPAM=silPAM[silPAM$sil_width<0,'country']%>%sort()
poorPAM
## [1] "Chile"  "Latvia"

3.1.4 Verificando etiqueta de clusters Exploremos el promedio de cada cluster:

aggregate(.~ pam, data=dataClus,mean)
##   pam EsperanzaVida EscolaridadDuracion EscolaridadPromedio        PBI
## 1   1    -1.0811743          -1.0681695          -1.1822831 -0.8111138
## 2   2     0.1340138           0.1819924           0.3393855 -0.2301917
## 3   3     1.2520905           1.1502463           1.0317146  1.5181170

El número asigando al cluster no tiene significado necesariamente, por lo que recomiendo dárselo. En este caso las etiquetas ascienden al igual que el promedio, por lo que no es necesario recodificar la etiqueta.

Antes de continuar, guardemos la columna de PAM en la data integrada, y eliminemosla de dataClus.

idhdemo$pamIDHpoor=idhdemo$Pais%in%poorPAM
idhdemo$pamIDH=as.ordered(dataClus$pam)
dataClus$pam=NULL

3.2 Estrategia Jerarquica La jerarquización busca clusterizar por etapas, hasta que todas las posibilidades de clusterizacion sean visible. Este enfoque tiene dos familias de algoritmos:

Aglomerativos Divisivos 3.2.1 Estrategia Aglomerativa En esta estrategia se parte por considerar cada caso (fila) como un cluster, para de ahi ir creando miniclusters hasta que todos los casos sean un solo cluster. El proceso va mostrando qué tanto esfuerzo toma juntar los elementos cluster tras cluster.

3.2.1.1 Decidir linkages Aunque se tiene la distancia entre elementos, tenemos que decidir como se irá calculando la distancia entre los clusters que se van formando (ya no son casos individuales). Los tres mas simples metodos:

Linkage tipo SINGLE.

Linkage tipo COMPLETE.

Linkage tipo AVERAGE

Otro metodo adicional, y muy eficiente, es el de Ward. Al final, lo que necesitamos saber cual de ellos nos entregará una mejor propuesta de clusters. Usemos este último para nuestro caso.

3.2.1.2 Decidir cantidad de Clusters La Figura 3.3 sirve para determinar la cantidad de clusters a solicitar (usando el estadístico gap).

## PARA JERARQUICO

fviz_nbclust(dataClus, hcut,diss=g.dist,method = "gap_stat",k.max = 10,verbose = F,hc_func = "agnes")

Figure 3.3: Clusters sugeridos para algoritmo AGNES.

3.2.1.3 Clusterizar vía AGNES La función hcut es la que usaremos para el método jerarquico, y el algoritmo aglomerativo se emplea usando agnes. El linkage será ward (aquí ward.D):

set.seed(123)
library(factoextra)

res.agnes<- hcut(g.dist, k = 3,hc_func='agnes',hc_method = "ward.D")

dataClus$agnes=res.agnes$cluster

# ver

head(dataClus,15)%>%kbl()%>%kable_styling()
EsperanzaVida EscolaridadDuracion EscolaridadPromedio PBI agnes
Afghanistan -1.1720163 -1.1223433 -1.7899045 -0.9036118 1
Albania 0.6655042 0.2825071 0.7113515 -0.2954005 2
Algeria 0.6546035 0.3425723 -0.2580009 -0.4600137 2
Angola -1.2150350 -0.4816372 -1.0570320 -0.7236515 1
Argentina 0.5293798 1.4330952 0.6694140 0.0403686 2
Armenia 0.1046748 -0.1644630 0.7245624 -0.3434922 2
Australia 1.6888036 2.5007014 1.1453419 1.4396130 3
Austria 1.3148582 0.8062709 1.0036890 1.6560856 3
Azerbaijan -0.2350715 -0.0368326 0.4873350 -0.2891916 2
Bahrain 0.9571050 0.9035056 0.6390787 0.9582010 2
Bangladesh 0.1475666 -0.3910236 -0.4659696 -0.7233309 2
Belarus 0.1547871 0.5249118 0.9696084 -0.0622427 2
Belgium 1.3528009 2.0137324 1.0395414 1.5905903 3
Benin -1.4462954 -0.9535138 -1.3922662 -0.8252918 1
Bhutan 0.0757291 -0.1281194 -1.1314933 -0.5273580 2

El dendograma de la Figura 3.4 nos muestra el proceso de conglomeración AGNES:

# Visualize
fviz_dend(res.agnes, cex = 0.7, horiz = T,main = "")
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

Figure 3.4: Dendograma de AGNES

El eje ‘Height’ nos muestra el “costo” de conglomerar: mientras más corta la distancia mayor similitud y la conglomeracion es más rápida.

3.2.1.4 Evaluando el uso de AGNES La Figura 3.5 nos muestra las silhouettes para AGNES.

fviz_silhouette(res.agnes,print.summary = F)

Figure 3.5: Evaluando resultados de AGNES

Nótese que también se presentan valores mal clusterizados. Los identificados son estos:

silAGNES=data.frame(res.agnes$silinfo$widths)
silAGNES$country=row.names(silAGNES)
poorAGNES=silAGNES[silAGNES$sil_width<0,'country']%>%sort()
poorAGNES
##  [1] "Bahrain"        "Bhutan"         "Cape Verde"     "Czech Republic"
##  [5] "Estonia"        "Greece"         "Kuwait"         "Lithuania"     
##  [9] "Poland"         "Portugal"       "Saudi Arabia"   "Spain"

3.2.1.5 Verificando etiqueta de clusters Exploremos el promedio de cada cluster:

aggregate(.~ agnes, data=dataClus,mean)
##   agnes EsperanzaVida EscolaridadDuracion EscolaridadPromedio         PBI
## 1     1    -1.1025984          -1.0855778          -1.1832237 -0.81636850
## 2     2     0.2356679           0.2867675           0.3801487 -0.08496801
## 3     3     1.3867428           1.2105608           1.1283409  1.76038883

Estas etiquetas no necesitan recodificación tampoco. Guardemos la columna de AGNES en la data integrada, y eliminemosla de dataClus.

idhdemo$agnesIDHpoor=idhdemo$Pais%in%poorAGNES
idhdemo$agnesIDH=as.ordered(dataClus$agnes)
dataClus$agnes=NULL

3.2.1.6 Comparando Veamos qué tanto se parece a la clasificación jerarquica a la de partición:

# verificar recodificacion
table(idhdemo$pamIDH,idhdemo$agnesIDH,dnn = c('Particion','Aglomeracion'))
##          Aglomeracion
## Particion  1  2  3
##         1 54  1  0
##         2  0 70  0
##         3  0 11 29

3.2.2 Estrategia Divisiva Esta estrategia comienza con todos los casos como un gran cluster; para de ahi dividir en clusters más pequeños.

3.2.2.1 Decidir Cantidad de Clusters La Figura 3.6 sirve para determinar la cantidad de clusters a solicitar (usando el estadístico gap).

## PARA JERARQUICO

fviz_nbclust(dataClus, hcut,diss=g.dist,method = "gap_stat",k.max = 10,verbose = F,hc_func = "diana")

Figure 3.6: Clusters sugeridos para algoritmo DIANA

3.2.2.2 Clusterizar vía DIANA La función hcut es la que usaremos para el método jerarquico, y el algoritmo divisivo se emplea usando diana. Aquí una muestra del resultado:

set.seed(123)
res.diana <- hcut(g.dist, k = 4,hc_func='diana')
dataClus$diana=res.diana$cluster
# veamos
head(dataClus,15)%>%kbl%>%kable_styling()
EsperanzaVida EscolaridadDuracion EscolaridadPromedio PBI diana
Afghanistan -1.1720163 -1.1223433 -1.7899045 -0.9036118 1
Albania 0.6655042 0.2825071 0.7113515 -0.2954005 2
Algeria 0.6546035 0.3425723 -0.2580009 -0.4600137 2
Angola -1.2150350 -0.4816372 -1.0570320 -0.7236515 1
Argentina 0.5293798 1.4330952 0.6694140 0.0403686 2
Armenia 0.1046748 -0.1644630 0.7245624 -0.3434922 2
Australia 1.6888036 2.5007014 1.1453419 1.4396130 3
Austria 1.3148582 0.8062709 1.0036890 1.6560856 3
Azerbaijan -0.2350715 -0.0368326 0.4873350 -0.2891916 2
Bahrain 0.9571050 0.9035056 0.6390787 0.9582010 3
Bangladesh 0.1475666 -0.3910236 -0.4659696 -0.7233309 4
Belarus 0.1547871 0.5249118 0.9696084 -0.0622427 2
Belgium 1.3528009 2.0137324 1.0395414 1.5905903 3
Benin -1.4462954 -0.9535138 -1.3922662 -0.8252918 1
Bhutan 0.0757291 -0.1281194 -1.1314933 -0.5273580 4

El dendograma de la Figura 3.7 nos muestra el proceso de conglomeración AGNES:

# Visualize
fviz_dend(res.diana, cex = 0.7, horiz = T, main = "")
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

Figure 3.7: Dendograma de DIANA

3.2.2.3 Evaluando el uso de DIANA La Figura 3.8 nos muestra las silhouettes para DIANA.

fviz_silhouette(res.diana,print.summary = F) 

Figure 3.8: Evaluando resultados de DIANA

Nótese que también se presentan valores mal clusterizados. Los identificados son estos

silDIANA=data.frame(res.diana$silinfo$widths)
silDIANA$country=row.names(silDIANA)
poorDIANA=silDIANA[silDIANA$sil_width<0,'country']%>%sort()
poorDIANA
## [1] "Azerbaijan"   "Ecuador"      "Mongolia"     "Turkmenistan"

3.2.2.4 Verificando Etiqueta Exploremos el promedio de cada cluster:

aggregate(.~ diana, data=dataClus,mean)
##   diana EsperanzaVida EscolaridadDuracion EscolaridadPromedio          PBI
## 1     1    -1.1287230          -1.1169561         -1.24824598 -0.823370585
## 2     2     0.3137105           0.4546312          0.57266780 -0.006070452
## 3     3     1.3052423           1.1835769          1.01701723  1.586748700
## 4     4    -0.1965156          -0.2767992         -0.04875179 -0.539435372

Aquí vemos que las etiquetas no muestran un orden. Este sería el orden:

original=aggregate(.~ diana, data=dataClus,mean)
original[order(original$EsperanzaVida),]
##   diana EsperanzaVida EscolaridadDuracion EscolaridadPromedio          PBI
## 1     1    -1.1287230          -1.1169561         -1.24824598 -0.823370585
## 4     4    -0.1965156          -0.2767992         -0.04875179 -0.539435372
## 2     2     0.3137105           0.4546312          0.57266780 -0.006070452
## 3     3     1.3052423           1.1835769          1.01701723  1.586748700

Esas posiciones hay que usarlas para recodificar:

dataClus$diana=dplyr::recode(dataClus$diana, `1` = 1, `4`=2,`2`=3,`3`=4)

Guardemos la columna de DIANA en la data integrada, y eliminemosla de dataClus.

idhdemo$dianaIDHpoor=idhdemo$Pais%in%poorDIANA
idhdemo$dianaIDH=as.ordered(dataClus$diana)
dataClus$diana=NULL

4 Visualización comparativa Vamos a usar la matriz de distancia para darle a cada país una coordenada, tal que la distancia entre esos paises se refleje en sus posiciones. Eso requiere una técnica que proyecte las dimensiones originales en un plano bidimensional. Para ello usaremos la técnica llamada escalamiento multidimensional. Veams algunas coordenadas

# k es la cantidad de dimensiones
proyeccion = cmdscale(g.dist, k=2,add = T) 
head(proyeccion$points,20)
##                                [,1]        [,2]
## Afghanistan             0.444400179  0.13113731
## Albania                -0.147175274 -0.13701510
## Algeria                -0.023300723 -0.09169449
## Angola                  0.331282576  0.03588831
## Argentina              -0.236409012 -0.08313538
## Armenia                -0.048973926 -0.16503890
## Australia              -0.529876811  0.16845260
## Austria                -0.429300483  0.11885998
## Azerbaijan             -0.006636829 -0.16267282
## Bahrain                -0.325596910  0.02662569
## Bangladesh              0.138226916 -0.08817999
## Belarus                -0.165689524 -0.13349112
## Belgium                -0.494630520  0.16277289
## Benin                   0.419354114  0.10895568
## Bhutan                  0.172062052 -0.04766684
## Bolivia                 0.067447666 -0.09021494
## Bosnia and Herzegovina -0.094332269 -0.15218399
## Botswana                0.109290126 -0.06847570
## Brazil                 -0.025209702 -0.10957750
## Bulgaria               -0.118824558 -0.14195524

Habiendo calculado la proyeccción, recuperemos las coordenadas del mapa del mundo basado en nuestras dimensiones nuevas:

# data frame prep:
idhdemo$dim1 <- proyeccion$points[,1]
idhdemo$dim2 <- proyeccion$points[,2]

Aquí puedes ver el mapa:

library(ggrepel)
base= ggplot(idhdemo,aes(x=dim1, y=dim2,label=row.names(dataClus))) 
base + geom_text_repel(size=3, max.overlaps = 50,min.segment.length = unit(0, 'lines'))

Coloreemos el mapa anterior segun el cluster al que corresponden.

4.1 Gráfica de PAM

# solo paises mal clusterizados
PAMlabels=ifelse(idhdemo$pamIDHpoor,idhdemo$Pais,'')

#base
base= ggplot(idhdemo,aes(x=dim1, y=dim2))  +
    scale_color_brewer(type = 'qual',palette ='Dark2'  ) + labs(subtitle = "Se destacan los países mal clusterizados")

pamPlot=base + geom_point(size=3, 
                          aes(color=pamIDH))  + 
        labs(title = "PAM") 
# hacer notorios los paises mal clusterizados
pamPlot + geom_text_repel(size=4,
                          aes(label=PAMlabels),
                          max.overlaps = 50,
                          min.segment.length = unit(0, 'lines'))

Figure 4.1: Conglomera dos PAM en Mapa Bidimensonal de países

4.2 Gráfica de AGNES

# solo paises mal clusterizados
AGNESlabels=ifelse(idhdemo$agnesIDHpoor,idhdemo$Pais,'')

agnesPlot=base + geom_point(size=3, 
                            aes(color=as.factor(agnesIDH))) +
          labs(title = "AGNES") 
# hacer notorios los paises mal clusterizados
agnesPlot + geom_text_repel(size=4,
                            aes(label=AGNESlabels),
                            max.overlaps = 50,
                            min.segment.length = unit(0, 'lines'))

Figure 4.2: Conglomerados AGNES en Mapa Bidimensonal de países

4.3 Gráfica de DIANA

# solo paises mal clusterizados
DIANAlabels=ifelse(idhdemo$dianaIDHpoor,idhdemo$Pais,'')

dianaPlot=base + geom_point(size=3,
                            aes(color=dianaIDH)) + 
          labs(title = "DIANA")

# hacer notorios los paises mal clusterizados
dianaPlot + geom_text_repel(size=4,
                            aes(label=DIANAlabels), 
                            max.overlaps = 50,
                            min.segment.length = unit(0, 'lines'))

Figure 4.3: Conglomerados DIANA en Mapa Bidimensonal de países

Nota que en estas técnicas (partición y jerarquica) todo elemento termina siendo parte de un cluster.