INTRODUCCIÓN

1. Carga de datos

datos1<- read.csv("C:/Users/marco/Documents/Rscripts/consumo_clientes.csv")
datos2<- read.csv("C:/Users/marco/Documents/Rscripts/consumo_clientes_2.csv")

2. Verificación de datos

En el primer archivo: Todas las variables son de tipo entero (int).

En el segundo archivo: Las variables son de tipo numérico (num). Los datos se encuentran normalizados

# Verificar la estructura de los datos
str(datos1)
## 'data.frame':    32 obs. of  7 variables:
##  $ X               : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Fresh           : int  12669 7057 6353 13265 22615 9413 12126 7579 5963 6006 ...
##  $ Milk            : int  9656 9810 8808 1196 5410 8259 3199 4956 3648 11093 ...
##  $ Grocery         : int  7561 9568 7684 4221 7198 5126 6975 9426 6192 18881 ...
##  $ Frozen          : int  214 1762 2405 6404 3915 666 480 1669 425 1159 ...
##  $ Detergents_Paper: int  2674 3293 3516 507 1777 1795 3140 3321 1716 7425 ...
##  $ Delicassen      : int  1338 1776 7844 1788 5185 1451 545 2566 750 2098 ...
str(datos2)
## 'data.frame':    33 obs. of  7 variables:
##  $ X               : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Fresh           : num  -0.2472 0.4641 0.0107 0.3655 -0.3145 ...
##  $ Milk            : num  -0.617 -0.27 -0.417 -0.792 -0.881 ...
##  $ Grocery         : num  -0.675 -0.386 -0.66 -0.561 -0.193 ...
##  $ Frozen          : num  -0.1426 2.5361 0.2419 -0.0289 0.0362 ...
##  $ Detergents_Paper: num  -0.6104 -0.1015 0.0639 -0.6931 -0.437 ...
##  $ Delicassen      : num  0.738 1.948 0.64 0.304 -0.444 ...
# Mostrar las primeras filas
head(datos1)
##   X Fresh Milk Grocery Frozen Detergents_Paper Delicassen
## 1 1 12669 9656    7561    214             2674       1338
## 2 2  7057 9810    9568   1762             3293       1776
## 3 3  6353 8808    7684   2405             3516       7844
## 4 4 13265 1196    4221   6404              507       1788
## 5 5 22615 5410    7198   3915             1777       5185
## 6 6  9413 8259    5126    666             1795       1451
head(datos2)
##   X       Fresh       Milk     Grocery      Frozen Detergents_Paper Delicassen
## 1 1 -0.24716064 -0.6167425 -0.67450893 -0.14255660      -0.61043983  0.7382564
## 2 2  0.46411094 -0.2699829 -0.38625002  2.53605371      -0.10147801  1.9480556
## 3 3  0.01066055 -0.4167840 -0.66000299  0.24190563       0.06385354  0.6401954
## 4 4  0.36550909 -0.7922857 -0.56069315 -0.02892846      -0.69310560  0.3038233
## 5 5 -0.31446968 -0.8809845 -0.19320955  0.03623463      -0.43700379 -0.4441770
## 6 6 -0.31063189 -0.3469376 -0.05670501 -0.45167402      -0.38189328 -0.8865918

3. Combinar datos

Para combinar los datos se debe normalizar las observaciones del primer conjunto de datos (datos1). Al combinarse los datos en la columna X aparecen NA, por lo que deben sobreescribirse este indicador.

# Calcular medias y desviaciones estándar (excluyendo el índice)
medias <- colMeans(datos1[,-1]) # Excluye la primera columna (Unnamed: 0)
desviaciones <- apply(datos1[,-1], 2, sd) # Calcula desviaciones estándar

# Aplicar normalización (z-score)
datos1_normalizado <- as.data.frame(scale(datos1[,-1], center = medias, scale = desviaciones))

# Añadir de nuevo el índice
datos1_normalizado$Unnamed.0 <- datos1$Unnamed.0

# Unir los datos
datos_combinados <- bind_rows(datos1_normalizado, datos2)

# Sobrescribir la columna "X" con una secuencia de 1 a la cantidad de filas
datos_combinados$X <- seq_len(nrow(datos_combinados))
head(datos_combinados)
##        Fresh        Milk     Grocery      Frozen Detergents_Paper Delicassen X
## 1 -0.1383067  0.92027590 -0.11506546 -0.81324576      -0.06159416 -0.4056681 1
## 2 -0.7172866  0.95554094  0.25985028 -0.04532845       0.22082648 -0.1756024 2
## 3 -0.7899170  0.72608918 -0.09208856  0.27364495       0.32257091  3.0117006 3
## 4 -0.0768184 -1.01701146 -0.73899102  2.25743132      -1.05029455 -0.1692992 4
## 5  0.8878043 -0.05203168 -0.18287534  1.02271157      -0.47085316  1.6150232 5
## 6 -0.4742223  0.60037159 -0.56993335 -0.58902184      -0.46264060 -0.3463132 6

4. Analisis de datos utilizando la técnica K-Means

# Escalar los datos
datos_numericos <- datos_combinados[, sapply(datos_combinados, is.numeric)]
datos_escalados <- scale(datos_numericos)

# Fijar la semilla
set.seed(123)

# Iterar sobre varios valores de k
k_min <- 2    # Mínimo número de clusters
k_max <- 10   # Máximo número de clusters

resultados <- data.frame(
  k = k_min:k_max,
  tot_withinss = numeric(k_max - k_min + 1),
  betweenss = numeric(k_max - k_min + 1)
)

for (k in k_min:k_max) {
  agrupamientos <- kmeans(x = datos_escalados, centers = k, nstart = 25)
  resultados$tot_withinss[k - k_min + 1] <- agrupamientos$tot.withinss
  resultados$betweenss[k - k_min + 1] <- agrupamientos$betweenss
}

# Visualizar los resultados
library(ggplot2)

# Gráfico para elegir el mejor número de clusters (Método del codo)
ggplot(resultados, aes(x = k, y = tot_withinss)) +
  geom_line(color = "blue") +
  geom_point(color = "red") +
  labs(
    title = "Metodo del Codo Seleccion del numero optimo de Clusters",
    x = "Numero de Clusters",
    y = "Suma de Cuadrados Totales Dentro de los Clusters"
  ) +
  theme_minimal()

.

OBS: Se determina visualmente que el número de Clusters óptimo es k=3, debido a que aquí se produce el “codo”.

5. Gráfico de agrupamiento con k=3

k <- 3 # Ajusta según el número de clusters elegido

agrupamientos <- kmeans(x = datos_escalados, centers = k, nstart = 25)

# Reducir la dimensionalidad con PCA para graficar en 2D
pca <- prcomp(datos_escalados)  # Realiza PCA sobre los datos escalados
pca_data <- data.frame(pca$x[, 1:2])  # Tomar las dos primeras componentes principales

# Agregar los grupos de los clusters a los datos PCA
pca_data$Cluster <- as.factor(agrupamientos$cluster)

# Graficar los agrupamientos
library(ggplot2)

ggplot(pca_data, aes(x = PC1, y = PC2, color = Cluster)) +
  geom_point(size = 3, alpha = 0.7) +
  labs(
    title = paste("Agrupamientos K-Means con k =", k),
    x = "Componente Principal 1",
    y = "Componente Principal 2"
  ) +
  scale_color_manual(values = c("red", "green", "blue")) +  # Colores personalizados
  theme_minimal() +
  theme(legend.title = element_blank())

. OBS: Aqui se puede ver 3 grupos (K=3), de los cuales 2 son relativamente homogeneos (verde y rojo) y el tercero se encuentra disperso (azul)

6. Agrupamientos jerarquicos: Bottom-Up

# Fijar la semilla
set.seed(123)

# Crear el agrupamiento jerárquico (hclust)
agrup_jerar <- hclust(d = dist(x = datos_escalados, method = "euclidean"), method = "complete")

# Asignar cada observación a un agrupamiento (k = número de clusters)
k <- 3
clusters <- cutree(agrup_jerar, k = k)

# Convertir el dendrograma a objeto 'dendrogram'
dendro <- as.dendrogram(agrup_jerar)

# Colorear las ramas de acuerdo con los clusters
dendro <- color_branches(dendro, k = k)

# Mejorar etiquetas
dendro <- set(dendro, "labels_cex", 0.7)  # Tamaño de las etiquetas

# Graficar el dendrograma mejorado
plot(
  dendro,
  main = paste("Dendrograma - Bottom-Up Clustering (k =", k, ")"),
  ylab = "Distancia",
  xlab = "Observaciones",
  sub = "",
  cex.main = 1.5,  # Tamaño del título
  cex.lab = 1.2    # Tamaño de etiquetas de ejes
)

. . OBS: En este caso se realizan agrupamientos mucho mas pequeños.

7. Agrupamientos jerarquicos: Top-Down

# Fijar la semilla
set.seed(123)

# Generar la matriz de distancias
mat_dist <- dist(x = datos_escalados, method = "euclidean")

# Crear el agrupamiento jerárquico (diana)
agrup_jerar_td <- diana(x = mat_dist, diss = TRUE, stand = FALSE)

# Convertir el resultado en un objeto 'dendrogram'
dendro_td <- as.dendrogram(agrup_jerar_td)

# Asignar colores a las ramas
k <- 3  # Número de clusters
dendro_td <- color_branches(dendro_td, k = k)

# Ajustar etiquetas
dendro_td <- set(dendro_td, "labels_cex", 0.7)

# Graficar el dendrograma mejorado
plot(
  dendro_td,
  main = paste("Dendrograma - Top-Down Clustering (k =", k, ")"),
  ylab = "Distancia",
  xlab = "Observaciones",
  sub = "",
  cex.main = 1.5,  # Tamaño del título
  cex.lab = 1.2    # Tamaño de etiquetas de ejes
)

. .

OBS: El dendograma Top-Down es mas pertinente con un Custering del orden de 3/4. Por ende es conveniente quedarse con este. Sin embargo a simple vista se observan al menos 5 grupos.

8. Análisis de componentes principales (CP).

# Calcular la matriz de covarianza de los datos escalados
cov.mat <- cov(datos_escalados)

# Calcular los autovalores y autovectores de la matriz de covarianza
auto.val <- eigen(cov.mat)  # Descomposición en autovalores y autovectores

# Calcular la varianza explicada
varianza_explicada <- auto.val$values / sum(auto.val$values) * 100  # Porcentaje de varianza explicada

# Crear un data frame con los componentes y la varianza explicada
df_varianza <- data.frame(
  Componente = paste0("PC", seq_along(varianza_explicada)),
  Varianza = varianza_explicada
)

# Graficar la varianza explicada para todas las componentes
library(ggplot2)

ggplot(df_varianza, aes(x = Componente, y = Varianza)) +
  geom_bar(stat = "identity", fill = "lightblue") +
  labs(
    title = "Varianza Explicada por Componentes Principales",
    x = "Componentes Principales",
    y = "Varianza Explicada (%)"
  ) +
  theme_minimal()

. . OBS: Se puede ver como el PC1 explica aproximadamente el 37% de la varianza y el PC2 un 20%. Sumando entre ambas un 57%.

9. Primeras dos componentes principales.

# Calcular la matriz de covarianza de los datos escalados
cov.mat <- cov(datos_escalados)

# Calcular los autovalores y autovectores de la matriz de covarianza
auto.val <- eigen(cov.mat)

# Transformar los datos originales en el nuevo espacio (PCA)
datos_pca <- as.matrix(datos_escalados) %*% auto.val$vectors  # Nuevas coordenadas en el espacio PCA

# Crear un data frame con las primeras dos componentes principales
df_pca <- data.frame(PC1 = datos_pca[, 1], PC2 = datos_pca[, 2])

# Graficar las primeras dos componentes principales
library(ggplot2)

ggplot(df_pca, aes(x = PC1, y = PC2)) +
  geom_point(color = "blue", alpha = 0.7) +
  labs(
    title = "Gráfico de las Primeras Dos Componentes Principales",
    x = "Componente Principal 1 (PC1)",
    y = "Componente Principal 2 (PC2)"
  ) +
  theme_minimal()

## 10. Cargas de las Componentes principales

# Los autovectores contienen las cargas de cada variable en las componentes principales
# Vamos a extraer las cargas del primer y segundo componente principal para las primeras variables
cargas <- auto.val$vectors  # Las cargas de cada variable para cada componente

# Para ver qué variables más aportan a PC1 y PC2, podemos examinar las cargas de las primeras dos columnas:
cargas_primer_componente <- cargas[, 1]  # Cargas para el primer componente principal
cargas_segundo_componente <- cargas[, 2]  # Cargas para el segundo componente principal

# Mostrar las variables que más aportan a los primeros dos componentes
cargas_primer_componente
## [1]  0.03151499 -0.54778656 -0.57802670  0.12120759 -0.53777618 -0.24568015
## [7] -0.02384518
cargas_segundo_componente
## [1]  0.536112476 -0.081903212 -0.011274135  0.602295584  0.006447934
## [6]  0.572780273 -0.121940078

. . OBS: Aqui podemos ver que en PC1, las variables que tienen las cargas más altas en valor absoluto (en este caso, las más altas son -0.57802670 y -0.54778656) son las variables 3 y 2 respectivamente. Es decir, la variable 3 explica en un 58% la PC1 y la variable 2 un 55%. En PC2, las variables que tienen las cargas más altas en valor absoluto son 0.602295584 y 0.572780273, que corresponden a las variables 4 y 6. Es decir, la variable 4 explica un 60% de la PC2 y la variable 6 un 57%.

11. Proyecciones de las variables en las CP´s

# Realizar PCA con los datos escalados
PCA <- prcomp(datos_escalados)

# Graficar el biplot con scale=0
biplot(PCA, scale = 0)

RESULTADOS

Agrupamientos K-Means y Jerárquicos:

Se utilizó el método del codo para determinar que el número óptimo de clusters es k=3.

Los resultados de K-Means se visualizan claramente en el espacio reducido de las dos primeras componentes principales, lo que refleja una buena separación de los grupos. Los métodos jerárquicos Bottom-Up y Top-Down muestran diferencias significativas. Aunque el dendrograma Bottom-Up generó agrupamientos más pequeños, el análisis visual del dendrograma Top-Down sugiere que este último es más consistente al identificar grupos estructurados, idealmente entre 3 y 5 clusters.

Análisis de Componentes Principales (PCA):

El PCA mostró que las dos primeras componentes principales explican conjuntamente el 57% de la varianza, con el PC1 y PC2 aportando el 37% y 20%, respectivamente. Las variables que más contribuyen al PC1 son las variables 3 y 2, mientras que en el PC2 destacan las variables 4 y 6. Estas contribuciones reflejan la importancia relativa de estas variables en la variabilidad general de los datos. Proyecciones de las Variables:

El biplot permitió analizar cómo las variables originales se proyectan sobre las componentes principales. Las variables con cargas altas en valor absoluto en las primeras componentes son aquellas que más influyen en la formación de los clusters.

CONCLUSIÓN

En este trabajo práctico se aplicaron diversas técnicas de análisis exploratorio y de clasificación sobre un conjunto de datos de consumo de clientes, combinando dos bases de datos iniciales mediante normalización para garantizar la coherencia en la escala de las variables.