Análisis de cluster. Códigos
Beatriz Valdez
2025-07-06
K-means
En este ejemplo trabajaremos con el conjunto de datos “USArrest” que está precargado en R.
# Cargar la base de datos USArrests
data(USArrests)
Inspecciono el conjunto de datos
# Mostrar las primeras filas de la base de datos
head(USArrests)
## Murder Assault UrbanPop Rape
## Alabama 13.2 236 58 21.2
## Alaska 10.0 263 48 44.5
## Arizona 8.1 294 80 31.0
## Arkansas 8.8 190 50 19.5
## California 9.0 276 91 40.6
## Colorado 7.9 204 78 38.7
También inspecciono el contenido completo
glimpse(USArrests)
## Rows: 50
## Columns: 4
## $ Murder <dbl> 13.2, 10.0, 8.1, 8.8, 9.0, 7.9, 3.3, 5.9, 15.4, 17.4, 5.3, 2.~
## $ Assault <int> 236, 263, 294, 190, 276, 204, 110, 238, 335, 211, 46, 120, 24~
## $ UrbanPop <int> 58, 48, 80, 50, 91, 78, 77, 72, 80, 60, 83, 54, 83, 65, 57, 6~
## $ Rape <dbl> 21.2, 44.5, 31.0, 19.5, 40.6, 38.7, 11.1, 15.8, 31.9, 25.8, 2~
Puede observarse que la magnitud en que están expresadas las variables no es homogénea. En ese caso es necesario aplicar cualquiera de los métodos conocidos para estandarizar las variables. Voy a emplear puntuaciones z. En R existe una función que ayuda con este procedimiento:
# Escalar los datos es crucial para que todas las variables tengan la misma importancia
# Esto evita que variables con rangos más grandes dominen el cálculo de distancias
# guardo el resultado en un objeto que llamare "df"
df <- scale(USArrests)
Inspecciono nuevamente
glimpse(df)
## num [1:50, 1:4] 1.2426 0.5079 0.0716 0.2323 0.2783 ...
## - attr(*, "dimnames")=List of 2
## ..$ : chr [1:50] "Alabama" "Alaska" "Arizona" "Arkansas" ...
## ..$ : chr [1:4] "Murder" "Assault" "UrbanPop" "Rape"
## - attr(*, "scaled:center")= Named num [1:4] 7.79 170.76 65.54 21.23
## ..- attr(*, "names")= chr [1:4] "Murder" "Assault" "UrbanPop" "Rape"
## - attr(*, "scaled:scale")= Named num [1:4] 4.36 83.34 14.47 9.37
## ..- attr(*, "names")= chr [1:4] "Murder" "Assault" "UrbanPop" "Rape"
El siguiente paso consiste en determinar el número óptimo de conglomerados (K). Para ello emplearé el “Método del codo (Elbow method)” para encontrar el K óptimo. Se calcula la suma total de cuadrados dentro de los conglomerados (tot.withinss) para diferentes valores de K.
set.seed(123) # Para reproducibilidad de los resultados aleatorios
wss <- (nrow(df)-1)*sum(apply(df,2,var))
for (i in 2:15) {
wss[i] <- sum(kmeans(df, centers=i, nstart=25)$withinss)
}
Grafico el método del codo:
plot(1:15, wss, type="b", xlab="Número de Clusters (K)",
ylab="Suma de Cuadrados Intra-Cluster",
main="Método del Codo para K-means en USArrests")
En este gráfico, se busca un “codo” donde la disminución de la suma de cuadrados intra-cluster se ralentiza significativamente. Esto sugiere un K apropiado. Para USArrests, K=4 es un punto de inflexión común.
Asumiendo K=4 por el método del codo, nstart=25 significa que el algoritmo se ejecutará 25 veces con diferentes centroides iniciales y se elegirá la mejor solución (la que tenga la menor suma de cuadrados intra-cluster total), lo que ayuda a evitar óptimos locales.
km.res <- kmeans(df, centers = 4, nstart = 25)
El siguiente paso consiste en interpretar los resultados
km.res
## K-means clustering with 4 clusters of sizes 13, 13, 8, 16
##
## Cluster means:
## Murder Assault UrbanPop Rape
## 1 -0.9615407 -1.1066010 -0.9301069 -0.96676331
## 2 0.6950701 1.0394414 0.7226370 1.27693964
## 3 1.4118898 0.8743346 -0.8145211 0.01927104
## 4 -0.4894375 -0.3826001 0.5758298 -0.26165379
##
## Clustering vector:
## Alabama Alaska Arizona Arkansas California
## 3 2 2 3 2
## Colorado Connecticut Delaware Florida Georgia
## 2 4 4 2 3
## Hawaii Idaho Illinois Indiana Iowa
## 4 1 2 4 1
## Kansas Kentucky Louisiana Maine Maryland
## 4 1 3 1 2
## Massachusetts Michigan Minnesota Mississippi Missouri
## 4 2 1 3 2
## Montana Nebraska Nevada New Hampshire New Jersey
## 1 1 2 1 4
## New Mexico New York North Carolina North Dakota Ohio
## 2 2 3 1 4
## Oklahoma Oregon Pennsylvania Rhode Island South Carolina
## 4 4 4 4 3
## South Dakota Tennessee Texas Utah Vermont
## 1 3 2 4 1
## Virginia Washington West Virginia Wisconsin Wyoming
## 4 4 1 1 4
##
## Within cluster sum of squares by cluster:
## [1] 11.952463 19.922437 8.316061 16.212213
## (between_SS / total_SS = 71.2 %)
##
## Available components:
##
## [1] "cluster" "centers" "totss" "withinss" "tot.withinss"
## [6] "betweenss" "size" "iter" "ifault"
Para evitar tener de un “solo golpe” todo el resultado se puede proceder de la siguiente manera:
#centroides
km.res$centers
## Murder Assault UrbanPop Rape
## 1 -0.9615407 -1.1066010 -0.9301069 -0.96676331
## 2 0.6950701 1.0394414 0.7226370 1.27693964
## 3 1.4118898 0.8743346 -0.8145211 0.01927104
## 4 -0.4894375 -0.3826001 0.5758298 -0.26165379
los centroides de los clusters (medias para cada variable en cada cluster) representan las características promedio de cada conglomerado.
Veamos ahora el tamaño de cada cluster:
km.res$size
## [1] 13 13 8 16
El tamaño de cada cluster es el número de individuos, en esta caso de estados, en cada conglomerado
Ahora, observamos a qué cluster pertenece cada estado:
km.res$cluster
## Alabama Alaska Arizona Arkansas California
## 3 2 2 3 2
## Colorado Connecticut Delaware Florida Georgia
## 2 4 4 2 3
## Hawaii Idaho Illinois Indiana Iowa
## 4 1 2 4 1
## Kansas Kentucky Louisiana Maine Maryland
## 4 1 3 1 2
## Massachusetts Michigan Minnesota Mississippi Missouri
## 4 2 1 3 2
## Montana Nebraska Nevada New Hampshire New Jersey
## 1 1 2 1 4
## New Mexico New York North Carolina North Dakota Ohio
## 2 2 3 1 4
## Oklahoma Oregon Pennsylvania Rhode Island South Carolina
## 4 4 4 4 3
## South Dakota Tennessee Texas Utah Vermont
## 1 3 2 4 1
## Virginia Washington West Virginia Wisconsin Wyoming
## 4 4 1 1 4
Lo ideal es ver esos clusters en el conjunto de datos inicial. Para ello procedo de la siguiente forma:
# Añadir la asignación de cluster al data frame original para un análisis más fácil
USArrests_clustered <- cbind(USArrests, cluster = km.res$cluster)
Inspecciono:
glimpse(USArrests_clustered)
## Rows: 50
## Columns: 5
## $ Murder <dbl> 13.2, 10.0, 8.1, 8.8, 9.0, 7.9, 3.3, 5.9, 15.4, 17.4, 5.3, 2.~
## $ Assault <int> 236, 263, 294, 190, 276, 204, 110, 238, 335, 211, 46, 120, 24~
## $ UrbanPop <int> 58, 48, 80, 50, 91, 78, 77, 72, 80, 60, 83, 54, 83, 65, 57, 6~
## $ Rape <dbl> 21.2, 44.5, 31.0, 19.5, 40.6, 38.7, 11.1, 15.8, 31.9, 25.8, 2~
## $ cluster <int> 3, 2, 2, 3, 2, 2, 4, 4, 2, 3, 4, 1, 2, 4, 1, 4, 1, 3, 1, 2, 4~
Ahora lo importante es visualizar este resultado:
# Se utiliza el paquete 'factoextra' para una visualización más rica. Si no está instalada, descomentar la siguiente línea:
# install.packages("factoextra")
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
fviz_cluster(km.res, data = df,
palette = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"), # Colores para los clusters
ellipse.type = "euclid", # Dibuja elipses alrededor de los clusters basadas en la distancia euclidiana
star.plot = TRUE, # Dibuja líneas desde cada punto a su centroide
repel = TRUE, # Evita la superposición de etiquetas de texto
ggtheme = theme_minimal(), # Tema del gráfico
main = "Clusters de Estados de EE. UU. (USArrests) segun criminalidad")
Discusión de resultados en contexto sociológico:
Analizar las características de los estados en cada conglomerado basándose en los centroides (tasas de arresto). Por ejemplo, si se observa el output de km.res$centers:
- Un cluster podría tener altas tasas en todas las categorías (estados con alta criminalidad general).
- Otro cluster podría tener bajas tasas en todas las categorías (estados con baja criminalidad).
- Un tercer cluster podría tener tasas altas de asalto pero bajas de asesinato y violación (patrón específico).
- Un cuarto cluster podría mostrar otro patrón distintivo.
Implicaciones para políticas públicas: ¿Cómo diseñar políticas de seguridad o programas de intervención social diferenciados para cada tipo de estado, basándose en sus patrones de criminalidad?
No hay comentarios.:
Publicar un comentario