Análisis de Clúster (Conglomerados)

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


“Tema de investigación: Análisis de Clúster (Conglomerados)”

Asignatura: Métodos para el Análisis Económico

Grupo teórico: GT-01

Docente: MSF Carlos Ademir Pérez Alas


Integrantes:

   Jaime Elías Barahona Martínez           BM23012
  Jefferson Josué Sandoval Pérez           SP23029
Mariana Ivette Zelaya Castillo                ZC23003

Ciudad Universitaria, San Salvador – 17 de noviembre de 2025

1. Análisis de conglomerados

El análisis de conglomerados (cluster) es una técnica estadística multivariante cuyo propósito es reorganizar un conjunto de entidades en grupos internamente homogéneos y externamente diferenciados. En términos generales, busca que cada grupo contenga elementos lo más similares posible entre sí y que los grupos difieran de forma clara entre ellos (De la Fuente Fernández, 2011). Se trata de una técnica descriptiva, ateórica y no inferencial, basada más en criterios geométricos que en pruebas estadísticas. Su uso principal es la exploración y descripción de patrones subyacentes, más que la explicación causal o la generalización a poblaciones; además, las soluciones no son únicas y dependen tanto de las variables incluidas como del procedimiento aplicado, de modo que añadir o eliminar variables puede modificar de forma sustancial el resultado (De la Fuente Fernández, 2011).

Desde una perspectiva más conceptual, el análisis de conglomerados puede entenderse como una forma de darle orden a la complejidad. Cuando se trabaja con un conjunto amplio de datos, lo que realmente se busca es identificar patrones de semejanza y diferencia que permitan organizar esa diversidad en grupos manejables. No se trata de imponer una teoría previa, sino de observar cómo los datos se agrupan por sí mismos, revelando estructuras internas ocultas que no son evidentes en la superficie. En ese sentido, el análisis de conglomerados funciona como una herramienta de descubrimiento, que permite visualizar la lógica interna de las relaciones entre elementos, incluso cuando dicha lógica no se presenta explícitamente.

Al profundizar en esta técnica, resulta relevante señalar que no existe una única forma de agrupar. Los procedimientos pueden ser jerárquicos, construyendo un dendrograma que muestra cómo los grupos se fusionan o se separan; o no jerárquicos, estableciendo de antemano el número de grupos deseados y ajustando las asignaciones hasta obtener la mayor coherencia interna posible. Cada método refleja una manera distinta de concebir la semejanza: algunos privilegian la distancia mínima, otros la máxima, algunos el promedio o la variación interna. Esta diversidad de enfoques recuerda que el análisis de conglomerados no es una receta única, sino un conjunto de caminos metodológicos para interpretar la estructura de los datos.

El análisis de conglomerados, también denominado análisis cluster, tiene como objetivo organizar un conjunto de observaciones en grupos internamente homogéneos y externamente heterogéneos, identificando estructuras naturales sin que estas estén definidas previamente, ya que surgen directamente de las características observadas (Aldás Manzano & Uriel Jiménez, 2017). El procedimiento parte de un conjunto de observaciones descritas por diversas variables; sobre ellas se calcula una medida de proximidad o distancia, que expresa qué tan similares o diferentes son dos observaciones. Con base en estas distancias se forman grupos que reúnen a las observaciones más cercanas, para luego describir y comparar dichos grupos según los valores promedio de las variables utilizadas (Aldás Manzano & Uriel Jiménez, 2017).

Es fundamental destacar que el análisis de conglomerados se diferencia de otras técnicas multivariantes porque los grupos no están definidos previamente. En métodos como la regresión logística o el análisis discriminante, las observaciones ya se encuentran clasificadas y el objetivo es explicar esa clasificación. En cambio, en el análisis de conglomerados la tarea central es descubrir la clasificación misma, construyendo agrupaciones que reflejen patrones de similitud y diferencia (Aldás Manzano & Uriel Jiménez, 2017).


1.1 Enfoques de agrupamiento

El análisis puede realizarse desde dos perspectivas principales:

  1. Agrupamiento de individuos: reorganiza la muestra en clusters según su similitud utilizando directamente la matriz de datos X.
  2. Agrupamiento de variables: agrupa las columnas de X, buscando relaciones entre variables en lugar de sujetos (De la Fuente Fernández, 2011).

1.2 Medidas de asociación: distancias y similaridades

La formación de grupos depende de una medida de proximidad, que puede basarse en:

1.2.1 Distancias (criterio de disimilitud)

Evalúan la “lejanía” entre elementos. Entre ellas destacan:
- Euclídea
- Minkowski
- Manhattan (d₁)
- Tchebychev
- Mahalanobis
- Basada en χ²

Los clusters se forman reuniendo individuos con distancias pequeñas (De la Fuente Fernández, 2011).

1.2.2 Similaridades (criterio de semejanza)

Evalúan la “proximidad” conceptual.
Para variables:
- Coseno del ángulo
- Correlación
Para datos dicotómicos:
- Jaccard
- Dice
- Russell–Rao
- Rogers–Tanimoto
- Ochiai

(De la Fuente Fernández, 2011)


1.3 Métodos jerárquicos

Estos métodos construyen una jerarquía de particiones representada mediante un dendrograma. Su objetivo es:

  • Maximizar la homogeneidad interna, o
  • Minimizar la distancia entre grupos (De la Fuente Fernández, 2011).

1.3.1 Tipos de métodos

  • Aglomerativos: inician con un individuo por grupo y van fusionando.
  • Disociativos: inician con un solo grupo y lo dividen iterativamente.

1.3.2 Criterios de vinculación

  • Linkage simple (vecino más próximo)
  • Linkage completo (vecino más lejano)
  • Promedio entre grupos
  • Centroide
  • Mediana
  • Ward (minimiza la suma de cuadrados intracluster)

1.4 Métodos no jerárquicos

Clasifican individuos en K grupos previamente definidos y ajustan las asignaciones para optimizar la coherencia interna (De la Fuente Fernández, 2011).

1.4.1 Principal método

  • K-medias: parte de una partición inicial y reasigna individuos para minimizar la variación interna.

1.4.2 Otros métodos

  • Nubes dinámicas
  • Análisis modal
  • Métodos de búsqueda de densidad
  • Taxap
  • Block clustering
  • Análisis factorial tipo Q

1.5 Etapas del proceso

Según De la Fuente Fernández (2011), el análisis de conglomerados sigue cuatro decisiones clave:

  1. Elección de variables
    • Naturaleza: cualitativas o cuantitativas
    • Pertinencia para el objetivo del estudio
  2. Elección de la medida de asociación
    • Distancia o similaridad apropiada al tipo de datos
  3. Elección de la técnica
    • Métodos jerárquicos (y tipo de enlace)
    • Métodos no jerárquicos como K-medias
  4. Validación de resultados
    • Consistencia de la partición
    • Sensibilidad a variables y criterios de agrupamiento

2. Elaboración de cuadro comparativo de: Resumen del Análisis de Clúster

Elabore un cuadro comparativo, que incluya los siguientes elementos: (Lo indicado en rojo corresponde a los elementos a redactar, en la versión a ser entregada debe aparecer el texto negro normal.)

Resumen del Análisis de Clúster

Análisis de Clúster Técnicas disponibles Ventajas Desventajas
Jerárquico:

Definición ampliada:
El análisis jerárquico construye una estructura de tipo árbol (dendrograma) que representa cómo los objetos se agrupan según su similitud. No requiere definir la cantidad de clústeres de antemano. Parte ya sea desde clusters individuales (aglomerativo: AGNES) o desde un único clúster que se divide (divisivo: DIANA).

El dendrograma muestra visualmente a qué nivel se fusionan los objetos y qué tan similares son entre sí. Las uniones se basan en una matriz de distancias entre pares de observaciones.
Métodos :
• AGNES (aglomerativo): une clústeres más similares en cada paso.
• DIANA (divisivo): comienza con un gran clúster que se divide sucesivamente.

Métodos de enlace (linkage):
• Single linkage: usa la distancia mínima entre pares de elementos.
• Complete linkage: usa la distancia máxima entre pares.
• Average linkage: distancia promedio entre elementos de ambos clústeres.
• Centroide: distancia entre los centroides de los clústeres.
• Ward / Ward.D2: minimiza la varianza interna; genera clústeres compactos.
• No requiere número de clústeres previo .
• Genera una representación visual clara (dendrograma).
• Permite evaluar niveles de similitud entre objetos.
• Útil para explorar la estructura de los datos sin supuestos previos.
• El método Ward produce clústeres compactos y bien separados.
• Permite analizar pequeños clústeres (aglomerativo) o grandes grupos (divisivo).
• Posibilita verificar la calidad del árbol mediante correlación cophenética.
• Elevado costo computacional en grandes bases de datos.
• Sensible al método de enlace y a la métrica utilizada: distintas combinaciones generan dendrogramas diferentes.
• Difícil determinar el punto óptimo para “cortar” el árbol.
• El orden de los datos puede influir en las uniones iniciales.
• No necesariamente optimiza una función global de variación (a diferencia de k-means).
• Métodos como single linkage pueden producir clústeres “alargados” o cadenas.
No jerárquico:

Definición ampliada:
Los métodos no jerárquicos (de partición) clasifican las observaciones en k clústeres definidos por el analista. Buscan optimizar una medida interna como la variación intraclúster (k-means) o la disimilitud total (PAM). Las observaciones se reasignan de manera iterativa hasta alcanzar convergencia. Se recomienda su uso en bases de datos grandes o cuando se conoce un rango razonable de k.
Técnicas:
• K-means: minimiza la suma total de cuadrados dentro de los clústeres; utiliza centroides.
• K-medoids / PAM: selecciona objetos reales como medoids; más robusto ante outliers.
• CLARA: adapta PAM mediante muestreo para bases de datos muy grandes.

Medidas de distancia utilizadas:
• Euclidiana (usada por defecto en k-means).
• Manhattan (útil en PAM/CLARA en presencia de outliers).
• Mayor eficiencia computacional, especialmente k-means.
• Adecuado para miles de observaciones (CLARA es diseñado para esto).
• Permite evaluar k con métodos como:
  – Elbow / WSS.
  – Silhouette.
• PAM es más robusto frente a outliers que k-means.
• CLARA reduce el uso de memoria y acelera el procesamiento.
• k-means es simple, rápido y fácil de interpretar.
• Los algoritmos con nstart alto mejoran la estabilidad de los resultados.
• Requieren definir el número de clústeres antes del análisis.
• Sensibles a valores iniciales (k-means puede converger a mínimos locales).
• k-means es muy sensible a outliers.
• Diferentes inicializaciones pueden producir soluciones diferentes.
• Clasificación depende del orden de los datos en algunos casos.
• PAM es más robusto pero puede ser más lento con muchos k o variables.
• CLARA depende de la calidad de la muestra seleccionada.
Nota:
Fuente: elaboración propia con base en Kassambara (2017). Practical Guide to Cluster Analysis in R.Unsupervised Machine Learning (Multivariate Analysis) (1st ed.). STHDA

3. Descripción breve de las técnicas jerárquicas y no jerárquicas para análisis de clúster y su implementación en R

Describa las técnicas disponibles para realizar el análisis de clúster, tanto jerárquicas como no jerárquicas, presentadas en el cuadro anterior, incluya una explicación de la librería y sintaxis para implementarla en R.

3.1 Análisis de Clúster Jerárquico.

El análisis de clúster jerárquico, descrito por (Kassambara, 2017), es un conjunto de métodos de aprendizaje no supervisado que busca agrupar observaciones de manera progresiva, formando una estructura con forma de árbol o dendrograma. Cada fusión de grupos refleja la similitud entre observaciones o conjuntos de observaciones, calculada mediante una matriz de distancias (por ejemplo, euclidiana, Manhattan o de Gower).

En su versión aglomeraría (la más utilizada), cada observación comienza como un grupo individual y los clústeres se van uniendo sucesivamente, según el criterio de “menor distancia intergrupal”, hasta obtener un solo clúster que contenga todas las observaciones.

3.2 Técnicas disponibles (métodos de enlace o linkage methods)

1. Método de Enlace Simple (Single Linkage):

La distancia entre dos clústeres se define como la menor distancia entre un par de observaciones, una de cada grupo. El algoritmo siempre une los dos clústeres más cercanos en su punto más próximo, tendiendo a formar cadenas largas de puntos (chaining effect), ya que puede enlazar grupos muy separados si existe una secuencia de observaciones intermedias cercanas.

Tiene facil interpretacion y es capaz de identificar clusteres con formas irregulares o alargadas, aunque es muy sencible a los valores atípicos o el ruide y puede formar clústeres alargados poco compactos.

La sintaxis en R:

Cálculo de distancia (por ejemplo, euclidiana)

distancia <- dist(datos_escalados, method = “euclidean”)
  • dist(): Calcula la matriz de distancia entre todas las observaciones o puntos de datos en el dataset llamado.

Clustering jerárquico con enlace simple

hclust_single <- hclust(distancia, method = “single”).
  • hclust(): Realiza el análisis de clustering jerárquico aglomerativo. Emplea el método de enlace simple (“single”), donde la distancia entre dos clústeres se define como la distancia mínima entre cualquier punto de un clúster y cualquier punto del otro.

Visualizar dendrograma

fviz_dend(hclust_single, k = 4, rect = TRUE, cex = 0.6)
  • fviz_dend(): Visualiza el resultado del clustering jerárquico en forma de un dendrograma.

  • hclust_single: El objeto de clustering que se va a visualizar.

  • k = 4: Indica que se deben dibujar 4 clústeres (grupos) distintos en el dendrograma.

  • rect = TRUE: Dibuja un rectángulo alrededor de los 4 clústeres para resaltarlos.

  • cex = 0.6: Controla el tamaño de la etiqueta del texto para una mejor visualización.

2. Método de Enlace Completo (Complete Linkage)

Define la distancia entre dos clústeres como la mayor distancia entre observaciones de cada grupo. Este criterio produce clústeres más compactos y tiende a evitar el chaining. Forma clústeres bien separados y compacto reduciendo la probabilidad de unir grupos muy diferentes. Tiene la desventaja de ser muy sensible a valores extremos.

Sintaxis en R:
hclust_complete <- hclust(distancia, method = “complete”)
  • dist(): Función que calcula la matriz de distancias entre todas las observaciones.

  • datos_escalados: El dataframe o matriz de entrada (se recomienda que los datos estén escalados).

  • method = “complete”: Especifica la métrica de distancia a usar.

hclust_complete <- hclust(distancia, method = “complete”).
  • hclust(): Función principal para el clustering jerárquico.

3. Método de Enlace Promedio (Average Linkage o UPGMA)

La distancia entre dos clústeres se calcula como el promedio aritmético de todas las distancias entre pares de observaciones de ambos grupos. Busca un equilibrio entre el enlace simple (que puede unir cadenas) y el completo (que puede dividir grupos naturales).

Es más robusto frente a outliers que los métodos anteriores visto produciendo clústeres equilibrados y consistentes; aunque puede suavizar diferencias entre grupos de distinto tamaño y es ligeramente más costoso en términos computacionales.

Sintaxis en R:
hclust_average <- hclust(distancia, method = “average”)
  • hclust(): Función principal para el clustering jerárquico.

  • method = “average”: Este argumento especifica el método de enlace (linkage method) que se usará

fviz_dend(hclust_average, k = 3, rect = TRUE, main = “Método de enlace promedio”)
  • fviz_dend(): Es la función principal del paquete factoextra. Se usa para generar un dendrograma con características mejoradas y personalizadas (a diferencia de la función plot() básica).

  • hclust_average: Es el objeto de clustering que se va a dibujar. Contiene toda la estructura jerárquica de cómo se agruparon tus datos usando el método de enlace promedio.

  • k = 3: Este argumento es clave: le indica a la función que debe cortar el dendrograma para identificar y resaltar 3 clusters (grupos) distintos en tus datos.

  • rect = TRUE: Indica que se deben dibujar rectángulos alrededor de los \(k=3\) clusters identificados. Esto ayuda a delimitar visualmente los grupos.

  • main = “Método de enlace promedio”: Establece el título principal del gráfico, dando contexto sobre el método de enlace utilizado para generar el clustering.

4. Método de Ward (Ward.D o Ward.D2)

El método de Ward fusiona los clústeres que minimizan el incremento en la suma de cuadrados dentro del grupo (intra-cluster variance). Es decir, el algoritmo busca que los clústeres resultantes sean lo más homogéneos posible internamente. La versión más utilizada, ward.D2, usa la distancia euclidiana al cuadrado, y es el método recomendado por (Kassambara, 2017) para variables continúas escaladas.

Tiene a formar clústeres compactos y esféricos, bastante adecuado para datos cuantitativos escalados teniendo buena interpretabilidad visual en el dendrograma, aunque es bastante sensibles a valores atípicos y tampoco funciona bien si los clústeres tienen formas no esféricas.

Sintaxis en R:
hclust_ward <- hclust(distancia, method = “ward.D2”)
  • hclust(): Es la función principal en R para realizar el clustering jerárquico.

  • distancia: Este es el objeto de entrada, que debe ser una matriz de distancias precalculada entre tus puntos de datos.

  • method = “ward.D2”: Este es el argumento clave que define cómo se calcula la distancia (o disimilitud) entre clusters.

Visualizar dendrograma

fviz_dend(hclust_ward, k = 3, rect = TRUE, main = “Método de Ward (D2)”)
  • fviz_dend(): Es la función principal del paquete factoextra. Se usa para generar un dendrograma con características mejoradas y personalizadas (a diferencia de la función plot() básica).

  • hclust_average: Es el objeto de clustering que se va a dibujar. Contiene toda la estructura jerárquica de cómo se agruparon tus datos usando el método de enlace promedio.

  • k = 3: Este argumento es clave: le indica a la función que debe cortar el dendrograma para identificar y resaltar 3 clusters (grupos) distintos en tus datos.

  • rect = TRUE: Indica que se deben dibujar rectángulos alrededor de los \(k=3\) clusters identificados. Esto ayuda a delimitar visualmente los grupos.

  • main = “Método de Ward(D2)”: Establece el título principal del gráfico, dando contexto sobre el método de enlace utilizado para generar el clustering.

Librerías utilizadas

  • stats: función hclust() para clustering jerárquico.

  • factoextra (Kassambara): funciones de visualización como fviz_dend(), fviz_cluster(), fviz_nbclust().

  • cluster: funciones complementarias (por ejemplo agnes() para clustering jerárquico avanzado).

3.3 Análisis de Clúster No Jerárquico

El análisis de clúster no jerárquico, también llamado de partición, divide las observaciones en un número fijo de grupos k definido previamente.

Cada observación pertenece exclusivamente a un clúster, y los algoritmos buscan minimizar la variabilidad interna (dentro de los clústeres) y maximizar la distancia entre grupos.

Estos métodos son más eficientes que los jerárquicos cuando se trabaja con grandes conjuntos de datos. (Kassambara, 2017) presenta tres algoritmos principales: K-Means, K-Medoids (PAM) y CLARA.

1. K-Means

El algoritmo K-Means busca particionar los datos en k clústeres minimizando la suma de cuadrados intra-clúster (intra-cluster sum of squares).

Funciona de forma iterativa con dos pasos fundamentales:

  • Asignación: cada observación se asigna al clúster cuyo centro (centroide) esté más cercano.

  • Actualización: los centroides se recalculan como la media de los puntos en cada grupo.

Este proceso se repite hasta que las asignaciones ya no cambian o se alcanza un número máximo de iteraciones. Es un algoritmo rápido y eficiente incluso en bases grandes, fácil de interpretar e implementar ya que puede visualizarse fácilmente en 2D o 3D.

Se requiere especificar k de antemano, se asumen clústeres esféricos y de tamaño similar siendo estos sensibles a valores atípicos y a la inicialización aleatoria de centroides.

Sintaxis en R:

Escalado de datos

datos_escalados <- scale(mis_datos)
  • scale(): es una función que estandariza las columnas de tus datos. Esto es un paso crucial antes de aplicar K-Means, ya que convierte las variables para que tengan una media de 0 y una desviación estándar de 1. Asegura que todas las variables tengan la misma influencia en el cálculo de distancias
Ejecución del algoritmo K-Means
set.seed(123)
resultado_kmeans <- kmeans(datos_escalados, centers = 3, nstart = 25, iter.max = 100)
  • set.seed(123): Fija la semilla para el generador de números aleatorios. Esto asegura que, cada vez que ejecutes el código, la inicialización del algoritmo K-Means sea la misma, haciendo que tus resultados sean reproducibles.

  • nstart = 25: K-Means es sensible a la selección inicial de los centroides. Este argumento especifica que el algoritmo se ejecutará 25 veces con diferentes puntos de inicio aleatorios.

  • centers = 3: Este es el hiperparámetro clave \(k\). Le indica al algoritmo que debe buscar y crear 3 clusters (grupos) distintos en los datos.

  • iter.max = 100: Define el número máximo de iteraciones permitidas para cada ejecución de K-Means.

2. K-Medoids (PAM – Partitioning Around Medoids)

PAM es una alternativa robusta a K-Means. En lugar de centroides (promedios), usa medoides, que son observaciones reales representativas del grupo. Minimiza la suma de disimilitudes entre cada observación y el medoide más cercano.

Es bastante robusto ante los valores atípicos pudiéndose usar cualquier medida de distancia, no solo euclidiana y acepta datos mixtos ya sea numéricos y categóricos. Es más costoso computacionalmente que K-Means y es menos eficiente para bases de datos muy grandes.

Sintaxis en R:
resultado_pam <- pam(datos_escalados, k = 3, metric = “euclidean”, stand = FALSE)
  • datos_escalados: Son los datos previamente estandarizados que se usarán para el agrupamiento.

  • *k = 3:** Al igual que en K-Means, este es el número deseado de clusters (grupos). El algoritmo intentará dividir los datos en 3 clusters.

  • metric = “euclidean”: Especifica la medida de distancia que se utilizará para determinar la similitud entre los puntos. La distancia euclidiana es la más común y representa la distancia de “línea recta” en el espacio multidimensional.

  • stand = FALSE: Indica si los datos deben estandarizarse dentro de la función pam(). Dado que ya se usó scale() previamente para crear datos_escalados, se establece en FALSE para evitar una doble estandarización.

fviz_cluster(resultado_pam, ellipse.type = “t”, ggtheme = theme_classic())
  • resultado_pam: El objeto que contiene el agrupamiento resultante del algoritmo PAM.

  • ellipse.type = “t”: Dibuja elipses de concentración alrededor de cada cluster. El tipo “t” (basado en la distribución t de Student) proporciona una estimación más robusta de la forma del clúster que la elipse euclidiana simple, a menudo útil para visualizar la dispersión.

  • ggtheme = theme_classic(): Aplica un tema visual limpio (del paquete ggplot2) al gráfico, con ejes y sin el fondo cuadriculado.

3. CLARA (Clustering Large Applications)

CLARA es una extensión de PAM diseñada para datasets grandes. Aplica el algoritmo PAM a distintas muestras del conjunto de datos y selecciona la mejor partición según la suma de disimilitudes promedio.

Permite manejar bases de datos con miles de observaciones conservando la robustez del método PAM. Los resultados dependen de la calidad de las muestras seleccionadas y puede perder precisión si las muestras no son representativas.

Sintaxis en R:
library(cluster)
resultado_clara <- clara(datos_escalados, k = 3, samples = 5, metric = “euclidean”)
  • clara(…): La función Clustering LARge Applications (CLARA) es una extensión del algoritmo PAM, diseñada específicamente para manejar grandes conjuntos de datos donde el algoritmo PAM tradicional puede ser computacionalmente demasiado lento. datos_escalados: Son los datos de entrada estandarizados.

  • k = 3: El número deseado de clusters es 3.

  • samples = 5: Este es el parámetro clave de CLARA. En lugar de procesar todo el conjunto de datos (como hace PAM), CLARA funciona tomando repetidamente muestras aleatorias del conjunto de datos y aplicando el algoritmo PAM a cada muestra. Aquí, se especifica que se tomarán 5 muestras.

  • metric = “euclidean”: Utiliza la distancia euclidiana para medir la disimilitud entre los puntos.

4. Desarrollo de ejemplos de los Capítulos 4 al 9 del Libro de Kassambara (2017)

Del texto: Kassambara, A. (2017). Practical Guide to Cluster Analysis in R: Unsupervised Machine Learning (Multivariate Analysis) (1st ed.). STHDA, disponible en: https://xsliulab.github.io/Workshop/week10/r-cluster-book.pdf, desarrolle los ejemplos presentados en los capítulos: 4,5,6,7,8,9.

Ejemplo de capítulo 4

Carga y Preparación de Datos

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

Estimación del Número Óptimo de Clusters (Método del Codo)

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

Ejecución del Algoritmo K-Means

# Compute k-means with k = 4
set.seed(123)
km.res <- kmeans(df, 4, nstart = 25)

# Mostrando los resultados
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"

Cálculo de Medias por Cluster

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

Integración de Clusters con Datos Originales

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 Componentes del Resultado K-Means

# 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
head(km.res$cluster, 4)
##  Alabama   Alaska  Arizona Arkansas 
##        1        4        4        1
# 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 Clusters K-Means

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()
)

Ejemplo de capítulo 5

Carga y preparación de datos

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

Estimación del número óptimo de clusters usando silueta

library(cluster)
## Warning: package 'cluster' was built under R version 4.4.3
library(factoextra)
fviz_nbclust(df, pam, method = "silhouette")+
theme_classic()

Cálculo del clustering 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"

Adición de asignaciones de clusters a los datos originales

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 componentes del objeto PAM

# 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 clusters 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()
)

Ejemplo de capítulo 6

Preparación de datos y formato

set.seed(1234)
# Generar 500 objetos, divididos en 2 clusters
df <- rbind(cbind(rnorm(200,0,8), rnorm(200,0,8)),
    cbind(rnorm(300,50,8), rnorm(300,50,8)))

# Especificar nombres de columnas y filas
colnames(df) <- c("x", "y")
rownames(df) <- paste0("S", 1:nrow(df))

# Vista previa de los datos
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

Estimación del número óptimo de clusters

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

Cálculo de CLARA

# Calcular CLARA
clara.res <- clara(df, 2, samples = 50, pamLike = TRUE)

# Imprimir componentes de 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"
# Añadir clasificaciones a los datos originales
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
# Acceder a los resultados
# Medoides
clara.res$medoids
##              x         y
## S121 -1.531137  1.145057
## S455 48.357304 50.233499
# Agrupamiento
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

Visualización de clusters CLARA

fviz_cluster(clara.res,
    palette = c("#00AFBB", "#FC4E07"), # paleta de colores
    ellipse.type = "t", # Elipse de concentración
    geom = "point", pointsize = 1,
    ggtheme = theme_classic()
)

Ejemplo de capítulo 7

Preparación de la estructura y datos

# Cargar los datos
data("USArrests")

# Estandarizar los datos
df <- scale(USArrests)

# Mostrar las primeras 6 filas
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 y matriz de distancias

# Calcular la matriz de disimilitud
# df = los datos estandarizados
res.dist <- dist(df, method = "euclidean")

# Mostrar las primeras 6 filas y columnas de la matriz de distancia
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

Función de enlace y agrupamiento jerárquico

# Calcular el agrupamiento jerárquico
res.hc <- hclust(d = res.dist, method = "ward.D2")

Visualización del dendrograma

# cex: tamaño de la etiqueta
library("factoextra")
fviz_dend(res.hc, cex = 0.5)

Verificación del árbol de clústeres

# Calcular distancia cofenética
res.coph <- cophenetic(res.hc)

# Correlación entre distancia cofenética y distancia original
cor(res.dist, res.coph)
## [1] 0.6975266
# Comparar con método de enlace promedio
res.hc2 <- hclust(res.dist, method = "average")
cor(res.dist, cophenetic(res.hc2))
## [1] 0.7180382

Cortar el dendrograma en diferentes grupos

# Cortar el árbol en 4 grupos
grp <- cutree(res.hc, k = 4)
head(grp, n = 4)
##  Alabama   Alaska  Arizona Arkansas 
##        1        2        2        3
# Número de miembros en cada clúster
table(grp)
## grp
##  1  2  3  4 
##  7 12 19 12
# Obtener los nombres para los miembros del clúster 1
rownames(df)[grp == 1]
## [1] "Alabama"        "Georgia"        "Louisiana"      "Mississippi"   
## [5] "North Carolina" "South Carolina" "Tennessee"

Visualización de los grupos del dendrograma

# Cortar en 4 grupos y colorear por grupos
fviz_dend(res.hc, k = 4, # Cortar en cuatro grupos
          cex = 0.5, # tamaño de etiqueta
          k_colors = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
          color_labels_by_k = TRUE, # colorear etiquetas por grupos
          rect = TRUE # Añadir rectángulo alrededor de grupos
)

Visualización de clústeres en gráfico de dispersión

fviz_cluster(list(data = df, cluster = grp),
             palette = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
             ellipse.type = "convex", # Elipse de concentración
             repel = TRUE, # Evitar superposición de etiquetas (lento)
             show.clust.cent = FALSE, ggtheme = theme_minimal())

Uso del paquete cluster para agrupamiento aglomerativo

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
)

fviz_dend(res.agnes, cex = 0.6, k = 4)

Ejemplo de capítulo 8

Datos

df <- scale(USArrests)

# Subconjunto que contiene 10 filas
set.seed(123)
ss <- sample(1:50, 10)
df <- df[ss,]

Comparacion

library(dendextend)
# Calcular la matriz de distancias
res.dist <- dist(df, method = "euclidean")

# Compute 2 hierarchical clusterings
hc1 <- hclust(res.dist, method = "average")
hc2 <- hclust(res.dist, method = "ward.D2")

# Crea dos dendrogramas
dend1 <- as.dendrogram (hc1)
dend2 <- as.dendrogram (hc2)

# Crea una lista para almacenar dendrogramas
dend_list <- dendlist(dend1, dend2)

Comparación visual de dos dendrogramas

tanglegram(dend1, dend2)

Personalizamos el tanglegrama utilizando muchas otras opciones, como se muestra a continuación.

tanglegram(dend1, dend2,
highlight_distinct_edges = FALSE, # Desactivar líneas discontinuas
common_subtrees_color_lines = FALSE, # Desactivar colores de línea
common_subtrees_color_branches = TRUE, # Color de ramas comunes
main = paste("entanglement =", round(entanglement(dend_list), 2))
)

Matriz de correlación entre una lista de dendrogramas

# Matriz de correlación cofenética
cor.dendlist(dend_list, method = "cophenetic")
##           [,1]      [,2]
## [1,] 1.0000000 0.9925544
## [2,] 0.9925544 1.0000000
# Matriz de correlación de Baker
cor.dendlist(dend_list, method = "baker")
##           [,1]      [,2]
## [1,] 1.0000000 0.9895528
## [2,] 0.9895528 1.0000000
# Coeficiente de correlación cofenética
cor_cophenetic(dend1, dend2)
## [1] 0.9925544
# Coeficiente de correlación de Baker
cor_bakers_gamma(dend1, dend2)
## [1] 0.9895528

COMPARACIÓN DE DENDROGRAMAS

# Crea múltiples dendrogramas encadenándolos
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

# Calcular la matriz de correlación
dend_list <- dendlist("Complete" = dend1, "Single" = dend2,
"Average" = dend3, "Centroid" = dend4)
cors <- cor.dendlist(dend_list)

# Imprimir matriz de correlación
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
# Visualizar la matriz de correlación usando el paquete corrplot
library(corrplot)
corrplot(cors, "pie", "lower")

Ejemplo de capítulo 9

# Cargar datos
data(USArrests)

# Calcular distancias y realizar agrupamiento jerárquico
dd <- dist(scale(USArrests), method = "euclidean")
hc <- hclust(dd, method = "ward.D2")

Para crear dendrogramas básicos

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
fviz_dend(hc, cex = 0.5, horiz = TRUE)

# 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 = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
# rect_fill = TRUE)
fviz_dend(hc, k = 4, # Cortado en cuatro grupos
cex = 0.5, # tamaño de etiqueta
k_colors = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
color_labels_by_k = TRUE, # Etiquetas de color por grupos
ggtheme = theme_gray() # Cambiar tema
)

fviz_dend(hc, cex = 0.5, k = 4, # Cortar en cuatro grupos
k_colors = "jco")

# Si quieres dibujar un dendrograma horizontal con rectángulos alrededor de los clústeres
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")

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

require("igraph")
fviz_dend(hc, k = 4, # Cortado en cuatro grupos
k_colors = "jco",
type = "phylogenic", repel = TRUE,
phylo_layout = "layout.gem")

Caso de dendrograma con gran cantidad de datos

Ampliando el dendrograma

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

Trazando un subárbol de dendrogramas

# Crea un gráfico del dendrograma completo,
# y extrae los datos del dendrograma.
dend_plot <- fviz_dend(hc, k = 4, # Cortado en cuatro grupos
cex = 0.5, # tamaño de etiqueta
k_colors = "jco"
)
dend_data <- attr(dend_plot, "dendrogram") # Extraer datos del dendrograma
# Cortar el dendrograma a una altura h = 10
dend_cuts <- cut(dend_data, h = 10)
# Visualizar la versión truncada que contiene dos ramas
fviz_dend(dend_cuts$upper)

# Trazar el dendrograma completo
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")

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

Guardar dendrograma en una página PDF grande

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

Manipulación de dendrogramas mediante dendextend

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

library(dendextend)
dend <- USArrests[1:5,] %>% # datos
scale %>% # escala de datos
dist %>% # Calcular una matriz de distancias,
hclust(method = "ward.D2") %>% # Agrupamiento jerárquico
as.dendrogram # Convierte el objeto en un dendrograma.
plot(dend)

library(dendextend)
# 1. Crea un dendrograma personalizado
mycols <- c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07")
dend <- as.dendrogram(hc) %>%
set("branches_lwd", 1) %>% # Ancho de línea de las ramas
set("branches_k_color", mycols, k = 4) %>% # Colorea las ramas por grupos
set("labels_colors", mycols, k = 4) %>% # Etiquetas de color por grupos
set("labels_cex", 0.5) # Cambiar tamaño de etiqueta
# 2. Crear gráfico
fviz_dend(dend)

5. Ejemplo de aplicación de Análisis de Cluster

Presente un ejemplo de aplicación de Análisis de Cluster, con datos de un caso de los disponibles en https://www.kaggle.com/datasets (puede buscar dentro del sitio por la palabra clave “Cluster”.

El conjunto de datos seleccionado contiene información clínica de pacientes con enfermedades cardíacas: “Heart Disease patients: Targeting treatment for heart disease patients”, incluyendo variables como edad, sexo, tipo de dolor de pecho, presión arterial en reposo, colesterol, glucosa, frecuencia cardíaca máxima y presencia de angina inducida por ejercicio. Estas características permiten identificar patrones comunes entre pacientes que podrían responder de forma similar a ciertos tratamientos.

Para el análisis se seleccionó el método K-means, ya que el conjunto de datos es relativamente pequeño y está compuesto principalmente por variables numéricas, lo que lo hace adecuado para este algoritmo. K-means permite agrupar a los pacientes en clústeres basados en similitudes clínicas, facilitando la identificación de perfiles de riesgo y posibles líneas de tratamiento para grupos con características similares.

Carga y Preparación de Datos

# Cargar los datos
datos <- read.csv("E:/Metodos/heart_disease_patients.csv")

# Ver la estructura de los datos
str(datos)
## 'data.frame':    303 obs. of  12 variables:
##  $ id      : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ age     : int  63 67 67 37 41 56 62 57 63 53 ...
##  $ sex     : int  1 1 1 1 0 1 0 0 1 1 ...
##  $ cp      : int  1 4 4 3 2 2 4 4 4 4 ...
##  $ trestbps: int  145 160 120 130 130 120 140 120 130 140 ...
##  $ chol    : int  233 286 229 250 204 236 268 354 254 203 ...
##  $ fbs     : int  1 0 0 0 0 0 0 0 0 1 ...
##  $ restecg : int  2 2 2 0 2 0 2 0 2 2 ...
##  $ thalach : int  150 108 129 187 172 178 160 163 147 155 ...
##  $ exang   : int  0 1 1 0 0 0 0 1 0 1 ...
##  $ oldpeak : num  2.3 1.5 2.6 3.5 1.4 0.8 3.6 0.6 1.4 3.1 ...
##  $ slope   : int  3 2 2 3 1 1 3 1 2 3 ...
head(datos, 3)
##   id age sex cp trestbps chol fbs restecg thalach exang oldpeak slope
## 1  1  63   1  1      145  233   1       2     150     0     2.3     3
## 2  2  67   1  4      160  286   0       2     108     1     1.5     2
## 3  3  67   1  4      120  229   0       2     129     1     2.6     2
# Verificar que todas las variables sean numéricas
summary(datos)
##        id             age             sex               cp       
##  Min.   :  1.0   Min.   :29.00   Min.   :0.0000   Min.   :1.000  
##  1st Qu.: 76.5   1st Qu.:48.00   1st Qu.:0.0000   1st Qu.:3.000  
##  Median :152.0   Median :56.00   Median :1.0000   Median :3.000  
##  Mean   :152.0   Mean   :54.44   Mean   :0.6799   Mean   :3.158  
##  3rd Qu.:227.5   3rd Qu.:61.00   3rd Qu.:1.0000   3rd Qu.:4.000  
##  Max.   :303.0   Max.   :77.00   Max.   :1.0000   Max.   :4.000  
##     trestbps          chol            fbs            restecg      
##  Min.   : 94.0   Min.   :126.0   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:120.0   1st Qu.:211.0   1st Qu.:0.0000   1st Qu.:0.0000  
##  Median :130.0   Median :241.0   Median :0.0000   Median :1.0000  
##  Mean   :131.7   Mean   :246.7   Mean   :0.1485   Mean   :0.9901  
##  3rd Qu.:140.0   3rd Qu.:275.0   3rd Qu.:0.0000   3rd Qu.:2.0000  
##  Max.   :200.0   Max.   :564.0   Max.   :1.0000   Max.   :2.0000  
##     thalach          exang           oldpeak         slope      
##  Min.   : 71.0   Min.   :0.0000   Min.   :0.00   Min.   :1.000  
##  1st Qu.:133.5   1st Qu.:0.0000   1st Qu.:0.00   1st Qu.:1.000  
##  Median :153.0   Median :0.0000   Median :0.80   Median :2.000  
##  Mean   :149.6   Mean   :0.3267   Mean   :1.04   Mean   :1.601  
##  3rd Qu.:166.0   3rd Qu.:1.0000   3rd Qu.:1.60   3rd Qu.:2.000  
##  Max.   :202.0   Max.   :1.0000   Max.   :6.20   Max.   :3.000

Estandarización de variables

df <- scale(datos) # Scaling the data
# View the firt 3

Determinación del número Óptimo de Clusters - Método del Codo

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

Determinación del número Óptimo de Clusters - Método de la Silueta

# Método de la silueta
fviz_nbclust(df, kmeans, method = "silhouette") +
  labs(title = "Metodo de la Silueta",
       subtitle = "Mayor valor promedio de silueta indica mejor k") +
  theme_minimal()

Determinación del Número Óptimo de Clusters - Método Gap Statistic

# Método Gap Statistic (más robusto)
fviz_nbclust(df, kmeans, method = "gap_stat") +
  labs(title = "Metodo Gap Statistic",
       subtitle = "Mayor gap statistic indica mejor k") +
  theme_minimal()

Comparativa de Métodos para determinar K Óptimo

# Comparar los tres métodos juntos
library(gridExtra)

plot1 <- fviz_nbclust(df, kmeans, method = "wss") + 
  ggtitle("Metodo del Codo")

plot2 <- fviz_nbclust(df, kmeans, method = "silhouette") + 
  ggtitle("Metodo Silueta")

plot3 <- fviz_nbclust(df, kmeans, method = "gap_stat") + 
  ggtitle("Gap Statistic")

grid.arrange(plot1, plot2, plot3, ncol = 3)

Visualización de Clusters para Diferentes Valores de K

# Visualizar clusters para diferentes valores de k
par(mfrow = c(2, 2))
for (k in 2:5) {
  set.seed(123)
  km_temp <- kmeans(df, centers = k, nstart = 25)
  
  # PCA para visualización 2D
  pca <- prcomp(df)
  plot_df <- data.frame(
    PC1 = pca$x[, 1],
    PC2 = pca$x[, 2],
    Cluster = as.factor(km_temp$cluster)
  )
  
  plot(plot_df$PC1, plot_df$PC2, col = plot_df$Cluster, 
       pch = 19, main = paste("K =", k),
       xlab = "PC1", ylab = "PC2")
  legend("topright", legend = levels(plot_df$Cluster), 
         col = 1:k, pch = 19)
}

Resumen de recomendaciones y distribución de Cluster

# Resumen de recomendaciones
cat("=== RESUMEN PARA ELECCIÓN DE K ===\n")
cat("1. Método del Codo: Observar gráfico wss (buscar el codo)\n")
cat("2. Método Silueta: Elegir k con mayor valor de silueta\n") 
cat("3. Gap Statistic: Elegir k con mayor gap\n")
cat("4. Contexto médico: Considerar utilidad clínica práctica\n")
cat("5. Tamaños de cluster: Evitar clusters muy pequeños (< 5% datos)\n")

# Verificar tamaños de cluster para k candidatos
for (k_candidato in 2:6) {
  set.seed(123)
  km_candidato <- kmeans(df, centers = k_candidato, nstart = 25)
  tamanos <- prop.table(km_candidato$size) * 100
  cat("\nK =", k_candidato, "- Distribucion:", 
      paste(round(tamanos, 1), "%", collapse = ", "))
}
## === RESUMEN PARA ELECCIÓN DE K ===
## 1. Método del Codo: Observar gráfico wss (buscar el codo)
## 2. Método Silueta: Elegir k con mayor valor de silueta
## 3. Gap Statistic: Elegir k con mayor gap
## 4. Contexto médico: Considerar utilidad clínica práctica
## 5. Tamaños de cluster: Evitar clusters muy pequeños (< 5% datos)
## 
## K = 2 - Distribucion: 57.4 %, 42.6 %
## K = 3 - Distribucion: 14.5 %, 49.8 %, 35.6 %
## K = 4 - Distribucion: 24.4 %, 25.4 %, 14.2 %, 36 %
## K = 5 - Distribucion: 14.9 %, 14.2 %, 19.5 %, 31 %, 20.5 %
## K = 6 - Distribucion: 17.2 %, 13.9 %, 19.8 %, 16.5 %, 14.5 %, 18.2 %

Ejecución de K-means con K=2 (de acuerdo a lo realizado y visto en los métodos anteriormente)

# Compute k-means with k = 2
set.seed(123)
km.res <- kmeans(df, 2, nstart = 25)

# Print the results
print(km.res)
## K-means clustering with 2 clusters of sizes 174, 129
## 
## Cluster means:
##            id        age         sex         cp   trestbps        chol
## 1 -0.02741950 -0.3353259 -0.07744472 -0.3924556 -0.1511972 -0.08031738
## 2  0.03698444  0.4523001  0.10446032  0.5293587  0.2039405  0.10833508
##           fbs    restecg    thalach      exang    oldpeak      slope
## 1 -0.07811724 -0.1979912  0.5462813 -0.5119809 -0.4855344 -0.5177496
## 2  0.10536745  0.2670579 -0.7368446  0.6905788  0.6549069  0.6983600
## 
## Clustering vector:
##   [1] 2 2 2 1 1 1 2 1 2 2 1 1 2 1 1 1 1 1 1 1 2 1 1 1 2 1 1 2 1 2 1 2 1 1 1 1 2
##  [38] 2 2 2 2 1 1 1 1 2 1 2 1 2 1 1 1 1 2 2 1 1 2 1 2 2 2 1 2 2 2 1 2 2 1 1 2 1
##  [75] 1 1 2 1 1 2 2 1 1 2 1 1 1 1 1 1 2 2 2 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 2 1 2
## [112] 2 1 2 2 1 1 1 2 2 2 2 2 2 1 1 2 2 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 2 2 1 2 1
## [149] 1 1 1 1 2 2 2 2 1 1 2 1 1 2 1 2 1 1 1 1 1 1 2 2 2 2 2 2 1 2 1 1 1 2 1 2 1
## [186] 1 1 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 1 1 1 2 2 2 1 2 1 1 1 2 1 1 1 2 2 1 1 1
## [223] 1 2 2 1 1 1 2 2 1 2 1 2 1 2 2 1 1 1 1 1 1 1 1 2 1 2 1 1 2 2 2 1 1 1 1 2 1
## [260] 1 1 1 1 1 2 2 2 2 1 1 2 2 2 2 1 1 1 1 1 2 2 1 2 1 1 2 2 1 1 1 2 1 2 2 2 1
## [297] 2 2 1 2 2 1 1
## 
## Within cluster sum of squares by cluster:
## [1] 1609.557 1437.287
##  (between_SS / total_SS =  15.9 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"

Análisis de características por Cluster

aggregate(datos, by=list(cluster=km.res$cluster), mean)
##   cluster       id      age       sex       cp trestbps     chol       fbs
## 1       1 149.5977 51.40805 0.6436782 2.781609 129.0287 242.5345 0.1206897
## 2       2 155.2403 58.52713 0.7286822 3.666667 135.2791 252.3023 0.1860465
##     restecg  thalach     exang   oldpeak    slope
## 1 0.7931034 162.1034 0.0862069 0.4758621 1.281609
## 2 1.2558140 132.7519 0.6511628 1.8000000 2.031008
dd <- cbind(datos, cluster = km.res$cluster)
head(dd)
##   id age sex cp trestbps chol fbs restecg thalach exang oldpeak slope cluster
## 1  1  63   1  1      145  233   1       2     150     0     2.3     3       2
## 2  2  67   1  4      160  286   0       2     108     1     1.5     2       2
## 3  3  67   1  4      120  229   0       2     129     1     2.6     2       2
## 4  4  37   1  3      130  250   0       0     187     0     3.5     3       1
## 5  5  41   0  2      130  204   0       2     172     0     1.4     1       1
## 6  6  56   1  2      120  236   0       0     178     0     0.8     1       1

Extracción de información del Modelo K-means

# Cluster number for each of the observations
km.res$cluster
##   [1] 2 2 2 1 1 1 2 1 2 2 1 1 2 1 1 1 1 1 1 1 2 1 1 1 2 1 1 2 1 2 1 2 1 1 1 1 2
##  [38] 2 2 2 2 1 1 1 1 2 1 2 1 2 1 1 1 1 2 2 1 1 2 1 2 2 2 1 2 2 2 1 2 2 1 1 2 1
##  [75] 1 1 2 1 1 2 2 1 1 2 1 1 1 1 1 1 2 2 2 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 2 1 2
## [112] 2 1 2 2 1 1 1 2 2 2 2 2 2 1 1 2 2 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 2 2 1 2 1
## [149] 1 1 1 1 2 2 2 2 1 1 2 1 1 2 1 2 1 1 1 1 1 1 2 2 2 2 2 2 1 2 1 1 1 2 1 2 1
## [186] 1 1 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 1 1 1 2 2 2 1 2 1 1 1 2 1 1 1 2 2 1 1 1
## [223] 1 2 2 1 1 1 2 2 1 2 1 2 1 2 2 1 1 1 1 1 1 1 1 2 1 2 1 1 2 2 2 1 1 1 1 2 1
## [260] 1 1 1 1 1 2 2 2 2 1 1 2 2 2 2 1 1 1 1 1 2 2 1 2 1 1 2 2 1 1 1 2 1 2 2 2 1
## [297] 2 2 1 2 2 1 1
head(km.res$cluster, 2)
## [1] 2 2
# Cluster size
km.res$size
## [1] 174 129
# Cluster means
km.res$centers
##            id        age         sex         cp   trestbps        chol
## 1 -0.02741950 -0.3353259 -0.07744472 -0.3924556 -0.1511972 -0.08031738
## 2  0.03698444  0.4523001  0.10446032  0.5293587  0.2039405  0.10833508
##           fbs    restecg    thalach      exang    oldpeak      slope
## 1 -0.07811724 -0.1979912  0.5462813 -0.5119809 -0.4855344 -0.5177496
## 2  0.10536745  0.2670579 -0.7368446  0.6905788  0.6549069  0.6983600

Visualización de los Clusters con PCA

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

Análisis detallado de características clínicas por Cluster

# ANÁLISIS DETALLADO DE LOS CLUSTERS K=2
cat("=== ANÁLISIS CLÍNICO DE LOS CLUSTERS ===\n\n")
## === ANÁLISIS CLÍNICO DE LOS CLUSTERS ===
# CARACTERÍSTICAS POR CLUSTER
caracteristicas_clusters <- aggregate(datos, by = list(cluster = km.res$cluster), mean)
print(caracteristicas_clusters)
##   cluster       id      age       sex       cp trestbps     chol       fbs
## 1       1 149.5977 51.40805 0.6436782 2.781609 129.0287 242.5345 0.1206897
## 2       2 155.2403 58.52713 0.7286822 3.666667 135.2791 252.3023 0.1860465
##     restecg  thalach     exang   oldpeak    slope
## 1 0.7931034 162.1034 0.0862069 0.4758621 1.281609
## 2 1.2558140 132.7519 0.6511628 1.8000000 2.031008

Perfil Clínico detallado de cada cluster

# PERFIL CLÍNICO DE CADA CLUSTER 
cat("\n--- PERFIL CLÍNICO CLUSTER 1 ---\n")
## 
## --- PERFIL CLÍNICO CLUSTER 1 ---
cluster1 <- caracteristicas_clusters[caracteristicas_clusters$cluster == 1, -1] # Excluir columna cluster
cat("• Número de pacientes:", km.res$size[1], "\n")
## • Número de pacientes: 174
cat("• Edad promedio:", round(cluster1$age, 1), "años\n")
## • Edad promedio: 51.4 años
cat("• Presión arterial:", round(cluster1$trestbps, 1), "mm Hg\n")
## • Presión arterial: 129 mm Hg
cat("• Colesterol:", round(cluster1$chol, 1), "mg/dl\n")
## • Colesterol: 242.5 mg/dl
cat("• Frecuencia cardíaca máxima:", round(cluster1$thalach, 1), "\n")
## • Frecuencia cardíaca máxima: 162.1
cat("• ST depression:", round(cluster1$oldpeak, 2), "\n")
## • ST depression: 0.48
cat("• Ejercicio angina:", round(cluster1$exang, 2), "(1=sí, 0=no)\n")
## • Ejercicio angina: 0.09 (1=sí, 0=no)
cat("• Dolor pecho tipo:", round(cluster1$cp, 2), "\n")
## • Dolor pecho tipo: 2.78
cat("• Glucosa en ayunas > 120:", round(cluster1$fbs, 2), "(1=sí, 0=no)\n")
## • Glucosa en ayunas > 120: 0.12 (1=sí, 0=no)
cat("\n--- PERFIL CLÍNICO CLUSTER 2 ---\n")
## 
## --- PERFIL CLÍNICO CLUSTER 2 ---
cluster2 <- caracteristicas_clusters[caracteristicas_clusters$cluster == 2, -1] # Excluir columna cluster
cat("• Número de pacientes:", km.res$size[2], "\n")
## • Número de pacientes: 129
cat("• Edad promedio:", round(cluster2$age, 1), "años\n")
## • Edad promedio: 58.5 años
cat("• Presión arterial:", round(cluster2$trestbps, 1), "mm Hg\n")
## • Presión arterial: 135.3 mm Hg
cat("• Colesterol:", round(cluster2$chol, 1), "mg/dl\n")
## • Colesterol: 252.3 mg/dl
cat("• Frecuencia cardíaca máxima:", round(cluster2$thalach, 1), "\n")
## • Frecuencia cardíaca máxima: 132.8
cat("• ST depression:", round(cluster2$oldpeak, 2), "\n")
## • ST depression: 1.8
cat("• Ejercicio angina:", round(cluster2$exang, 2), "(1=sí, 0=no)\n")
## • Ejercicio angina: 0.65 (1=sí, 0=no)
cat("• Dolor pecho tipo:", round(cluster2$cp, 2), "\n")
## • Dolor pecho tipo: 3.67
cat("• Glucosa en ayunas > 120:", round(cluster2$fbs, 2), "(1=sí, 0=no)\n")
## • Glucosa en ayunas > 120: 0.19 (1=sí, 0=no)

Identificación de variables diferenciadoras entre clusters

# COMPARACIÓN CLÍNICA ENTRE CLUSTERS 
cat("\n=== COMPARACIÓN ENTRE CLUSTERS ===\n")
## 
## === COMPARACIÓN ENTRE CLUSTERS ===
# Calcular diferencias absolutas
diferencias <- abs(cluster1 - cluster2)

# Convertir a vector numérico para ordenar
diferencias_vector <- as.numeric(diferencias)
nombres_variables <- names(datos)

# Ordenar variables por diferencia
indices_ordenados <- order(diferencias_vector, decreasing = TRUE)
variables_diferenciadoras <- nombres_variables[indices_ordenados[1:5]]

cat("Variables que más diferencian los clusters:\n")
## Variables que más diferencian los clusters:
for (i in 1:5) {
  var <- variables_diferenciadoras[i]
  diff_val <- diferencias_vector[indices_ordenados[i]]
  cat("•", var, ": Diferencia =", round(diff_val, 2), "\n")
  cat("  (Cluster 1:", round(cluster1[[var]], 2), "vs Cluster 2:", round(cluster2[[var]], 2), ")\n")
}
## • thalach : Diferencia = 29.35 
##   (Cluster 1: 162.1 vs Cluster 2: 132.75 )
## • chol : Diferencia = 9.77 
##   (Cluster 1: 242.53 vs Cluster 2: 252.3 )
## • age : Diferencia = 7.12 
##   (Cluster 1: 51.41 vs Cluster 2: 58.53 )
## • trestbps : Diferencia = 6.25 
##   (Cluster 1: 129.03 vs Cluster 2: 135.28 )
## • id : Diferencia = 5.64 
##   (Cluster 1: 149.6 vs Cluster 2: 155.24 )

Interpretación médica de los resultados

# INTERPRETACIÓN MÉDICA
cat("\n=== INTERPRETACIÓN MÉDICA ===\n")
## 
## === INTERPRETACIÓN MÉDICA ===
cat("BASADO EN K=2 CLUSTERS:\n\n")
## BASADO EN K=2 CLUSTERS:
# Determinar qué cluster es de mayor riesgo basado en las diferencias
if (cluster1$thalach < cluster2$thalach) {
  cat("CLUSTER 1 (Perfil de MAYOR RIESGO):\n")
  cat("   - Menor frecuencia cardíaca máxima durante ejercicio\n")
  cat("   - Indicador de menor capacidad funcional cardíaca\n")
  cat("   - Posible enfermedad coronaria más severa\n")
  cat("   - Mayor riesgo de eventos cardiovasculares\n\n")
  
  cat("CLUSTER 2 (Perfil de MENOR RIESGO):\n")
  cat("   - Mejor frecuencia cardíaca máxima durante ejercicio\n")
  cat("   - Mejor capacidad funcional cardíaca\n")
  cat("   - Posible enfermedad coronaria menos avanzada\n")
  cat("   - Menor riesgo de eventos cardiovasculares\n")
} else {
  cat("CLUSTER 1 (Perfil de MENOR RIESGO):\n")
  cat("   - Mejor función cardíaca durante ejercicio\n")
  cat("   - Menor afectación isquémica\n")
  cat("   - Posiblemente enfermedad coronaria menos severa\n\n")
  
  cat("CLUSTER 2 (Perfil de MAYOR RIESGO):\n")
  cat("   - Mayor afectación durante ejercicio\n")
  cat("   - Mayor depresión del ST\n")
  cat("   - Más episodios de angina\n")
  cat("   - Posiblemente enfermedad coronaria más avanzada\n")
}
## CLUSTER 1 (Perfil de MENOR RIESGO):
##    - Mejor función cardíaca durante ejercicio
##    - Menor afectación isquémica
##    - Posiblemente enfermedad coronaria menos severa
## 
## CLUSTER 2 (Perfil de MAYOR RIESGO):
##    - Mayor afectación durante ejercicio
##    - Mayor depresión del ST
##    - Más episodios de angina
##    - Posiblemente enfermedad coronaria más avanzada

Evaluación de la calidad del Clustering

# ESTADÍSTICAS DE CALIDAD DEL CLUSTERING
cat("\n=== CALIDAD DEL CLUSTERING ===\n")
## 
## === CALIDAD DEL CLUSTERING ===
cat("• Tamaño Cluster 1:", km.res$size[1], "pacientes (", 
    round(km.res$size[1]/sum(km.res$size)*100, 1), "%)\n")
## • Tamaño Cluster 1: 174 pacientes ( 57.4 %)
cat("• Tamaño Cluster 2:", km.res$size[2], "pacientes (", 
    round(km.res$size[2]/sum(km.res$size)*100, 1), "%)\n")
## • Tamaño Cluster 2: 129 pacientes ( 42.6 %)
cat("• Varianza explicada entre clusters:", 
    round((km.res$betweenss/km.res$totss)*100, 1), "%\n")
## • Varianza explicada entre clusters: 15.9 %
cat("• Total within-cluster sum of squares:", round(km.res$tot.withinss, 2), "\n")
## • Total within-cluster sum of squares: 3046.84
# Calcular silueta promedio (manejo de error)
tryCatch({
  silueta_global <- mean(silhouette(km.res$cluster, dist(df))[, 3])
  cat("• Coeficiente de silueta promedio:", round(silueta_global, 3), "\n")
  cat("  - > 0.5: Estructura fuerte\n")
  cat("  - 0.25-0.5: Estructura razonable\n")
  cat("  - < 0.25: Estructura débil\n")
}, error = function(e) {
  cat("• Coeficiente de silueta: No disponible\n")
})
## • Coeficiente de silueta promedio: 0.157 
##   - > 0.5: Estructura fuerte
##   - 0.25-0.5: Estructura razonable
##   - < 0.25: Estructura débil

Recomendaciones clínicas basadas en los resultados

# 6. RECOMENDACIONES CLÍNICAS
cat("\n=== RECOMENDACIONES PARA MÉDICOS ===\n")
## 
## === RECOMENDACIONES PARA MÉDICOS ===
cat("1. TRATAMIENTO DIFERENCIADO:\n")
## 1. TRATAMIENTO DIFERENCIADO:
cat("   • Pacientes Cluster 1: Considerar tratamiento más intensivo\n")
##    • Pacientes Cluster 1: Considerar tratamiento más intensivo
cat("   • Pacientes Cluster 2: Enfocar en prevención y monitoreo\n\n")
##    • Pacientes Cluster 2: Enfocar en prevención y monitoreo
cat("2. SEGUIMIENTO:\n")
## 2. SEGUIMIENTO:
cat("   • Estratificar frecuencia de controles según cluster\n")
##    • Estratificar frecuencia de controles según cluster
cat("   • Personalizar planes de rehabilitación cardíaca\n\n")
##    • Personalizar planes de rehabilitación cardíaca
cat("3. INTERVENCIONES ESPECÍFICAS:\n")
## 3. INTERVENCIONES ESPECÍFICAS:
cat("   • Desarrollar protocolos basados en perfiles de riesgo\n")
##    • Desarrollar protocolos basados en perfiles de riesgo
cat("   • Optimizar uso de medicamentos según características\n")
##    • Optimizar uso de medicamentos según características
cat("   • Personalizar recomendaciones de estilo de vida\n")
##    • Personalizar recomendaciones de estilo de vida

Resumen final de lo realizado

# RESUMEN EJECUTIVO FINAL
cat("\n")
cat(paste(rep("=", 60), collapse = ""), "\n")
## ============================================================
cat("RESUMEN EJECUTIVO - CLUSTERING K-MEANS (K=2)\n")
## RESUMEN EJECUTIVO - CLUSTERING K-MEANS (K=2)
cat(paste(rep("=", 60), collapse = ""), "\n\n")
## ============================================================
cat("OBJETIVO ALCANZADO: Se identificaron 2 perfiles clínicos distintos\n")
## OBJETIVO ALCANZADO: Se identificaron 2 perfiles clínicos distintos
cat("   de pacientes con enfermedad cardíaca usando K-means.\n\n")
##    de pacientes con enfermedad cardíaca usando K-means.
cat("RESULTADOS PRINCIPALES:\n")
## RESULTADOS PRINCIPALES:
cat("• Cluster 1:", km.res$size[1], "pacientes (", round(km.res$size[1]/sum(km.res$size)*100, 1), "%)\n")
## • Cluster 1: 174 pacientes ( 57.4 %)
cat("• Cluster 2:", km.res$size[2], "pacientes (", round(km.res$size[2]/sum(km.res$size)*100, 1), "%)\n")
## • Cluster 2: 129 pacientes ( 42.6 %)
cat("• Varianza explicada:", round((km.res$betweenss/km.res$totss)*100, 1), "%\n")
## • Varianza explicada: 15.9 %
cat("• Las variables más diferenciadoras:", paste(variables_diferenciadoras[1:3], collapse = ", "), "\n\n")
## • Las variables más diferenciadoras: thalach, chol, age
cat("IMPLICACIONES CLÍNICAS:\n")
## IMPLICACIONES CLÍNICAS:
cat("• Permite medicina personalizada basada en perfiles de riesgo\n")
## • Permite medicina personalizada basada en perfiles de riesgo
cat("• Optimiza asignación de recursos y tratamientos\n")
## • Optimiza asignación de recursos y tratamientos
cat("• Mejora predicción de respuestas a terapias\n")
## • Mejora predicción de respuestas a terapias
cat("• Facilita diseño de estudios clínicos estratificados\n\n")
## • Facilita diseño de estudios clínicos estratificados
cat("PRÓXIMOS PASOS RECOMENDADOS:\n")
## PRÓXIMOS PASOS RECOMENDADOS:
cat("1. Validar clusters con outcomes clínicos reales\n")
## 1. Validar clusters con outcomes clínicos reales
cat("2. Desarrollar protocolos de tratamiento específicos\n")
## 2. Desarrollar protocolos de tratamiento específicos
cat("3. Implementar sistema de estratificación en práctica clínica\n")
## 3. Implementar sistema de estratificación en práctica clínica
cat(paste(rep("=", 60), collapse = ""), "\n")
## ============================================================

Referencias

Aldás Manzano, J., & Uriel Jiménez, E. (2017). Análisis multivariante aplicado con r (2nd ed.). Paraninfo. https://es.scribd.com/document/523409933/487385836-Analisis-Multivariante-Aplicado-Con-R-Joaquin-Aldas-Ezequiel-Uriel-2a-Edicion-1-1-PDF
De la Fuente Fernández, S. (2011). Análisis de conglomerados. Facultad de Ciencias Económicas y Empresariales, Universidad Autónoma de Madrid. https://www.fuenterrebollo.com/Economicas/ECONOMETRIA/SEGMENTACION/CONGLOMERADOS/conglomerados.pdf
Kassambara, A. (2017). Practical guide to cluster analysis in r: Unsupervised machine learning (1st ed.). STHDA. https://books.google.com.sv/books?id=-q3snAAACAAJ