Análisis Cluster

Author

Katerin

Published

June 3, 2025

1 Análisis Cluster en RStudio

1.1 Cargar librerías

library(tidyverse)
Warning: package 'tidyverse' was built under R version 4.3.3
Warning: package 'forcats' was built under R version 4.3.2
Warning: package 'lubridate' was built under R version 4.3.3
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.3     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ ggplot2   3.4.4     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.0
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(factoextra)
Warning: package 'factoextra' was built under R version 4.3.3
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(cluster)
Warning: package 'cluster' was built under R version 4.3.3
library(GGally)
Warning: package 'GGally' was built under R version 4.3.2
Registered S3 method overwritten by 'GGally':
  method from   
  +.gg   ggplot2

1.2 Leer los datos

mayoristas <- read_csv("C:/Users/MINEDUCYT/Desktop/CICLOI_2025/SeminarioI/Datos de clientes mayoristas.csv")
Rows: 440 Columns: 8
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
dbl (8): Channel, Region, Fresh, Milk, Grocery, Frozen, Detergents_Paper, De...

ℹ 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.
head(mayoristas,10)
# A tibble: 10 × 8
   Channel Region Fresh  Milk Grocery Frozen Detergents_Paper Delicassen
     <dbl>  <dbl> <dbl> <dbl>   <dbl>  <dbl>            <dbl>      <dbl>
 1       2      3 12669  9656    7561    214             2674       1338
 2       2      3  7057  9810    9568   1762             3293       1776
 3       2      3  6353  8808    7684   2405             3516       7844
 4       1      3 13265  1196    4221   6404              507       1788
 5       2      3 22615  5410    7198   3915             1777       5185
 6       2      3  9413  8259    5126    666             1795       1451
 7       2      3 12126  3199    6975    480             3140        545
 8       2      3  7579  4956    9426   1669             3321       2566
 9       1      3  5963  3648    6192    425             1716        750
10       2      3  6006 11093   18881   1159             7425       2098

1.3 Ver si existen valores perdidos

sum(is.na(mayoristas))
[1] 0

Como se puede observar no existen datos perdidos o datos vacios.

1.4 Limpiar nombres de columnas

colnames(mayoristas) <- tolower(trimws(colnames(mayoristas)))

1.5 Seleccionar solo las variables numéricas para clúster

mayoristas_features <- mayoristas %>% select(-channel, -region)
head(mayoristas_features,10)
# A tibble: 10 × 6
   fresh  milk grocery frozen detergents_paper delicassen
   <dbl> <dbl>   <dbl>  <dbl>            <dbl>      <dbl>
 1 12669  9656    7561    214             2674       1338
 2  7057  9810    9568   1762             3293       1776
 3  6353  8808    7684   2405             3516       7844
 4 13265  1196    4221   6404              507       1788
 5 22615  5410    7198   3915             1777       5185
 6  9413  8259    5126    666             1795       1451
 7 12126  3199    6975    480             3140        545
 8  7579  4956    9426   1669             3321       2566
 9  5963  3648    6192    425             1716        750
10  6006 11093   18881   1159             7425       2098

En este resultado, eliminamos las columnas channel y region, las columnas restantes son de tipo dbl que son de tipo numericos.

1.6 Escalar los datos

mayoristas_scaled <- scale(mayoristas_features)

Escalamos los datos, este es un proceso en la cula transformamos los valores numericos de nuestra base de datos para que así, todas las variables estan en la misma escala.

1.7 Matriz de correlación

cor_matrix <- cor(mayoristas_scaled)
ggcorrplot::ggcorrplot(cor_matrix, lab = TRUE)

print(cor_matrix)
                       fresh      milk     grocery      frozen detergents_paper
fresh             1.00000000 0.1005098 -0.01185387  0.34588146       -0.1019529
milk              0.10050977 1.0000000  0.72833512  0.12399376        0.6618157
grocery          -0.01185387 0.7283351  1.00000000 -0.04019274        0.9246407
frozen            0.34588146 0.1239938 -0.04019274  1.00000000       -0.1315249
detergents_paper -0.10195294 0.6618157  0.92464069 -0.13152491        1.0000000
delicassen        0.24468997 0.4063683  0.20549651  0.39094747        0.0692913
                 delicassen
fresh             0.2446900
milk              0.4063683
grocery           0.2054965
frozen            0.3909475
detergents_paper  0.0692913
delicassen        1.0000000

Para nuestro grafico de las correlaciones, observanmos unas correlaciones fuertes como los son: milk y grocery con una correlacion de 0.73, milk y detergents_paper con una correlación de 0.66 y grocery con detergents_paper con una correlacion de 0.92, esta correlacion es extremadamente alta lo que nos indica que los clientes que compran muchos productos de abarrotes, tambien suelen comprar muchos productos de limpieza/papel. Las demas correlaciones son bajas.

1.8 Método del codo para determinar k óptimo

fviz_nbclust(mayoristas_scaled, kmeans, method = "wss") + 
  geom_vline(xintercept = 5, linetype = 2) +
  labs(title = "Método del Codo")

En el eje x observamos el numero de clusters de 1 a 10, en el eje y es la suma total de los cuadrados dentro de los clusters. Ya que definimos 5 cluster que esta indicado por la línea de puntos vertical.

1.9 K-Means con k = 5

set.seed(123)
kmeans_result <- kmeans(mayoristas_scaled, centers = 5, nstart = 25)
print(kmeans_result)
K-means clustering with 5 clusters of sizes 63, 269, 10, 97, 1

Cluster means:
       fresh       milk    grocery       frozen detergents_paper delicassen
1  1.6570840 -0.1082488 -0.2174555  1.102218231       -0.4041420  0.3326463
2 -0.2281097 -0.3850613 -0.4383243 -0.163797758       -0.3991069 -0.1945037
3  0.3134735  3.9174467  4.2707490 -0.003570131        4.6129149  0.5027930
4 -0.4962279  0.6810009  0.9032545 -0.332321693        0.8994410  0.1018261
5  1.9645810  5.1696185  1.2857533  6.892753825       -0.5542311 16.4597113

Clustering vector:
  [1] 2 4 4 2 1 2 2 2 2 4 4 2 1 4 4 2 4 2 2 2 2 2 1 4 4 2 2 2 4 1 2 2 2 1 2 4 1
 [38] 4 4 1 1 2 4 4 4 4 4 3 4 4 2 2 1 4 2 2 3 4 2 2 2 3 2 4 2 3 2 4 2 2 1 1 2 1
 [75] 2 2 2 4 2 2 2 4 4 2 2 3 3 1 2 1 2 2 3 1 4 2 2 2 2 2 4 4 2 1 2 2 4 4 2 4 2
[112] 4 1 2 2 2 2 2 2 2 2 2 2 2 1 1 1 2 2 1 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 4 2 2
[149] 2 1 2 2 2 2 2 4 4 2 4 4 4 2 2 4 2 4 4 2 2 2 4 4 2 4 2 4 1 2 2 2 2 1 4 5 2
[186] 2 2 2 4 4 2 2 2 4 2 1 1 4 2 2 4 4 1 2 2 4 2 2 2 4 2 3 2 2 4 4 4 2 4 2 2 4
[223] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 2 4 4 2 2 2 2 2 3 2 1 4 1 2 2 1
[260] 1 2 2 2 2 4 4 4 2 4 2 2 2 2 1 2 2 1 1 2 2 2 2 1 1 1 1 2 2 2 1 2 2 2 4 2 2
[297] 2 2 2 2 2 4 4 4 4 4 4 2 2 4 2 1 4 2 2 4 2 2 2 4 2 2 2 2 2 1 2 2 2 2 2 4 2
[334] 3 1 1 2 2 2 2 4 4 2 4 2 2 4 1 2 4 2 4 2 4 2 2 2 4 2 2 2 2 2 2 2 2 2 2 2 2
[371] 1 1 2 2 2 2 4 1 2 2 1 1 1 2 4 2 2 2 2 2 2 2 2 1 2 2 4 2 2 2 2 1 2 2 2 2 1
[408] 4 2 2 2 2 2 1 2 2 4 2 4 2 4 2 2 2 2 1 4 1 2 2 2 1 2 2 2 1 1 4 2 2

Within cluster sum of squares by cluster:
[1] 440.1481 235.0199 149.4481 231.7329   0.0000
 (between_SS / total_SS =  59.9 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
[6] "betweenss"    "size"         "iter"         "ifault"      

Nustro conjunto de datos fue agrupado en 5 clusters y estan definidos por:

Cluster1: 63 observaciones

Cluster2: 269 observaciones y es el grupo más grande

Cluster3: 10 observaciones

Cluster4: 97 observaciones

Cluster5: 1 observacion

También observamos los valores promedio por variable dentro de cada grupo en escala estandarizada:

Cluster1: se destaca en fresh y frozen

Cluster2: todos los valores estan por debajo del promedio

Cluster3: posee valores altos en milk, grocery, detergents_paper.

Cluster4: tiene consumo por encima del promedio en todas las variables

Cluster5: tiene valores altos

El Cluster 3 y 5 parecen contener lo que son los clientes mas grandes, Cluster2 representa lo que es la mayoria, con un consumo mas bajo.

1.10 Añadir clúster al dataset original

mayoristas_clustered <- mayoristas_features %>%
  mutate(cluster = as.factor(kmeans_result$cluster))
print(mayoristas_clustered)
# A tibble: 440 × 7
   fresh  milk grocery frozen detergents_paper delicassen cluster
   <dbl> <dbl>   <dbl>  <dbl>            <dbl>      <dbl> <fct>  
 1 12669  9656    7561    214             2674       1338 2      
 2  7057  9810    9568   1762             3293       1776 4      
 3  6353  8808    7684   2405             3516       7844 4      
 4 13265  1196    4221   6404              507       1788 2      
 5 22615  5410    7198   3915             1777       5185 1      
 6  9413  8259    5126    666             1795       1451 2      
 7 12126  3199    6975    480             3140        545 2      
 8  7579  4956    9426   1669             3321       2566 2      
 9  5963  3648    6192    425             1716        750 2      
10  6006 11093   18881   1159             7425       2098 4      
# ℹ 430 more rows
head(mayoristas_clustered,20)
# A tibble: 20 × 7
   fresh  milk grocery frozen detergents_paper delicassen cluster
   <dbl> <dbl>   <dbl>  <dbl>            <dbl>      <dbl> <fct>  
 1 12669  9656    7561    214             2674       1338 2      
 2  7057  9810    9568   1762             3293       1776 4      
 3  6353  8808    7684   2405             3516       7844 4      
 4 13265  1196    4221   6404              507       1788 2      
 5 22615  5410    7198   3915             1777       5185 1      
 6  9413  8259    5126    666             1795       1451 2      
 7 12126  3199    6975    480             3140        545 2      
 8  7579  4956    9426   1669             3321       2566 2      
 9  5963  3648    6192    425             1716        750 2      
10  6006 11093   18881   1159             7425       2098 4      
11  3366  5403   12974   4400             5977       1744 4      
12 13146  1124    4523   1420              549        497 2      
13 31714 12319   11757    287             3881       2931 1      
14 21217  6208   14982   3095             6707        602 4      
15 24653  9465   12091    294             5058       2168 4      
16 10253  1114    3821    397              964        412 2      
17  1020  8816   12121    134             4508       1080 4      
18  5876  6157    2933    839              370       4478 2      
19 18601  6327   10099   2205             2767       3181 2      
20  7780  2495    9464    669             2518        501 2      

Observamos que se añadio una nueva columna llamada cluster, que representa lo que es la signacion de cada observacion (cliente) a uno de los 5 grupos que identificamos con k-means.

Ejemplo: fresh = 12669, milk = 9656, grocery = 7561, detergents = 2674, delicassen = 1338 y cluster = 2. Decimos que el cliente con consumos altos en casi todo pero es más en frescos y leche y a sido asignado en el cluster 2 que si observamos es el grupo mas numeroso.

1.11 PCA para visualización

pca_result <- prcomp(mayoristas_scaled)
pca_df <- data.frame(pca_result$x[, 1:2], cluster = mayoristas_clustered$cluster)
print(pca_df)
              PC1           PC2 cluster
1    -0.193070773   0.304753062       2
2    -0.433925956   0.328039214       4
3    -0.810220956  -0.814168929       4
4     0.777762502  -0.652011546       2
5    -0.166098189  -1.269988085       1
6     0.155992365   0.294805410       2
7     0.334907178   0.524406324       2
8    -0.140426586   0.230730046       2
9     0.516731343   0.658613124       2
10   -1.590298838   0.740168795       4
11   -0.637360435   0.401368322       4
12    0.926637783   0.219037910       2
13   -0.990234988  -0.685130920       1
14   -0.849280021   0.085594532       4
15   -0.844082970  -0.154548402       4
16    0.950503413   0.495344645       2
17   -0.556924767   1.024956009       4
18    0.352137758  -0.206840463       2
19   -0.316218610  -0.441563658       2
20    0.323387623   0.702786961       2
21    0.319636753  -0.156450059       2
22    1.117175516   0.230265407       2
23    0.176493641  -2.141279562       1
24   -4.680326570  -3.594586649       4
25   -1.243626454  -1.054459366       4
26    0.153938797   0.522299090       2
27    1.038441478   0.036438173       2
28    1.096279281   0.242705693       2
29   -3.118541754   0.329699623       4
30    0.779053139  -1.071953132       1
31   -0.074498387  -0.266200893       2
32    0.728360232   0.444303503       2
33    0.961136362   0.018967605       2
34    0.349373210  -1.179252181       1
35    1.067866778   0.800685496       2
36   -0.167057514   1.073175296       4
37    0.171941052  -1.265565778       1
38   -0.971582614   0.367756560       4
39   -1.582862391   1.127962958       4
40    0.778509490  -3.156826515       1
41    0.011385821  -2.213948731       1
42    0.232625380  -0.413354864       2
43   -0.854750688   0.978559241       4
44   -1.943649746   1.564916622       4
45   -0.280988890   0.794790697       4
46   -2.810297920   0.098761513       4
47   -1.863198925   0.892588736       4
48   -9.527271195  -1.505922903       3
49   -0.429587666   0.502811179       4
50   -3.593814000   1.237957233       4
51    0.927649378  -0.134370995       2
52    0.508866127   0.869798256       2
53    0.250427692  -0.861400874       1
54   -0.699487251   1.154592939       4
55    0.966826255  -0.712126251       2
56    0.513254059   0.538118693       2
57   -4.594525937   1.234970127       3
58   -0.924114149   0.943702440       4
59    1.088368634   0.236308383       2
60    0.047534423   0.618461195       2
61    0.227067427   0.843943417       2
62   -8.421339261   0.591695761       3
63   -0.032373807   0.175547537       2
64   -1.426494398   0.483533345       4
65    0.863304583   0.179761751       2
66   -5.803972704   2.452140438       3
67    0.483179915   1.204334275       2
68   -0.613772190   0.216699135       4
69    0.238496669  -0.423634644       2
70    1.040081693   0.499939124       2
71    0.803690538  -1.299805476       1
72   -1.799233683  -2.545722770       1
73    0.633627652  -0.331080502       2
74    0.228709325  -0.906488894       1
75   -0.030613736   0.308117629       2
76    1.165782413  -0.644109031       2
77    0.567274587  -0.100407773       2
78   -2.749818533   1.089762478       4
79    1.131153702   0.240661512       2
80    0.689608841   0.559706158       2
81    1.062131302   0.577629594       2
82   -1.047755961   1.408945335       4
83   -0.557456166   0.259675031       4
84    1.001168586  -0.313054122       2
85    0.407966123   0.525963872       2
86  -12.638615487   2.712118693       3
87   -8.411414048   0.446349687       3
88   -1.074462392  -4.064714917       1
89    1.013135645  -0.548221263       2
90    0.390455180  -1.029074991       1
91    1.194388061   0.009471804       2
92    0.788128806  -0.836151102       2
93   -5.222963543   0.436038947       3
94    0.444394778  -4.412950270       1
95   -0.763349021   0.950838160       4
96    0.762311220   0.842043176       2
97    0.297329687   1.193874632       2
98    1.374660424   0.833052295       2
99    1.366978726   0.805595590       2
100   1.028945537  -0.205477128       2
101  -1.128184514   0.043351066       4
102  -1.300329760   0.851165932       4
103  -0.178492820   0.546562267       2
104  -0.120597591  -3.931784955       1
105   0.802651974   0.203170746       2
106   0.935061563  -0.005362637       2
107  -0.622332350   1.003148045       4
108  -1.456538424   0.602743042       4
109   0.001132668   0.779987633       2
110  -1.658853957   1.107051186       4
111   0.998168810  -0.108656124       2
112  -1.367987291   0.541663784       4
113   0.644507391  -1.412133416       1
114   0.888880613  -0.305931780       2
115   1.036882844  -0.327182230       2
116   1.059267309   0.085279169       2
117   0.952068060   0.291857720       2
118   0.657322295   0.298689855       2
119   0.914369788  -0.690280028       2
120   0.992217322   0.021213606       2
121   0.951424650  -0.226311129       2
122   1.029976719   0.434944695       2
123   1.281005760   0.037813367       2
124  -0.181751136   0.301021414       2
125   0.776946357  -1.394423271       1
126   0.159130274  -4.367966802       1
127   0.980606816  -0.947855150       1
128  -0.333397511  -0.338748874       2
129   0.440987158   0.940856519       2
130   0.922298403  -1.323836776       1
131   1.013335638  -0.302855404       2
132   1.343092943   0.764435182       2
133   0.955288325   0.262264670       2
134   1.198695310   0.450638896       2
135   1.094975699   0.468855763       2
136   1.113416999   0.516564133       2
137  -0.091732452   0.046762397       2
138   0.246708126   0.805526078       2
139   0.466002862  -0.177473584       2
140   0.698193248   0.483730983       2
141   0.265570759  -0.425657066       2
142  -0.083314532  -0.685406292       1
143   0.260870096  -0.927093355       1
144   1.069142345  -0.582609422       2
145   0.916955290   0.129256718       2
146  -3.202731861   0.346285851       4
147   1.085847643   0.430882160       2
148   0.822110819   0.370589192       2
149   1.242080520   0.131004863       2
150   0.989135955  -0.721632753       1
151   1.031859466  -0.010589522       2
152   0.887095917   0.518581802       2
153   1.007290993  -0.196421465       2
154   0.426171903   0.363989950       2
155   1.435076065   0.914214161       2
156  -2.055064300   1.250320066       4
157  -0.911575113   0.707572181       4
158   0.974141728  -0.226722797       2
159  -0.248191449   0.700099572       4
160  -0.973594022   1.401349130       4
161  -0.313135920   0.780571589       4
162   1.143835237  -0.108148107       2
163   0.910122310  -0.080781295       2
164  -3.009967235   1.311161170       4
165  -0.121143513   0.611709684       2
166  -1.820394062  -0.141944937       4
167  -0.523249384   0.304059132       4
168   0.748097322   0.756322831       2
169   1.267360833   0.462898105       2
170   1.149856681   0.315775262       2
171  -0.855805274   1.224292186       4
172  -3.234339434   0.224261036       4
173   0.252927916   0.931757279       2
174  -1.528918266   1.426224335       4
175   0.870963867   0.437905812       2
176  -0.492278028   0.860185057       4
177   0.110569273  -1.820727544       1
178   0.338154005  -0.286248309       2
179   0.529826832   0.276977507       2
180   0.658245861  -0.135429614       2
181   0.012836758   0.025841507       2
182  -3.722721875  -7.159388999       1
183  -0.635040219   1.271828581       4
184  -7.789073974 -13.933223927       5
185   1.055709705   1.004196654       2
186   0.827066440   0.237325047       2
187   1.141262496   0.314740224       2
188  -0.210301844   0.303461554       2
189  -0.703626769   0.653758372       4
190  -0.738278241   0.810154811       4
191   0.715564082  -0.726074653       2
192   1.277998266   0.273614283       2
193   1.124750248   0.605380875       2
194  -0.758301989   1.275262485       4
195   1.021725337   0.400178461       2
196   0.419915406  -1.028235321       1
197  -0.021592867  -3.164447114       1
198  -0.351640042   0.754210940       4
199   0.670455679   0.004987904       2
200   1.039633967   0.446318201       2
201  -2.198101340   0.933688223       4
202  -2.956560270   0.994193920       4
203  -0.750931105  -1.934390805       1
204   1.147944552   0.939998761       2
205   0.928648603   0.526532752       2
206  -1.993914415   1.382529753       4
207   1.247243145   0.542888324       2
208   0.068896908   0.775521965       2
209   0.334279292   0.735353136       2
210  -2.037674490   1.245413758       4
211   0.931820094  -0.414954567       2
212  -5.637416126   0.663908565       3
213   1.028991665   0.198078782       2
214  -0.042129024   0.669275036       2
215  -0.497505693   1.013974453       4
216  -1.195524630   0.661638998       4
217  -3.599348044   1.786694439       4
218   1.013289363  -0.308078480       2
219  -1.156654382   0.053360741       4
220   1.280303783   0.483983240       2
221   1.160127660  -0.008762919       2
222  -0.271023248   0.971934194       4
223   1.036802386  -0.227097020       2
224   0.665094215  -0.022418168       2
225   1.212757990   0.448379874       2
226   0.752150734   0.264359182       2
227  -0.068398508  -0.205088957       2
228   1.065009911   0.382708721       2
229   0.792194964   0.972471024       2
230   0.828830373  -0.404583154       2
231   0.243674793  -0.872846825       2
232   0.183354643   0.604896894       2
233   0.885504971  -0.309830010       2
234   0.820825771   0.806423660       2
235   0.657386047  -0.650895045       2
236   0.623521099   0.702972791       2
237   1.084354748   0.317215616       2
238   0.909673015  -0.664417366       2
239   1.317134451   0.470698556       2
240   0.521682384  -2.022786628       1
241   0.297745990  -1.783461283       1
242   0.920386436  -0.517823930       2
243   1.005843589  -0.318644695       2
244   0.426816538   0.110315709       2
245  -0.350593956   0.436051256       4
246  -1.137106291   0.917442107       4
247   1.012116131   0.268470105       2
248   1.265453343   0.009410834       2
249   0.741194511   0.129972194       2
250   0.820016852  -0.198551437       2
251   1.076442648   0.488939176       2
252  -4.987050880   0.179132331       3
253   0.749247945  -0.534488710       2
254  -0.439352024  -0.395859934       1
255  -0.715461336  -0.704562870       4
256   1.116271674  -0.865218523       1
257   0.641672441   0.407695355       2
258   0.612514724  -0.011595946       2
259   0.350676422  -2.773937557       1
260   0.242896638  -1.688573020       1
261   0.786915608   0.446166041       2
262   1.173749578  -0.281693055       2
263   1.052005853  -0.229088120       2
264   0.539977937   0.486486305       2
265  -1.008385307   1.119032056       4
266  -1.664075455  -1.236315849       4
267  -1.434426133   0.679738795       4
268   0.687137992  -1.006817993       2
269  -1.199314020   0.748287336       4
270   1.229040924  -0.006910766       2
271   1.214641184   0.063524408       2
272   0.814339626   0.419565350       2
273   0.308734214   0.691967370       2
274   0.775511362  -1.518524270       1
275   1.014010616   0.661788273       2
276   1.263529273   0.734930107       2
277   0.318573424  -1.129866960       1
278   1.056201060  -1.549850124       1
279   0.868702624  -0.499144555       2
280  -0.237737581   0.026975289       2
281   0.952422483   0.502093506       2
282   0.042615673   0.069387734       2
283   0.426961320  -2.025150248       1
284   0.708159356  -1.664100746       1
285  -0.266497749  -3.269297260       1
286   0.985958120  -0.794067421       1
287   1.016554999   0.483433517       2
288   0.849012810  -0.888299218       2
289   1.190997867   0.154603676       2
290   1.236732137  -1.009780124       1
291   0.871589400   0.568756415       2
292   0.873659542   0.283180951       2
293   0.721598752   0.233881672       2
294  -0.901512967   0.921515333       4
295   0.565442163  -0.263311776       2
296   0.686152840   0.711811083       2
297   0.875816429  -0.312199529       2
298   0.273055298   0.603404467       2
299  -0.138709332   0.708815311       2
300   1.200912435   0.888395638       2
301   0.055538915  -0.029698082       2
302  -1.834281846   1.102175697       4
303  -0.316289283   1.108160117       4
304  -0.841280873   1.478245930       4
305  -2.184561767   1.361425196       4
306  -0.521012739   1.000245157       4
307  -1.860343450   0.762829795       4
308   0.910498866  -0.048413542       2
309   1.077775366   0.536357530       2
310  -1.778060618   0.924584963       4
311   0.905819901  -1.090600020       2
312   0.589125019  -0.571473875       1
313  -1.995938972   1.843263895       4
314   1.109671393  -0.035920516       2
315   0.594224275   0.242033212       2
316  -1.150571098   0.399669679       4
317   1.060832695   0.147809545       2
318   0.597998890   0.587166640       2
319   0.886529581   0.242361129       2
320  -3.091467407   0.762215182       4
321   0.923313310   0.669429780       2
322   0.812795221   0.096568843       2
323   0.914933497  -0.099312499       2
324   0.376854995   0.431307976       2
325   0.811306357  -0.623736565       2
326  -2.012065622  -8.995247995       1
327   1.075832944   0.199949012       2
328   1.221711576   0.801269373       2
329   0.782556129  -0.981762509       2
330   0.869612681   0.038112572       2
331   0.824816095  -0.027309135       2
332  -2.536775236   0.887431517       4
333   0.777265899  -0.730245390       2
334  -7.539998194   3.376046028       3
335   0.691632322  -1.782279373       1
336  -0.477198913  -0.426466449       1
337   1.119599526   0.156337684       2
338   0.948002807  -0.576441637       2
339   0.784609818  -1.008691909       2
340   0.692032975  -0.614778678       2
341  -0.397773419   1.131838633       4
342  -0.459004870   0.754090027       4
343   0.015180584   0.929315202       2
344  -2.470901819   1.842896967       4
345   1.165608395   0.498623482       2
346   0.274426270   1.098925040       2
347  -0.903485483   0.809104652       4
348  -0.573440076  -0.374471446       1
349   0.931849283   0.586087579       2
350  -2.366925366   1.172036818       4
351   1.110477664   0.425751115       2
352  -2.402214322   1.024670856       4
353   1.238402566   0.800358588       2
354  -1.402985457   1.437085597       4
355   0.275777604  -0.832258062       2
356   1.239964102   0.920291504       2
357   1.269144761  -0.490949696       2
358  -0.787492841   1.599920729       4
359  -0.518080077  -0.741485496       2
360   0.787786279   0.713014098       2
361   0.924910988  -0.268968494       2
362   1.271624310   0.542271195       2
363   1.087649335   0.692093811       2
364   0.908735507   0.804312107       2
365   1.173613254   0.658581524       2
366  -0.316202500   0.290342098       2
367   1.076973375   0.334953682       2
368   1.267010652   0.571072052       2
369   1.058773860  -0.311601305       2
370   1.322015012   0.508780958       2
371   0.452374575  -1.218732652       1
372   0.888027202  -1.012387926       1
373   0.577211259  -0.182970270       2
374  -0.023146084  -0.280285055       2
375   1.050097061   0.390978562       2
376   1.186926948   0.644690402       2
377  -0.746418151   0.214426685       4
378   0.794528860  -1.012078538       1
379   0.972700862   0.575650478       2
380   0.312677174   0.876910916       2
381   1.021782233  -0.712808517       1
382   0.774097745  -1.151360584       1
383  -0.209075837  -1.188071804       1
384   1.014931430   0.363134953       2
385  -1.659727532  -0.864330965       4
386   1.102461926  -0.027971209       2
387   0.859889740   0.503077599       2
388   1.011584083  -0.327635464       2
389   1.208151000   0.276158335       2
390   1.102124183   0.071776610       2
391   1.090374984  -0.038551302       2
392   0.916405603   0.407373025       2
393   0.843313882   0.742430288       2
394   0.433089032  -1.265255953       1
395   1.079001964   0.223182981       2
396   1.092009796   0.178337440       2
397  -0.630420072   0.221505269       4
398   1.057708853  -0.519555116       2
399   1.214274326  -0.249993308       2
400   1.215780891   0.312302515       2
401   1.149717730   0.149049424       2
402   0.705455803  -2.183792347       1
403   0.667084452  -0.221868515       2
404   0.172075077  -0.206568493       2
405   0.626156963  -0.427626536       2
406   1.156780960   0.371630914       2
407   0.376803139  -1.424229183       1
408  -1.646491426   0.748112478       4
409   0.384957895   0.536674136       2
410   0.060091271  -0.462052451       2
411   0.600946818   0.305240730       2
412   0.573633295  -0.087297941       2
413   0.081213096   1.230703781       2
414   0.163757800  -1.847736123       1
415   0.860339732   0.023969679       2
416  -0.132963098   0.326709310       2
417  -0.906604247   0.886235912       4
418  -0.143876846   0.796414154       2
419  -1.158203281   1.317510758       4
420   0.684328702   0.303790140       2
421  -0.666050645   1.019957225       4
422  -0.076929113   0.055517586       2
423   0.781133732  -0.375723347       2
424   0.558684365   0.089778373       2
425   0.034826273   0.413370270       2
426   0.737297053  -1.771359499       1
427  -0.940206120   0.053449892       4
428  -0.528782663  -2.533400589       1
429   0.552396875   0.542270300       2
430   0.844540144   0.176650799       2
431  -0.092949967   0.594250262       2
432   0.269020238  -1.281929230       1
433   0.814213865   0.094297255       2
434   0.931502791   0.419100734       2
435   0.264238329   0.219957052       2
436  -0.869612577  -2.218320117       1
437   0.901493887  -1.675008897       1
438  -3.461763078   1.038655699       4
439   0.916978932   0.030012427       2
440   1.103880538   0.860358524       2
head(pca_df, 20)
          PC1         PC2 cluster
1  -0.1930708  0.30475306       2
2  -0.4339260  0.32803921       4
3  -0.8102210 -0.81416893       4
4   0.7777625 -0.65201155       2
5  -0.1660982 -1.26998809       1
6   0.1559924  0.29480541       2
7   0.3349072  0.52440632       2
8  -0.1404266  0.23073005       2
9   0.5167313  0.65861312       2
10 -1.5902988  0.74016879       4
11 -0.6373604  0.40136832       4
12  0.9266378  0.21903791       2
13 -0.9902350 -0.68513092       1
14 -0.8492800  0.08559453       4
15 -0.8440830 -0.15454840       4
16  0.9505034  0.49534465       2
17 -0.5569248  1.02495601       4
18  0.3521378 -0.20684046       2
19 -0.3162186 -0.44156366       2
20  0.3233876  0.70278696       2

En esta tabla muestra lo que son los resultados de lo que es un análisis de componentes principales (PCA) combinado con lo que es asignación de clusters.

PC1 y PC2 son las primeras 2 componentes principales extraidas de un conjunto de datos. Por ejemplo el primer registro tiene valores PC1= -0.1930708 y PC2 0.30475306 lo que representa su posicion en este nuevo espacio. Este pertenece al cluster 2.

En tu tabla hay al menos tres clústeres identificados: 1, 2, 4.

1.12 Visualización PCA

ggplot(pca_df, aes(PC1, PC2, color = cluster)) +
  geom_point(size = 3, alpha = 0.8) +
  theme_minimal() +
  labs(title = "Clústeres visualizados con PCA")

El grafico muestra puntos que estan representando observaciones del conjunto de datos, en el eje x es el primer componente principal y en el eje y es el segundo componente.

Cada punto esta coloreado segun al cluster que pertenece del 1 al 5. Los clústeres 1 (rojo), 2 (verde mostaza), y 3 (verde) parecen estar más agrupados entre sí, miestras que el clúster 4 (celeste) y el clúster 5 (morado) están más dispersos o alejados, lo que puede indicar que datos atipicos o grupos más heterogéneos o que estan menos relacionados con los otros cluster.

1.13 Perfil medio de cada clúster

perfil <- mayoristas_clustered %>%
  group_by(cluster) %>%
  summarise(across(where(is.numeric), mean, .names = "mean_{.col}"))
print(perfil)
# A tibble: 5 × 7
  cluster mean_fresh mean_milk mean_grocery mean_frozen mean_detergents_paper
  <fct>        <dbl>     <dbl>        <dbl>       <dbl>                 <dbl>
1 1           32958.     4997.        5885.       8423.                  955.
2 2            9115.     2954.        3786.       2277.                  979.
3 3           15965.    34708.       48537.       3055.                24875.
4 4            5724.    10822.       16535.       1459.                 7170.
5 5           36847     43950        20170       36534                   239 
# ℹ 1 more variable: mean_delicassen <dbl>

Cada columna representa el valor medio de una característica para los clientes agrupados en cada clúster.

1.14 Visualización de perfiles

perfil_largo <- pivot_longer(perfil, starts_with("mean_"), names_to = "Variable", values_to = "Promedio")
perfil_largo$Variable <- gsub("mean_", "", perfil_largo$Variable)
print(perfil_largo)
# A tibble: 30 × 3
   cluster Variable         Promedio
   <fct>   <chr>               <dbl>
 1 1       fresh              32958.
 2 1       milk                4997.
 3 1       grocery             5885.
 4 1       frozen              8423.
 5 1       detergents_paper     955.
 6 1       delicassen          2463.
 7 2       fresh               9115.
 8 2       milk                2954.
 9 2       grocery             3786.
10 2       frozen              2277.
# ℹ 20 more rows
ggplot(perfil_largo, aes(x = Variable, y = Promedio, fill = cluster)) +
  geom_bar(stat = "identity", position = "dodge") +
  theme_minimal() +
  coord_flip() +
  labs(title = "Perfil de gasto por clúster")

En el eje y muetra las variables y en eje x muestra lo que son los promedios de gastos.

Los cluster están representados en diferentes colores para poderlos identificar.

El cluster 1 tiene un gasto muy alto en lo que es la categoria fresh.

El cluster 2 tambien destaca mucho en la categoria fresh

El cluster 3 parece tener un gasto algo balanceado y es alto en milk, grocery, detergents_paper

El cluster 4 tiene un gasto bajo en general

El cluster 5 gasta mas significativamente en casi todas las categorias especialmente en detergents_paper, grocery y milk.

library(ggplot2)
library(factoextra)

# Genera el gráfico (sin plotly aún)
p <- fviz_cluster(kmeans_result, data = mayoristas_scaled,
                  palette = c("#8B0000", "#006400", "#8B008B", "#00008B", "#FFA500"),
                  ellipse.type = "euclid", 
                  star.plot = TRUE, 
                  repel = TRUE, 
                  ggtheme = theme_minimal())

# carga plotly
library(plotly)
Warning: package 'plotly' was built under R version 4.3.3

Attaching package: 'plotly'
The following object is masked from 'package:ggplot2':

    last_plot
The following object is masked from 'package:stats':

    filter
The following object is masked from 'package:graphics':

    layout
# Convierte el gráfico a interactivo
ggplotly(p)
Too few points to calculate an ellipse
Warning in geom2trace.default(dots[[1L]][[5L]], dots[[2L]][[1L]], dots[[3L]][[1L]]): geom_GeomTextRepel() has yet to be implemented in plotly.
  If you'd like to see this geom implemented,
  Please open an issue with your example code at
  https://github.com/ropensci/plotly/issues
Warning in geom2trace.default(dots[[1L]][[5L]], dots[[2L]][[1L]], dots[[3L]][[1L]]): geom_GeomTextRepel() has yet to be implemented in plotly.
  If you'd like to see this geom implemented,
  Please open an issue with your example code at
  https://github.com/ropensci/plotly/issues

Warning in geom2trace.default(dots[[1L]][[5L]], dots[[2L]][[1L]], dots[[3L]][[1L]]): geom_GeomTextRepel() has yet to be implemented in plotly.
  If you'd like to see this geom implemented,
  Please open an issue with your example code at
  https://github.com/ropensci/plotly/issues

Warning in geom2trace.default(dots[[1L]][[5L]], dots[[2L]][[1L]], dots[[3L]][[1L]]): geom_GeomTextRepel() has yet to be implemented in plotly.
  If you'd like to see this geom implemented,
  Please open an issue with your example code at
  https://github.com/ropensci/plotly/issues

Warning in geom2trace.default(dots[[1L]][[5L]], dots[[2L]][[1L]], dots[[3L]][[1L]]): geom_GeomTextRepel() has yet to be implemented in plotly.
  If you'd like to see this geom implemented,
  Please open an issue with your example code at
  https://github.com/ropensci/plotly/issues

La dimension1 explica el 44.1% de lo que es la variabilidad total.

La dimesion 2 explica el 28.4% de la variabilidad total.

Juntas las dos dimensiones capturan lo que es el 72.5% de la informacion del nuestro conjunto de datos original.

El cluster 1, 2, 3 y 4 estan agrupados mas cercamente, miestras que el cluster 5 parece tener lo que es un solo individuo y esta muy alejado del resto de clusters.

1.15 Estimacion del numero optimo de conglomerados

#Método del codo para determinar k óptimo
library(cluster)
library(factoextra)

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

En el eje x observamos el numero de clusters de 1 a 10, en el eje y es la suma total de los cuadrados dentro de los clusters. Se observa que necesitamos 2 cluster que esta indicado por la línea de puntos vertical azul.

1.16 Calculo PAM clustering

#K-Means con k = 2
#PAM (Partitioning Around Medoids) es un algoritmo que: Agrupa los datos en k clústeres (al igual que k-means).
pam.may <- pam(mayoristas_scaled, 2)
print(pam.may)
Medoids:
      ID      fresh       milk    grocery      frozen detergents_paper
[1,] 322 -0.2249722 -0.5283288 -0.2929843 -0.07372933       -0.5565382
[2,]  10 -0.4739576  0.7176780  1.1501142 -0.39403925        0.9529458
     delicassen
[1,] -0.1460479
[2,]  0.2032298
Clustering vector:
  [1] 1 2 2 1 1 1 1 1 1 2 2 1 2 2 2 1 2 1 1 1 1 1 1 2 2 1 1 1 2 1 1 1 1 1 1 1 1
 [38] 2 2 1 1 1 2 2 1 2 2 2 2 2 1 1 1 2 1 1 2 2 1 1 1 2 1 2 1 2 1 2 1 1 1 2 1 1
 [75] 1 1 1 2 1 1 1 2 2 1 1 2 2 1 1 1 1 1 2 1 2 1 1 1 1 1 2 2 1 1 1 1 2 2 1 2 1
[112] 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1
[149] 1 1 1 1 1 1 1 2 2 1 1 2 2 1 1 2 1 2 2 1 1 1 2 2 1 2 1 2 1 1 1 1 1 2 2 2 1
[186] 1 1 1 2 2 1 1 1 2 1 1 1 2 1 1 2 2 1 1 1 2 1 1 1 2 1 2 1 1 2 2 2 1 2 1 1 2
[223] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 2 1 1 1 1
[260] 1 1 1 1 1 2 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1
[297] 1 1 1 1 1 2 2 2 2 2 2 1 1 2 1 1 2 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 1
[334] 2 1 1 1 1 1 1 2 2 1 2 1 1 2 1 1 2 1 2 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1
[371] 1 1 1 1 1 1 2 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1
[408] 2 1 1 1 1 1 1 1 1 2 1 2 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1
Objective function:
   build     swap 
1.556140 1.472508 

Available components:
 [1] "medoids"    "id.med"     "clustering" "objective"  "isolation" 
 [6] "clusinfo"   "silinfo"    "diss"       "call"       "data"      

El cluster 1 representa clientes con gastos bajos o moderados en la mayoria de las categorias.

El cluster 2 representa un alto gasto en milk, grosery y detergents miestras que en fresh y frozen es bajo.

Podemos ver que por ejemplo el primer cliente fue asignado al cluster2, el segundo tambien, el tercero en el cluster 1, etc.

dd <- cbind(mayoristas_features, cluster = pam.may$cluster)
head(dd, n = 20)
   fresh  milk grocery frozen detergents_paper delicassen cluster
1  12669  9656    7561    214             2674       1338       1
2   7057  9810    9568   1762             3293       1776       2
3   6353  8808    7684   2405             3516       7844       2
4  13265  1196    4221   6404              507       1788       1
5  22615  5410    7198   3915             1777       5185       1
6   9413  8259    5126    666             1795       1451       1
7  12126  3199    6975    480             3140        545       1
8   7579  4956    9426   1669             3321       2566       1
9   5963  3648    6192    425             1716        750       1
10  6006 11093   18881   1159             7425       2098       2
11  3366  5403   12974   4400             5977       1744       2
12 13146  1124    4523   1420              549        497       1
13 31714 12319   11757    287             3881       2931       2
14 21217  6208   14982   3095             6707        602       2
15 24653  9465   12091    294             5058       2168       2
16 10253  1114    3821    397              964        412       1
17  1020  8816   12121    134             4508       1080       2
18  5876  6157    2933    839              370       4478       1
19 18601  6327   10099   2205             2767       3181       1
20  7780  2495    9464    669             2518        501       1

Cada fila esta representa por un cliente mayorista, y las columnas muestran su consumo en diferentes categorías de productos, junto con el grupo (cluster) al que fue asignado.

Por ejemplo el cluster 1 posee alto consumo de fresh con 12,669, milk con 9,656 y grocery con 7,561; por otro lado se observa que existe bajo consumo en frozen con 214 y delicassen con 1,338 y pertenece al cluster 1.

1.17 Accediendo a los resultados de la funcion pam()

pam.may$medoids
          fresh       milk    grocery      frozen detergents_paper delicassen
[1,] -0.2249722 -0.5283288 -0.2929843 -0.07372933       -0.5565382 -0.1460479
[2,] -0.4739576  0.7176780  1.1501142 -0.39403925        0.9529458  0.2032298

Esta tabla muestra los centros representativos (medoides) de cada cluster.

Cluster1: tiene valores negativos en todas las variables que corresponde a clientes con bajo consumo en milk, grocery y detergents_paper que representan clientes con alto consumo en esos productos

Cluster2: Valores positivos en milk, grocery, detergents_paper y delicassen que son clientes con alto consumo en esos productos.

head(pam.may$clustering)
[1] 1 2 2 1 1 1

Esta parte muestra los clusters asignados a los primeros clientes de la base de datos por ejemplo:

1 2 2 1 1 1

Cliente 1 → Cluster 1

Cliente 2 → Cluster 2

Cliente 3 → Cluster 2

Cliente 4 → Cluster 1

Cliente 5 → Cluster 1

Cliente 6 → Cluster 1

1.18 Visualizacion PAM clusters

library(ggplot2)
library(dplyr)
library(cluster)
library(factoextra)

# Función
replace_null <- function(x, y, ...) {
  if (is.null(x)) y else x
}

fviz_cluster(pam.may, 
             palette = c("#EE82EE", "#9ACD32"), 
             ellipse.type = "t", 
             repel = TRUE)

Se observan los ejes representados por Dim1 y Dim2 que son las primeras dos dimensiones principales generadas a partir del analisis de componentes principales que le aplicamos a la base de datos.

La Dim1 explica el 44.1% de la varibilidad y la Dim2 explica el 28.4% y en conjunto el grafico muestra casi un 72.5% de lo que es la varibilidad total de la base de datos.

fviz_cluster(list(data = mayoristas_scaled, cluster = pam.may$clustering),

palette = c("#8B0000", "#006400", "#8B008B", "#00008B"),
ellipse.type = "convex", 
repel = TRUE, 
show.clust.cent = FALSE, ggtheme = theme_minimal())

2 Análisis Cluster en Python

2.1 Cargar librerías

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score

2.2 Leer los datos

import pandas as pd

mayoristas = pd.read_csv("C:/Users/MINEDUCYT/Desktop/CICLOI_2025/SeminarioI/Datos de clientes mayoristas.csv")
print(mayoristas.head(10))
   Channel  Region  Fresh   Milk  Grocery  Frozen  Detergents_Paper  Delicassen
0        2       3  12669   9656     7561     214              2674        1338
1        2       3   7057   9810     9568    1762              3293        1776
2        2       3   6353   8808     7684    2405              3516        7844
3        1       3  13265   1196     4221    6404               507        1788
4        2       3  22615   5410     7198    3915              1777        5185
5        2       3   9413   8259     5126     666              1795        1451
6        2       3  12126   3199     6975     480              3140         545
7        2       3   7579   4956     9426    1669              3321        2566
8        1       3   5963   3648     6192     425              1716         750
9        2       3   6006  11093    18881    1159              7425        2098

2.3 Limpiar nombres de columnas

mayoristas.columns = mayoristas.columns.str.strip().str.lower()

2.4 Seleccionar solo las variables numéricas para clustering

mayoristas_features = mayoristas.drop(columns=["channel", "region"])
print(mayoristas_features.head(10))
   fresh   milk  grocery  frozen  detergents_paper  delicassen
0  12669   9656     7561     214              2674        1338
1   7057   9810     9568    1762              3293        1776
2   6353   8808     7684    2405              3516        7844
3  13265   1196     4221    6404               507        1788
4  22615   5410     7198    3915              1777        5185
5   9413   8259     5126     666              1795        1451
6  12126   3199     6975     480              3140         545
7   7579   4956     9426    1669              3321        2566
8   5963   3648     6192     425              1716         750
9   6006  11093    18881    1159              7425        2098

2.5 Escalar los datos

scaler = StandardScaler()
mayoristas_scaled = scaler.fit_transform(mayoristas_features)

2.6 Matriz de correlación

cor_matrix = pd.DataFrame(mayoristas_scaled, columns=mayoristas_features.columns).corr()
plt.figure(figsize=(10, 8))
sns.heatmap(cor_matrix, annot=True, cmap="coolwarm", fmt=".2f")
plt.title("Matriz de correlación")
plt.show()

2.7 Método del codo para determinar el número óptimo de clústeres

from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

# Paso 1: Método del codo
wcss = []
for i in range(1, 11):
    kmeans = KMeans(n_clusters=i, random_state=123)
    kmeans.fit(mayoristas_scaled)
    wcss.append(kmeans.inertia_)
KMeans(n_clusters=10, random_state=123)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
plt.figure(figsize=(8, 5))
plt.plot(range(1, 11), wcss, marker="o")
plt.axvline(x=2, linestyle="--", color="red")  # Aquí seleccionamos 2 clústeres
plt.title("Método del Codo")
plt.xlabel("Número de clústeres")
plt.ylabel("WCSS")
plt.show()

2.8 K-Means con k=2

kmeans = KMeans(n_clusters=2, random_state=123)
clusters = kmeans.fit_predict(mayoristas_scaled)

# KMeans con 2 clústeres
kmeans = KMeans(n_clusters=2, random_state=123)
labels = kmeans.fit_predict(mayoristas_scaled)


#Visualización con PCA
pca = PCA(n_components=2)
components = pca.fit_transform(mayoristas_scaled)

plt.figure(figsize=(8, 6))
plt.scatter(components[:, 0], components[:, 1], c=labels, cmap="viridis", s=50)
plt.title("Clustering K-Means (k=2)")
plt.xlabel("PCA 1")
plt.ylabel("PCA 2")
plt.grid(True)
plt.show()

2.9 Añadir clúster al dataset original

mayoristas_features["cluster"] = clusters.astype(str)

2.10 PCA para visualización

pca = PCA(n_components=2)
pca_result = pca.fit_transform(mayoristas_scaled)
pca_df = pd.DataFrame(pca_result, columns=["PC1", "PC2"])
pca_df["cluster"] = mayoristas_features["cluster"]

print(pca_df)
          PC1       PC2 cluster
0    0.193291 -0.305100       1
1    0.434420 -0.328413       1
2    0.811143  0.815096       1
3   -0.778648  0.652754       1
4    0.166287  1.271434       1
..        ...       ...     ...
435  0.870602  2.220845       1
436 -0.902520  1.676916       1
437  3.465704 -1.039838       0
438 -0.918023 -0.030047       1
439 -1.105137 -0.861338       1

[440 rows x 3 columns]

2.11 Visualización con PCA

plt.figure(figsize=(8, 6))
sns.scatterplot(data=pca_df, x="PC1", y="PC2", hue="cluster", palette="Set2", s=100, alpha=0.8)
centers_pca = pca.transform(kmeans.cluster_centers_)
plt.scatter(centers_pca[:, 0], centers_pca[:, 1], c="black", s=200, marker="X")
plt.title("Clústeres visualizados con PCA")
plt.show()

2.12 Perfil medio por clúster

perfil = mayoristas_features.groupby("cluster").mean(numeric_only=True)
print(perfil)
                fresh          milk  ...  detergents_paper   delicassen
cluster                              ...                               
0        11189.698113  19090.264151  ...      11850.471698  3564.433962
1        12111.310078   3975.640827  ...       1653.183463  1245.550388

[2 rows x 6 columns]

2.13 Visualización de perfiles

perfil.T.plot(kind="bar", figsize=(10, 6))
plt.title("Perfil de gasto por clúster")
plt.ylabel("Promedio")
plt.xticks(rotation=45)
(array([0, 1, 2, 3, 4, 5]), [Text(0, 0, 'fresh'), Text(1, 0, 'milk'), Text(2, 0, 'grocery'), Text(3, 0, 'frozen'), Text(4, 0, 'detergents_paper'), Text(5, 0, 'delicassen')])
plt.legend(title="Clúster")
plt.tight_layout()
plt.show()