CARATULA

UNIVERSIDAD DE EL SALVADOR
FACULTAD DE CIENCIAS ECONÓMICAS
ESCUELA DE ECONOMÍA
CICLO II - 2025

“ANÁLISIS DE CLÚSTER (CONGLOMERADOS)”

ASIGNATURA: MÉTODOS PARA EL ANÁLISIS ECONÓMICO
CATEDRÁTICO: MSF. CARLOS ADEMIR PERÉZ ALAS
GRUPO TEÓRICO: 01

EL EQUIPO DE TRABAJO ESTA CONFORMADO POR:

NOMBRE CARNET
HENRY GEOVANNY VENTURA VÁSQUEZ VV19010
JEFFERSON ELIEZER VENTURA MARTINEZ VM21050
NOÉ ALEXANDER SÁNCHEZ TRUJILLO ST21011

ANÁLISIS DE CONGLOMERADOS

El análisis de conglomerados (o cluster analysis) es una técnica estadística no supervisada que permite agrupar un conjunto de objetos —como países, productos o individuos— en grupos homogéneos, llamados conglomerados, según su similitud en diversas variables. El objetivo es que los elementos dentro de cada grupo sean lo más parecidos posible entre sí, y a la vez, distintos de los de otros grupos (Kassambara (2017)). Esta metodología identifica patrones o estructuras subyacentes en los datos sin utilizar categorías predefinidas, lo que la hace particularmente útil en áreas como segmentación de mercados, estudios socioeconómicos y clasificación territorial según indicadores de desarrollo. El valor principal del análisis de conglomerados reside en su capacidad para revelar agrupaciones naturales que emergen directamente de los datos, sin supuestos iniciales sobre la estructura de los grupos.

CUADRO COMPARATIVO

Análisis de cluster

Técnicas disponibles

Ventajas

Desventajas

Jerárquico: Método que construye una jerarquía de clústeres, ya sea aglomerando (unión) o dividiendo (división) los datos de forma sucesiva, representada comúnmente en un dendrograma.

● Aglomerativo: Cada punto inicia como un clúster que se fusiona sucesivamente.

● Divisivo: Todos los puntos inician en un clúster que se divide sucesivamente.

● No requiere especificar previamente el número de clústeres.

● El dendrograma proporciona una visualización intuitiva de la estructura jerárquica de los datos.

● Es útil para datos pequeños o cuando se busca una exploración visual.

● Computacionalmente costoso para conjuntos de datos grandes.

● Los errores en las primeras uniones o divisiones se propagan en la jerarquía.

● Generalmente es menos eficiente que los métodos no jerárquicos en datos extensos.

No jerárquico (Particional):

Método que asigna directamente los datos a un número predefinido de clústeres, optimizando una función objetivo, como la minimización de la varianza intra-clúster.

● K-Medias: Asigna observaciones al clúster de la media más cercana.

● K-Medoides: Similar a K-Medias, pero usa observaciones reales (medoides) como centroides, es más robusto a outliers.

● Clustering Espectral: Utiliza los eigenvectores de una matriz de similitud para realizar la agrupación.

● Altamente eficiente y escalable para grandes volúmenes de datos.

● Produce clústeres más compactos y de forma esférica cuando se usa con distancias euclidianas.

● Menos complejidad computacional en comparación con los métodos jerárquicos.

● Requiere definir de antemano el número de clústeres (K).

● Sensible a la inicialización de los centroides, pudiendo converger a óptimos locales.

● Sensible a outliers y a la escala de las variables.

 

Fuente: Elaboración propia con base en Kassambara (2017)

TECNICAS PARA REALIZAR EL ANALISIS DE CLUSTER

MÉTODOS JERÁRQUICOS

Clustering Jerárquico Aglomerativo

Para qué sirve: Construye un árbol de clústeres fusionando progresivamente los grupos más similares, permitiendo visualizar relaciones a múltiples niveles.

Funciones principales:

Calcula matriz de distancias entre observaciones

dist_matrix <- dist(datos, method = “euclidean”)

Ejecuta clustering jerárquico con método Ward

hc <- hclust(dist_matrix, method = “ward.D2”)

Corta el dendrograma en k grupos específicos

clusters <- cutree(hc, k = 4)

Crea dendrograma visual con colores y rectángulos

fviz_dend(hc, k = 4, palette = “jco”, rect = TRUE)

Qué hace cada función:

· dist(): Calcula la matriz de distancias entre todas las observaciones usando métricas como euclidiana, manhattan o correlación

· hclust(): Construye la estructura jerárquica fusionando clusters basado en métodos como Ward, completo o promedio

· cutree(): Divide el dendrograma en un número específico de clusters

· fviz_dend(): Genera visualizaciones profesionales del dendrograma con colores y anotaciones

AGNES (Agglomerative Nesting)

Para qué sirve: Implementación mejorada del clustering jerárquico aglomerativo con más opciones de métricas y métodos.

Ejecuta AGNES con método Ward

res.agnes <- agnes(datos, metric = “euclidean”, method = “ward”)

Visualiza el resultado

fviz_dend(res.agnes, k = 4, palette = “jco”)

DIANA (Divisive Analysis)

Para qué sirve: Enfoque divisivo que comienza con un cluster único y lo divide sucesivamente.

Aplica algoritmo divisivo DIANA

res.diana <- diana(datos, metric = “euclidean”)

Visualiza dendrograma divisivo

fviz_dend(res.diana, k = 4, palette = “jco”)

MÉTODOS NO JERÁRQUICOS

K-MEANS

Para qué sirve: Divide datos en k grupos basándose en centroides, minimizando la varianza intra-cluster. Ideal para datos esféricos y clusters de tamaño similar.

Funciones principales:

Estandariza los datos para que todas las variables contribuyan igualmente

datos_escalados <- scale(datos)

Determina número óptimo de clusters usando método del codo

fviz_nbclust(datos_escalados, kmeans, method = “wss”)

Ejecuta K-Means con 25 inicializaciones aleatorias para evitar óptimos locales

km_res <- kmeans(datos_escalados, centers = 4, nstart = 25)

Visualiza clusters en espacio reducido (PCA automático)

fviz_cluster(km_res, data = datos_escalados, palette = “jco”)

Qué hace cada función:

· scale(): Estandariza variables restando media y dividiendo por desviación estándar · fviz_nbclust(): Evalúa calidad de clustering para diferentes números de clusters · kmeans(): Implementa el algoritmo iterativo de asignación y actualización de centroides · fviz_cluster(): Proyecta clusters en componentes principales para visualización 2D/3D

K-MEDOIDES (PAM)

Para qué sirve: Similar a K-Means pero usa observaciones reales como centros, siendo más robusto a outliers y ruido.

Determina k óptimo usando método de silueta

fviz_nbclust(datos_escalados, pam, method = “silhouette”)

Aplica PAM con distancia euclidiana

pam_res <- pam(datos_escalados, k = 4, metric = “euclidean”)

Visualiza resultados mostrando los medoides

fviz_cluster(pam_res, palette = “jco”, repel = TRUE)

CLARA (Clustering Large Applications)

Para qué sirve: Versión escalable de PAM para datasets grandes, trabajando con muestras en lugar del dataset completo.

Aplica CLARA con 50 muestras para datasets grandes

clara_res <- clara(datos_escalados, k = 4, samples = 50, pamLike = TRUE)

Visualiza clustering de grandes volúmenes eficientemente

fviz_cluster(clara_res, palette = “jco”, geom = “point”)

DBSCAN (Density-Based Spatial Clustering)

Para qué sirve: Identifica clusters basándose en densidad de puntos, detectando automáticamente outliers y manejando formas arbitrarias.

Encuentra parámetro eps óptimo analizando distancias a k-vecinos

dbscan::kNNdistplot(datos_escalados, k = 5) abline(h = 0.15, col = “red”, lty = 2)

Aplica DBSCAN con radio 0.15 y mínimo 5 puntos por cluster

db_res <- dbscan(datos_escalados, eps = 0.15, minPts = 5)

Visualiza clusters de densidad variable

fviz_cluster(db_res, data = datos_escalados, stand = FALSE)

FUZZY CLUSTERING (FCM)

Para qué sirve: Asigna grados de pertenencia a múltiples clusters, ideal cuando los límites entre grupos son difusos.

Aplica Fuzzy C-Means con 4 clusters

fanny_res <- fanny(datos_escalados, k = 4)

Muestra coeficientes de membresía (0-1) para cada observación

head(fanny_res$membership)

Evalúa calidad con coeficiente de Dunn

fanny_res$coeff

Visualiza clusters difusos

fviz_cluster(fanny_res, palette = “jco”)

MODEL-BASED CLUSTERING

Para qué sirve: Asume que los datos vienen de mezclas de distribuciones Gaussianas, seleccionando automáticamente modelo y número de clusters.

Aplica clustering basado en modelos Gaussianos

mc <- Mclust(datos_escalados)

Muestra modelo seleccionado y número de clusters óptimo

summary(mc)

Visualiza criterio BIC para selección de modelo

fviz_mclust(mc, “BIC”, palette = “jco”)

Muestra clasificación final

fviz_mclust(mc, “classification”, palette = “jco”)

HIERARCHICAL K-MEANS

Para qué sirve: Combina estabilidad de métodos jerárquicos con eficiencia de K-Means, mejorando inicialización de centroides.

Aplica hierarchical K-Means con 4 clusters

hk_res <- hkmeans(datos_escalados, 4)

Visualiza dendrograma del proceso jerárquico

fviz_dend(hk_res, palette = “jco”, rect = TRUE)

Muestra clusters finales

fviz_cluster(hk_res, palette = “jco”)

VALIDACIÓN Y COMPARACIÓN

Determinación de Número Óptimo de Clusters

Método del codo - busca punto de inflexión en varianza intra-cluster

fviz_nbclust(datos, kmeans, method = “wss”)

Método de silueta - maximiza cohesión y separación entre clusters

fviz_nbclust(datos, kmeans, method = “silhouette”)

Gap statistic - compara con distribución de referencia

fviz_nbclust(datos, kmeans, method = “gap_stat”)

NbClust - evalúa 30 índices simultáneamente

nb <- NbClust(datos, method = “kmeans”) fviz_nbclust(nb)

Evaluación de Calidad

Calcula métricas de validación interna

stats <- cluster.stats(dist(datos), km_res$cluster)

Índice de Dunn - mayor valor indica mejor separación

stats$dunn

Silueta promedio - valores cercanos a 1 indican buena calidad

stats$avg.silwidth

Estadísticas completas de compactación y separación

print(stats)

Comparación de Algoritmos

Compara múltiples métodos simultáneamente

clmethods <- c(“hierarchical”, “kmeans”, “pam”) comparacion <- clValid(datos, nClust = 2:6, clMethods = clmethods, validation = “internal”)

Muestra ranking de algoritmos por diferentes métricas

summary(comparacion)

Extrae mejores configuraciones

optimalScores(comparacion)

EJEMPLOS

CAPITULO 4

Data

data("USArrests") # Loading the data set
df <- scale(USArrests) # Scaling the data
# View the firt 3 rows of the data
head(df, n = 3)
##             Murder   Assault   UrbanPop         Rape
## Alabama 1.24256408 0.7828393 -0.5209066 -0.003416473
## Alaska  0.50786248 1.1068225 -1.2117642  2.484202941
## Arizona 0.07163341 1.4788032  0.9989801  1.042878388

librerias a utilizar

library(factoextra)

Estimación del número óptimo de clústeres

Número óptimo de clústere

fviz_nbclust(df, kmeans, method = "wss") +
geom_vline(xintercept = 4, linetype = 2)

Cálculo de la agrupación k-means

El código R que se muestra a continuación realiza una agrupación k-means con k = 4:

#Compute k-means with k = 4
set.seed(123)
km.res <- kmeans(df, 4, nstart = 25)
print(km.res)
## K-means clustering with 4 clusters of sizes 8, 13, 16, 13
## 
## Cluster means:
##       Murder    Assault   UrbanPop        Rape
## 1  1.4118898  0.8743346 -0.8145211  0.01927104
## 2 -0.9615407 -1.1066010 -0.9301069 -0.96676331
## 3 -0.4894375 -0.3826001  0.5758298 -0.26165379
## 4  0.6950701  1.0394414  0.7226370  1.27693964
## 
## Clustering vector:
##        Alabama         Alaska        Arizona       Arkansas     California 
##              1              4              4              1              4 
##       Colorado    Connecticut       Delaware        Florida        Georgia 
##              4              3              3              4              1 
##         Hawaii          Idaho       Illinois        Indiana           Iowa 
##              3              2              4              3              2 
##         Kansas       Kentucky      Louisiana          Maine       Maryland 
##              3              2              1              2              4 
##  Massachusetts       Michigan      Minnesota    Mississippi       Missouri 
##              3              4              2              1              4 
##        Montana       Nebraska         Nevada  New Hampshire     New Jersey 
##              2              2              4              2              3 
##     New Mexico       New York North Carolina   North Dakota           Ohio 
##              4              4              1              2              3 
##       Oklahoma         Oregon   Pennsylvania   Rhode Island South Carolina 
##              3              3              3              3              1 
##   South Dakota      Tennessee          Texas           Utah        Vermont 
##              2              1              4              3              2 
##       Virginia     Washington  West Virginia      Wisconsin        Wyoming 
##              3              3              2              2              3 
## 
## Within cluster sum of squares by cluster:
## [1]  8.316061 11.952463 16.212213 19.922437
##  (between_SS / total_SS =  71.2 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"

Es posible calcular la media de cada variable por clústeres utilizando los datos originales:

aggregate(USArrests, by=list(cluster=km.res$cluster), mean)
##   cluster   Murder   Assault UrbanPop     Rape
## 1       1 13.93750 243.62500 53.75000 21.41250
## 2       2  3.60000  78.53846 52.07692 12.17692
## 3       3  5.65625 138.87500 73.87500 18.78125
## 4       4 10.81538 257.38462 76.00000 33.19231

Si quieres añadir las clasificaciones de puntos a los datos originales, usa esto:

dd <- cbind(USArrests, cluster = km.res$cluster)
head(dd)
##            Murder Assault UrbanPop Rape cluster
## Alabama      13.2     236       58 21.2       1
## Alaska       10.0     263       48 44.5       4
## Arizona       8.1     294       80 31.0       4
## Arkansas      8.8     190       50 19.5       1
## California    9.0     276       91 40.6       4
## Colorado      7.9     204       78 38.7       4

Acceso a los resultados de la función kmeans()

Se puede acceder a estos componentes de la siguiente manera:

# Cluster number for each of the observations
km.res$cluster
##        Alabama         Alaska        Arizona       Arkansas     California 
##              1              4              4              1              4 
##       Colorado    Connecticut       Delaware        Florida        Georgia 
##              4              3              3              4              1 
##         Hawaii          Idaho       Illinois        Indiana           Iowa 
##              3              2              4              3              2 
##         Kansas       Kentucky      Louisiana          Maine       Maryland 
##              3              2              1              2              4 
##  Massachusetts       Michigan      Minnesota    Mississippi       Missouri 
##              3              4              2              1              4 
##        Montana       Nebraska         Nevada  New Hampshire     New Jersey 
##              2              2              4              2              3 
##     New Mexico       New York North Carolina   North Dakota           Ohio 
##              4              4              1              2              3 
##       Oklahoma         Oregon   Pennsylvania   Rhode Island South Carolina 
##              3              3              3              3              1 
##   South Dakota      Tennessee          Texas           Utah        Vermont 
##              2              1              4              3              2 
##       Virginia     Washington  West Virginia      Wisconsin        Wyoming 
##              3              3              2              2              3
# Cluster size
km.res$size
## [1]  8 13 16 13
# Cluster means
km.res$centers
##       Murder    Assault   UrbanPop        Rape
## 1  1.4118898  0.8743346 -0.8145211  0.01927104
## 2 -0.9615407 -1.1066010 -0.9301069 -0.96676331
## 3 -0.4894375 -0.3826001  0.5758298 -0.26165379
## 4  0.6950701  1.0394414  0.7226370  1.27693964

Visualización de clústeres k-means

Parcela de conglomerado

fviz_cluster(km.res, data = df,
palette = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
ellipse.type = "euclid", # Concentration ellipse
star.plot = TRUE, # Add segments from centroids to items
repel = TRUE, # Avoid label overplotting (slow)
ggtheme = theme_minimal()
)

CAPITULO 5

Cálculo de PAM en R data

data("USArrests") # Load the data set
df <- scale(USArrests) # Scale the data
head(df, n = 3) # View the firt 3 rows of the data
##             Murder   Assault   UrbanPop         Rape
## Alabama 1.24256408 0.7828393 -0.5209066 -0.003416473
## Alaska  0.50786248 1.1068225 -1.2117642  2.484202941
## Arizona 0.07163341 1.4788032  0.9989801  1.042878388

Required R packages and functions

library(cluster)
library(factoextra)

Estimación del número óptimo de clústeres

La función R fviz_nbclust() [paquete factoextra] proporciona una solución conveniente para estimar el número óptimo de clústeres.

fviz_nbclust(df, pam, method = "silhouette")+
theme_classic()

Cálculo de la agrupación PAM

El código R que se muestra a continuación calcula el algoritmo PAM con k = 2:

pam.res <- pam(df, 2)
print(pam.res)
## Medoids:
##            ID     Murder    Assault   UrbanPop       Rape
## New Mexico 31  0.8292944  1.3708088  0.3081225  1.1603196
## Nebraska   27 -0.8008247 -0.8250772 -0.2445636 -0.5052109
## Clustering vector:
##        Alabama         Alaska        Arizona       Arkansas     California 
##              1              1              1              2              1 
##       Colorado    Connecticut       Delaware        Florida        Georgia 
##              1              2              2              1              1 
##         Hawaii          Idaho       Illinois        Indiana           Iowa 
##              2              2              1              2              2 
##         Kansas       Kentucky      Louisiana          Maine       Maryland 
##              2              2              1              2              1 
##  Massachusetts       Michigan      Minnesota    Mississippi       Missouri 
##              2              1              2              1              1 
##        Montana       Nebraska         Nevada  New Hampshire     New Jersey 
##              2              2              1              2              2 
##     New Mexico       New York North Carolina   North Dakota           Ohio 
##              1              1              1              2              2 
##       Oklahoma         Oregon   Pennsylvania   Rhode Island South Carolina 
##              2              2              2              2              1 
##   South Dakota      Tennessee          Texas           Utah        Vermont 
##              2              1              1              2              2 
##       Virginia     Washington  West Virginia      Wisconsin        Wyoming 
##              2              2              2              2              2 
## Objective function:
##    build     swap 
## 1.441358 1.368969 
## 
## Available components:
##  [1] "medoids"    "id.med"     "clustering" "objective"  "isolation" 
##  [6] "clusinfo"   "silinfo"    "diss"       "call"       "data"

Si quieres añadir las clasificaciones de puntos a los datos originales, usa esto:

dd <- cbind(USArrests, cluster = pam.res$cluster)
head(dd, n = 3)
##         Murder Assault UrbanPop Rape cluster
## Alabama   13.2     236       58 21.2       1
## Alaska    10.0     263       48 44.5       1
## Arizona    8.1     294       80 31.0       1

Acceso a los resultados de la función pam()

Se puede acceder a estos componentes de la siguiente manera:

# Cluster medoids: New Mexico, Nebraska
pam.res$medoids
##                Murder    Assault   UrbanPop       Rape
## New Mexico  0.8292944  1.3708088  0.3081225  1.1603196
## Nebraska   -0.8008247 -0.8250772 -0.2445636 -0.5052109
#Cluster numbers
head(pam.res$clustering)
##    Alabama     Alaska    Arizona   Arkansas California   Colorado 
##          1          1          1          2          1          1

Visualización de clústeres PAM

fviz_cluster(pam.res,
palette = c("#00AFBB", "#FC4E07"), # color palette
ellipse.type = "t", # Concentration ellipse
repel = TRUE, # Avoid label overplotting (slow)
ggtheme = theme_classic()
)

CAPITULO 6

CLARA - Agrupación de aplicaciones de gran tamaño

Data format and preparation

Aquí generaremos un conjunto de datos aleatorios. Para que el resultado sea reproducible, comenzaremos

utilizando la función set.seed().

set.seed(1234)
# Generate 500 objects, divided into 2 clusters.
df <- rbind(cbind(rnorm(200,0,8), rnorm(200,0,8)),
cbind(rnorm(300,50,8), rnorm(300,50,8)))
# Specify column and row names
colnames(df) <- c("x", "y")
rownames(df) <- paste0("S", 1:nrow(df))
# Previewing the data
head(df, nrow = 6)
##             x        y
## S1  -9.656526 3.881815
## S2   2.219434 5.574150
## S3   8.675529 1.484111
## S4 -18.765582 5.605868
## S5   3.432998 2.493448
## S6   4.048447 6.083699

Paquetes y funciones de R necesarios

library(cluster)
library(factoextra)

Estimación del número óptimo de clústeres

library(cluster)
library(factoextra)
fviz_nbclust(df, clara, method = "silhouette")+
theme_classic()

Cálculo de CLARA

El código R que se muestra a continuación calcula el algoritmo PAM con k = 2:

# Compute CLARA
clara.res <- clara(df, 2, samples = 50, pamLike = TRUE)
# Print components of clara.res
print(clara.res)
## Call:     clara(x = df, k = 2, samples = 50, pamLike = TRUE) 
## Medoids:
##              x         y
## S121 -1.531137  1.145057
## S455 48.357304 50.233499
## Objective function:   9.87862
## Clustering vector:    Named int [1:500] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ...
##  - attr(*, "names")= chr [1:500] "S1" "S2" "S3" "S4" "S5" "S6" "S7" ...
## Cluster sizes:            200 300 
## Best sample:
##  [1] S37  S49  S54  S63  S68  S71  S76  S80  S82  S101 S103 S108 S109 S118 S121
## [16] S128 S132 S138 S144 S162 S203 S210 S216 S231 S234 S249 S260 S261 S286 S299
## [31] S304 S305 S312 S315 S322 S350 S403 S450 S454 S455 S456 S465 S488 S497
## 
## Available components:
##  [1] "sample"     "medoids"    "i.med"      "clustering" "objective" 
##  [6] "clusinfo"   "diss"       "call"       "silinfo"    "data"

Si quieres añadir las clasificaciones de puntos a los datos originales, usa esto:

dd <- cbind(df, cluster = clara.res$cluster)
head(dd, n = 4)
##             x        y cluster
## S1  -9.656526 3.881815       1
## S2   2.219434 5.574150       1
## S3   8.675529 1.484111       1
## S4 -18.765582 5.605868       1

Puedes acceder a los resultados devueltos por clara() de la siguiente manera:

# Medoids
clara.res$medoids
##              x         y
## S121 -1.531137  1.145057
## S455 48.357304 50.233499
# Clustering
head(clara.res$clustering, 10)
##  S1  S2  S3  S4  S5  S6  S7  S8  S9 S10 
##   1   1   1   1   1   1   1   1   1   1

Los medoides son S121, S455.

Visualización de grupos CLARA

fviz_cluster(clara.res,
palette = c("#00AFBB", "#FC4E07"), # color palette
ellipse.type = "t", # Concentration ellipse
geom = "point", pointsize = 1,
ggtheme = theme_classic()
)

CAPITULO 7

Estructura y preparación de datos.

# Load the data
data("USArrests")
# Standardize the data
df <- scale(USArrests)
# Show the first 6 rows
head(df, nrow = 6)
##                Murder   Assault   UrbanPop         Rape
## Alabama    1.24256408 0.7828393 -0.5209066 -0.003416473
## Alaska     0.50786248 1.1068225 -1.2117642  2.484202941
## Arizona    0.07163341 1.4788032  0.9989801  1.042878388
## Arkansas   0.23234938 0.2308680 -1.0735927 -0.184916602
## California 0.27826823 1.2628144  1.7589234  2.067820292
## Colorado   0.02571456 0.3988593  0.8608085  1.864967207

Medidas de similitud

#Compute the dissimilarity matrix
# df = the standardized data
res.dist <- dist(df, method = "euclidean")

El código R que aparece a continuación muestra las primeras 6 filas y columnas de la matriz de distancias:

as.matrix(res.dist)[1:6, 1:6]
##             Alabama   Alaska  Arizona Arkansas California Colorado
## Alabama    0.000000 2.703754 2.293520 1.289810   3.263110 2.651067
## Alaska     2.703754 0.000000 2.700643 2.826039   3.012541 2.326519
## Arizona    2.293520 2.700643 0.000000 2.717758   1.310484 1.365031
## Arkansas   1.289810 2.826039 2.717758 0.000000   3.763641 2.831051
## California 3.263110 3.012541 1.310484 3.763641   0.000000 1.287619
## Colorado   2.651067 2.326519 1.365031 2.831051   1.287619 0.000000

Linkage

res.hc <- hclust(d = res.dist, method = "ward.D2")

Dendrograma

# cex: label size
library("factoextra")
fviz_dend(res.hc, cex = 0.5)

Verifique el árbol del clúster

# Compute cophentic distance
res.coph <- cophenetic(res.hc)
# Correlation between cophenetic distance and
# the original distance
cor(res.dist, res.coph)
## [1] 0.6975266
res.hc2 <- hclust(res.dist, method = "average")
cor(res.dist, cophenetic(res.hc2))
## [1] 0.7180382

Divide el dendrograma en diferentes grupos

# Cut tree into 4 groups
grp <- cutree(res.hc, k = 4)
head(grp, n = 4)
##  Alabama   Alaska  Arizona Arkansas 
##        1        2        2        3
# Number of members in each cluster
table(grp)
## grp
##  1  2  3  4 
##  7 12 19 12
# Get the names for the members of cluster 1
rownames(df)[grp == 1]
## [1] "Alabama"        "Georgia"        "Louisiana"      "Mississippi"   
## [5] "North Carolina" "South Carolina" "Tennessee"

El resultado de los cortes se puede visualizar fácilmente utilizando la función fviz_dend() [en factoextra]

# Cut in 4 groups and color by groups
fviz_dend(res.hc, k = 4, # Cut in four groups
cex = 0.5, # label size
k_colors = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
color_labels_by_k = TRUE, # color labels by groups
rect = TRUE # Add rectangle around groups
)

Mediante la función fviz_cluster() [en factoextra], también podemos visualizar el resultado en un diagrama de dispersión. Las observaciones se representan mediante puntos en el diagrama, utilizando componentes principales. Se dibuja un marco alrededor de cada clúster.

fviz_cluster(list(data = df, cluster = grp),
palette = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
ellipse.type = "convex", # Concentration ellipse
repel = TRUE, # Avoid label overplotting (slow)
show.clust.cent = FALSE, ggtheme = theme_minimal())

Paquete de clúster R

Las funciones se pueden ejecutar de la siguiente manera:

library("cluster")
# Agglomerative Nesting (Hierarchical Clustering)
res.agnes <- agnes(x = USArrests, # data matrix
stand = TRUE, # Standardize the data
metric = "euclidean", # metric for distance matrix
method = "ward" # Linkage method
)
# DIvisive ANAlysis Clustering
res.diana <- diana(x = USArrests, # data matrix
stand = TRUE, # standardize the data
metric = "euclidean" # metric for distance matrix
)

CAPITULO 8

Preparación de datos

df <- scale(USArrests)
# Subset containing 10 rows
set.seed(123)
ss <- sample(1:50, 10)
df <- df[ss,]

*Comparación de dendrogramas**

library(dendextend)
# Compute distance matrix
res.dist <- dist(df, method = "euclidean")
# Compute 2 hierarchical clusterings
hc1 <- hclust(res.dist, method = "average")
hc2 <- hclust(res.dist, method = "ward.D2")
# Create two dendrograms
dend1 <- as.dendrogram (hc1)
dend2 <- as.dendrogram (hc2)
# Create a list to hold dendrograms
dend_list <- dendlist(dend1, dend2)

Comparación visual de dos dendrogramas

Dibuja un tanglegrama:

tanglegram(dend1, dend2)
## Loading required namespace: colorspace

Personalizamos el tanglegrama utilizando muchas otras opciones, como se indica a continuación:

tanglegram(dend1, dend2,
highlight_distinct_edges = FALSE, # Turn-off dashed lines
common_subtrees_color_lines = FALSE, # Turn-off line colors
common_subtrees_color_branches = TRUE, # Color common branches
main = paste("entanglement =", round(entanglement(dend_list), 2))
)

Matriz de correlación entre una lista de dendrogramas

La función cor.dendlist() se utiliza para calcular la matriz de correlación de Baker o cofenética entre una lista de árboles. El valor puede oscilar entre -1 y 1. Los valores cercanos a 0 indican que los dos árboles no son estadísticamente similares.

# Cophenetic correlation matrix
cor.dendlist(dend_list, method = "cophenetic")
##           [,1]      [,2]
## [1,] 1.0000000 0.9925544
## [2,] 0.9925544 1.0000000
# Baker correlation matrix
cor.dendlist(dend_list, method = "baker")
##           [,1]      [,2]
## [1,] 1.0000000 0.9895528
## [2,] 0.9895528 1.0000000

La correlación entre dos árboles también se puede calcular de la siguiente manera:

# Cophenetic correlation coefficient
cor_cophenetic(dend1, dend2)
## [1] 0.9925544
# Baker correlation coefficient
cor_bakers_gamma(dend1, dend2)
## [1] 0.9895528

También es posible comparar varios dendrogramas simultáneamente. El operador de encadenamiento %>% se utiliza para ejecutar varias funciones al mismo tiempo. Es útil para simplificar el código:

# Create multiple dendrograms by chaining
dend1 <- df %>% dist %>% hclust("complete") %>% as.dendrogram
dend2 <- df %>% dist %>% hclust("single") %>% as.dendrogram
dend3 <- df %>% dist %>% hclust("average") %>% as.dendrogram
dend4 <- df %>% dist %>% hclust("centroid") %>% as.dendrogram
# Compute correlation matrix
dend_list <- dendlist("Complete" = dend1, "Single" = dend2,
"Average" = dend3, "Centroid" = dend4)
cors <- cor.dendlist(dend_list)
# Print correlation matrix
round(cors, 2)
##          Complete Single Average Centroid
## Complete     1.00   0.46    0.45     0.30
## Single       0.46   1.00    0.23     0.17
## Average      0.45   0.23    1.00     0.31
## Centroid     0.30   0.17    0.31     1.00
# Visualize the correlation matrix using corrplot package
library(corrplot)
corrplot(cors, "pie", "lower")

CAPITULO 9

Visualización de dendrogramas

Comenzamos calculando la agrupación jerárquica utilizando los conjuntos de datos de USArrests:

# Load data
data(USArrests)
# Compute distances and hierarchical clustering
dd <- dist(scale(USArrests), method = "euclidean")
hc <- hclust(dd, method = "ward.D2")

Para crear un dendrograma básico, escriba lo siguiente:

library(factoextra)
fviz_dend(hc, cex = 0.5)

Puedes usar los argumentos main, sub, xlab, ylab para cambiar los títulos de los gráficos de la siguiente manera:

fviz_dend(hc, cex = 0.5,
main = "Dendrogram - ward.D2",
xlab = "Objects", ylab = "Distance", sub = "")

Para dibujar un dendrograma horizontal, escriba lo siguiente:

fviz_dend(hc, cex = 0.5, horiz = TRUE)

También es posible cortar el árbol a una altura determinada para particionar los datos en múltiples grupos, como se describe en el capítulo anterior: Agrupamiento jerárquico (Capítulo 7). En este caso, es posible colorear las ramas por grupos y añadir un rectángulo alrededor de cada grupo.

fviz_dend(hc, k = 4, # Cut in four groups
cex = 0.5, # label size
k_colors = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
color_labels_by_k = TRUE, # color labels by groups
rect = TRUE, # Add rectangle around groups
rect_border = "jco",
rect_fill = TRUE)

Para cambiar el tema del gráfico, utilice el argumento ggtheme, cuyos valores permitidos incluyen ggplot2

temas oficiales [theme_gray(), theme_bw(), theme_minimal(), theme_classic(),

theme_void()] o cualquier otro tema de ggplot2 definido por el usuario.

fviz_dend(hc, k = 4, # Cut in four groups
cex = 0.5, # label size
k_colors = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
color_labels_by_k = TRUE, # color labels by groups
ggtheme = theme_gray() # Change theme
)

En el código R que aparece a continuación, cambiaremos los colores de los grupos utilizando la paleta de colores de “jco” (Journal of Clinical Oncology).

fviz_dend(hc, cex = 0.5, k = 4, # Cut in four groups
k_colors = "jco")

Si quieres dibujar un dendrograma horizontal con rectángulos alrededor de los grupos, usa esto:

fviz_dend(hc, k = 4, cex = 0.4, horiz = TRUE, k_colors = "jco",
rect = TRUE, rect_border = "jco", rect_fill = TRUE)

Además, puede trazar un dendrograma circular utilizando la opción type = “circular”.

fviz_dend(hc, cex = 0.5, k = 4,
k_colors = "jco", type = "circular")

Para graficar un árbol filogenético, use type = “phylogenic” y repel = TRUE (para evitar que las etiquetas se superpongan). Esta funcionalidad requiere el paquete igraph de R. Asegúrese de que esté instalado antes de escribir el siguiente código de R.

require("igraph")
fviz_dend(hc, k = 4, k_colors = "jco",
type = "phylogenic", repel = TRUE)

La estructura predeterminada para los árboles filogenéticos es «layout.auto». Los valores permitidos son:

c(«layout.auto», «layout_with_drl», «layout_as_tree», «layout.gem», «layout.mds»,

«layout_with_lgl»). Para obtener más información sobre estas estructuras, consulte la documentación del paquete igraph de R.

Probemos con phylo.layout = «layout.gem»:

require("igraph")
fviz_dend(hc, k = 4, # Cut in four groups
k_colors = "jco",
type = "phylogenic", repel = TRUE,
phylo_layout = "layout.gem")

Ampliando el dendrograma

Si quieres ampliar los primeros grupos, puedes usar las opciones xlim e ylim para limitar el área del gráfico. Por ejemplo, escribe el siguiente código:

fviz_dend(hc, xlim = c(1, 20), ylim = c(1, 8))

Trazando un subárbol de dendrogramas

El código R es el siguiente.

• Recortar el dendrograma y visualizar la versión truncada:

# Create a plot of the whole dendrogram,
# and extract the dendrogram data
dend_plot <- fviz_dend(hc, k = 4, # Cut in four groups
cex = 0.5, # label size
k_colors = "jco"
)
dend_data <- attr(dend_plot, "dendrogram") # Extract dendrogram data
# Cut the dendrogram at height h = 10
dend_cuts <- cut(dend_data, h = 10)
# Visualize the truncated version containing
# two branches
fviz_dend(dend_cuts$upper)
## Warning in min(-diff(our_dend_heights)): no non-missing arguments to min;
## returning Inf

• Trazar subárboles de dendrogramas:

# Plot the whole dendrogram
print(dend_plot)

# Plot subtree 1
fviz_dend(dend_cuts$lower[[1]], main = "Subtree 1")

# Plot subtree 2
fviz_dend(dend_cuts$lower[[2]], main = "Subtree 2")

También puedes representar árboles circulares de la siguiente manera:

fviz_dend(dend_cuts$lower[[2]], type = "circular")

Guardar dendrograma en una página PDF grande

Si tiene un dendrograma grande, puede guardarlo en una página PDF grande, a la que se le puede hacer zoom sin pérdida de resolución.

pdf("dendrogram.pdf", width=30, height=15) # Open a PDF
p <- fviz_dend(hc, k = 4, cex = 1, k_colors = "jco" ) # Do plotting
print(p)
dev.off()
## png 
##   2

Manipulación de dendrogramas mediante dendextend

Código R estándar para crear un dendrograma:

data <- scale(USArrests)
dist.res <- dist(data)
hc <- hclust(dist.res, method = "ward.D2")
dend <- as.dendrogram(hc)
plot(dend)

Código R para crear un dendrograma utilizando el operador de encadenamiento:

library(dendextend)
dend <- USArrests[1:5,] %>% # data
scale %>% # Scale the data
dist %>% # calculate a distance matrix,
hclust(method = "ward.D2") %>% # Hierarchical clustering
as.dendrogram # Turn the object into a dendrogram.
plot(dend)

Funciones para personalizar dendrogramas: La función set() [del paquete dendextend] permite modificar los parámetros de un dendrograma. El formato es:

library(dendextend)
# 1. Create a customized dendrogram
mycols <- c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07")
dend <- as.dendrogram(hc) %>%
set("branches_lwd", 1) %>% # Branches line width
set("branches_k_color", mycols, k = 4) %>% # Color branches by groups
set("labels_colors", mycols, k = 4) %>% # Color labels by groups
set("labels_cex", 0.5) # Change label size
# 2. Create plot
fviz_dend(dend)

APLICACION DE ANALISIS DE CLUSTER.

Se dispone del dataset “Mall_Customers.csv”(Analysis (2018)) con información de 200 clientes de un centro comercial, que incluye edad, ingresos anuales y puntuación de gasto. El objetivo es aplicar técnicas de clustering como K-Means, PAM, CLARA y agrupamiento jerárquico para identificar segmentos naturales de clientes con comportamientos similares, permitiendo así el desarrollo de estrategias de marketing personalizadas basadas en las características demográficas y de consumo de cada grupo.

Cargar y preparar datos

library(readr)
customer_data <- read_csv("Mall_Customers.csv")
## Rows: 200 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): Gender
## dbl (4): CustomerID, Age, Annual Income (k$), Spending Score (1-100)
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Ver estructura de datos
head(customer_data, n = 3)
## # A tibble: 3 × 5
##   CustomerID Gender   Age `Annual Income (k$)` `Spending Score (1-100)`
##        <dbl> <chr>  <dbl>                <dbl>                    <dbl>
## 1          1 Male      19                   15                       39
## 2          2 Male      21                   15                       81
## 3          3 Female    20                   16                        6
str(customer_data)
## spc_tbl_ [200 × 5] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ CustomerID            : num [1:200] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Gender                : chr [1:200] "Male" "Male" "Female" "Female" ...
##  $ Age                   : num [1:200] 19 21 20 23 31 22 35 23 64 30 ...
##  $ Annual Income (k$)    : num [1:200] 15 15 16 16 17 17 18 18 19 19 ...
##  $ Spending Score (1-100): num [1:200] 39 81 6 77 40 76 6 94 3 72 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   CustomerID = col_double(),
##   ..   Gender = col_character(),
##   ..   Age = col_double(),
##   ..   `Annual Income (k$)` = col_double(),
##   ..   `Spending Score (1-100)` = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>

Análisis exploratorio

# Renombrar columnas para claridad
colnames(customer_data) <- c("CustomerID", "Gender", "Age", "Annual_Income", "Spending_Score")

# Preparar datos para clustering
df <- customer_data[, c("Age", "Annual_Income", "Spending_Score")]
df_scaled <- scale(df)
head(df_scaled, n = 3)
##            Age Annual_Income Spending_Score
## [1,] -1.421003     -1.734646     -0.4337131
## [2,] -1.277829     -1.734646      1.1927111
## [3,] -1.349416     -1.696572     -1.7116178

Determinar número óptimo de clusters

library(factoextra)

fviz_nbclust(df_scaled, kmeans, method = "wss") +
  geom_vline(xintercept = 5, linetype = 2) +
  labs(subtitle = "Método del codo")

Aplicar K-Means clustering

K-means es un algoritmo de clasificación no supervisada (clusterización) que agrupa objetos en k grupos basándose en sus características. El agrupamiento se realiza minimizando la suma de distancias entre cada objeto y el centroide de su grupo o cluster.

Con el metodo de K-medias conoceremos las agrupaciones posibles

# Compute k-means with k = 5
set.seed(123)
km.res <- kmeans(df_scaled, 5, nstart = 25)
print(km.res)
## K-means clustering with 5 clusters of sizes 40, 54, 39, 47, 20
## 
## Cluster means:
##           Age Annual_Income Spending_Score
## 1 -0.42773261     0.9724070     1.21304137
## 2 -0.97822376    -0.7411999     0.46627028
## 3  0.07314728     0.9725047    -1.19429976
## 4  1.20182469    -0.2351832    -0.05223672
## 5  0.52974416    -1.2872781    -1.23337167
## 
## Clustering vector:
##   [1] 2 2 5 2 2 2 5 2 5 2 5 2 5 2 5 2 5 2 5 2 5 2 5 2 5 2 5 2 5 2 5 2 5 2 5 2 5
##  [38] 2 5 2 4 2 5 2 5 2 4 2 2 2 4 2 2 4 4 4 4 4 2 4 4 2 4 4 4 2 4 4 2 2 4 4 4 4
##  [75] 4 2 4 4 2 4 4 2 4 4 2 4 4 2 2 4 4 2 4 4 2 2 4 2 4 2 2 4 4 2 4 2 4 4 4 4 4
## [112] 2 3 2 2 2 4 4 4 4 2 3 1 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1
## [149] 3 1 3 1 3 1 3 1 3 1 3 1 4 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1 3
## [186] 1 3 1 3 1 3 1 3 1 3 1 3 1 3 1
## 
## Within cluster sum of squares by cluster:
## [1] 23.91544 51.85673 46.38992 26.65665 18.58760
##  (between_SS / total_SS =  72.0 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"

Agregar clusters a datos originales

customer_data$cluster <- km.res$cluster
head(customer_data)
## # A tibble: 6 × 6
##   CustomerID Gender   Age Annual_Income Spending_Score cluster
##        <dbl> <chr>  <dbl>         <dbl>          <dbl>   <int>
## 1          1 Male      19            15             39       2
## 2          2 Male      21            15             81       2
## 3          3 Female    20            16              6       5
## 4          4 Female    23            16             77       2
## 5          5 Female    31            17             40       2
## 6          6 Female    22            17             76       2

Análisis de clusters

aggregate(customer_data[, 3:5], by = list(cluster = km.res$cluster), mean)
##   cluster      Age Annual_Income Spending_Score
## 1       1 32.87500      86.10000       81.52500
## 2       2 25.18519      41.09259       62.24074
## 3       3 39.87179      86.10256       19.35897
## 4       4 55.63830      54.38298       48.85106
## 5       5 46.25000      26.75000       18.35000

Visulización de clusters

fviz_cluster(km.res, data = df_scaled,
             palette = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07", "#FF6B6B"),
             ellipse.type = "euclid",
             star.plot = TRUE,
             repel = TRUE,
             ggtheme = theme_minimal())

Acceso a resultados

# Cluster number for each observation
head(km.res$cluster, 10)
##  [1] 2 2 5 2 2 2 5 2 5 2
# Cluster size
km.res$size
## [1] 40 54 39 47 20
# Cluster means
km.res$centers
##           Age Annual_Income Spending_Score
## 1 -0.42773261     0.9724070     1.21304137
## 2 -0.97822376    -0.7411999     0.46627028
## 3  0.07314728     0.9725047    -1.19429976
## 4  1.20182469    -0.2351832    -0.05223672
## 5  0.52974416    -1.2872781    -1.23337167

Interpretacion

A partir de la aplicación del algoritmo K-Means con k = 5, se identificaron cinco grupos diferenciados de clientes del dataset Mall_Customers. Los tamaños de los clusters fueron los siguientes: 40 clientes en el Cluster 1, 54 en el Cluster 2, 39 en el Cluster 3, 47 en el Cluster 4 y 20 en el Cluster 5. Esto muestra que el Cluster 2 es el más numeroso mientras que el Cluster 5 es el más pequeño.

Los centroides de cada cluster, calculados sobre los datos estandarizados (Age, Annual_Income y Spending_Score), permiten caracterizar a los clientes de manera más precisa. El Cluster 1 tiene valores -0.4277326 para Age, 0.972407 para Income y km.res$centers[1, “Spending_Score”] para Spending Score, lo que indica que este grupo corresponde a clientes relativamente jóvenes, con ingresos altos y un nivel de gasto elevado. Este es el segmento de mayor valor económico.

El Cluster 2, con centroides -0.9782238 (Age), -0.7411999 (Income) y 0.4662703 (Spending Score), representa clientes muy jóvenes, con ingresos bajos pero con un gasto medio. Este grupo muestra actividad de consumo pese a sus limitaciones económicas.

El Cluster 3, cuyos centroides son 0.0731473, 0.9725047 y -1.1942998, corresponde a clientes de edad promedio, con ingresos altos pero gasto muy bajo. Este grupo puede considerarse de alto poder adquisitivo pero baja conversión en compras, lo que lo hace interesante para estrategias de marketing enfocadas en aumentar su consumo.

El Cluster 4 se caracteriza por centroides 1.2018247, -0.2351832 y -0.0522367, indicando adultos mayores con ingresos medios-bajos y un gasto moderado. Representa un segmento estable pero sin un alto potencial de consumo.

Finalmente, el Cluster 5 muestra valores 0.5297442,-1.2872781 y -1.2333717, describiendo un grupo de clientes ligeramente mayores, con ingresos bajos y el menor gasto de todos los clusters. Este es el segmento de menor valor económico.

En conjunto, el análisis permitió identificar patrones claros entre los clientes, destacando un segmento “premium” (Cluster 1) y un segmento de bajo valor (Cluster 5), así como grupos intermedios con conductas de consumo específicas. Esto demuestra que la segmentación mediante K-Means es útil para comprender las diferencias en edad, ingresos y gasto dentro de la población analizada.

BIBLIOGRAFIA

Analysis, M. B. (2018). Customer segmentation tutorial in python. Kaggle. https://www.kaggle.com/datasets/vjchoudhary7/customer-segmentation-tutorial-in-python
Kassambara, A. (2017). Practical guide to cluster analysis in r: Unsupervised machine learning. STHDA. http://www.sthda.com