Flujo de Trabajo en Clustering

Evaluación, Estimación y Validación

Preparación de los Datos

Antes de comenzar, generamos nuestros datos simulados y cargamos las librerías necesarias (factoextra, cluster, fpc).

Es crucial escalar los datos y asegurar que tengan nombres de columna para que las funciones de visualización funcionen correctamente.

library(factoextra)
library(cluster)
library(fpc)

# 1. Simulación de datos 
set.seed(2)
sim_data <- rbind(
  cbind(rnorm(25, mean = 3), rnorm(25, mean = -4)),
  cbind(rnorm(25), rnorm(25))
)

# 2. Asignar nombres de columna (Vital para fviz_cluster)
colnames(sim_data) <- c("Var1", "Var2")

# 3. Escalar los datos
df <- scale(sim_data)

plot(df)

Paso 1: Evaluación de Tendencia

¿Son los datos “agrupables”?

Antes de aplicar cualquier algoritmo, debemos responder: ¿Contienen los datos estructura real o son solo ruido aleatorio?

Para esto utilizamos el Estadístico de Hopkins (\(H\)).

Este estadístico compara las distancias entre sus datos reales contra las distancias de puntos generados aleatoriamente (distribución uniforme) en el mismo espacio.

El Estadístico de Hopkins

La fórmula compara dos sumas de distancias a los “vecinos más cercanos”:

\[H = \frac{\sum_{i=1}^m U_i}{\sum_{i=1}^m U_i + \sum_{i=1}^m W_i}\]

Donde:

  • \(U_i\): Distancia de un punto artificial (aleatorio) a su vecino real más cercano (buscamos huecos vacíos).
  • \(W_i\): Distancia de un punto real a su vecino real más cercano (buscamos compacidad).

Interpretación: * \(H \approx 0.5\): Los datos son ruido aleatorio (no agrupables). * \(H \rightarrow 1\): Los datos tienen una fuerte tendencia al agrupamiento.

Hopkins en R

Calculamos \(H\) usando get_clust_tendency.

# n = número de puntos a muestrear (n-1)
res <- get_clust_tendency(df, n = nrow(df)-1, graph = FALSE)

print(paste("Estadístico de Hopkins:", round(res$hopkins_stat, 3)))
[1] "Estadístico de Hopkins: 0.583"

Conclusión: Dado que \(H\) es cercano a 1 (muy por encima de 0.5), confirmamos que los datos tienen estructura y procedemos.

Evaluación Visual: El gráfico VAT

Además del número, necesitamos ver la estructura. Utilizamos el gráfico VAT (Visual Assessment of Cluster Tendency).

Es una matriz de disimilitud ordenada. * Ejes: Observaciones reordenadas por similitud. * Color: Distancia (Rojo = Cerca, Azul = Lejos).

Buscamos bloques cuadrados oscuros/rojos a lo largo de la diagonal.

Visualizando el VAT

# get_clust_tendency ya genera el gráfico si graph = TRUE
res_vat <- get_clust_tendency(df, n = nrow(df)-1, graph = TRUE)
res_vat$plot

Observamos bloques claros en la diagonal, lo que confirma visualmente la existencia de clusters.

Paso 2: Selección de Parámetros

Determinando el \(k\) óptimo

No usaremos el método del codo (subjetivo), sino el Estadístico Gap.

Este método responde: “¿Cuánto mejor es mi agrupación comparada con agrupar ruido aleatorio?”

  1. Calcula la compacidad de tus clusters (\(W_k\)).
  2. Simula ruido aleatorio y lo agrupa
  3. El Gap es la diferencia. Buscamos maximizar el Gap.

Estadístico Gap en R

Utilizamos fviz_nbclust con el método gap_stat.

set.seed(123)
# nboot = 50 para demostración (usar 500 en producción)
gap_stat <- fviz_nbclust(df, kmeans, method = "gap_stat", nboot = 50) +
  labs(subtitle = "Método del Estadístico Gap")

print(gap_stat)

El algoritmo sugiere el número óptimo de clusters (generalmente marcado con una línea discontinua). Asumiremos \(k=2\) basado en la simulación.

Paso 3: Estimación (K-Means)

Ajuste del Modelo

Dado que ya conocemos K-means, procedemos directamente a la estimación utilizando el \(k\) sugerido por el paso anterior.

Es buena práctica usar nstart = 25 para probar múltiples configuraciones iniciales y evitar óptimos locales.

# Fijamos k=2 basado en la estructura de los datos simulados
final_k <- 2

set.seed(123)
km_final <- kmeans(df, centers = final_k, nstart = 25)

# Vemos el tamaño de los grupos
print(km_final$size)
[1] 24 26

Paso 4: Validación Interna

Análisis de Silueta

Una vez creado el modelo, debemos validar la calidad de la asignación para cada punto. Usamos el Coeficiente de Silueta (\(s_i\)):

\[s_i = \frac{b_i - a_i}{\max(a_i, b_i)}\]

  • \(a_i\): Cohesión (distancia media a su propio cluster).
  • \(b_i\): Separación (distancia media al cluster vecino más cercano).

Interpretación: * Cercano a 1: Bien clasificado. * Cercano a 0: En la frontera. * Negativo: Probablemente mal clasificado.

Visualización de la Silueta

# 1. Calcular siluetas (necesita la matriz de distancias original)
dist_matrix <- dist(df)
sil <- silhouette(km_final$cluster, dist_matrix)

# 2. Visualizar
fviz_silhouette(sil, palette = "jco", ggtheme = theme_minimal())
  cluster size ave.sil.width
1       1   24          0.61
2       2   26          0.60

Buscamos barras altas y positivas. Si hubiera barras negativas (hacia abajo), indicarían mismatch.

Paso 5: Visualización Final

Mapa de Clusters (2D)

Finalmente, presentamos los resultados. Como los datos pueden tener >2 dimensiones, fviz_cluster realiza automáticamente un PCA (Componentes Principales) para proyectar los datos en 2D.

Dibuja elipses convexas alrededor de los grupos para visualizar la separación.

Resultado Final

fviz_cluster(km_final, data = df,
             palette = "jco", 
             geom = "point", 
             ellipse.type = "convex", 
             ggtheme = theme_minimal(),
             main = "Resultado Final de K-Means"
)