Componentes Principales y Análisis Factorial

Autor/a

Guillermo Mazana

Fecha de publicación

16 de mayo, 2026

1 Lectura y análisis de los datos

Ver código fuente
library(readxl)
datos <- read_excel("heinekenData.xlsx", sheet = "perceptions")

Como vemos, los datos tienen una estructura en la que las marcas están en las columnas y los atributos en las filas. Lo ideal es que las marcas sean las observaciones (filas) y los atributos las variables (columnas). Por tanto, trasponemos la matriz:

Ver código fuente
datos_df <- as.data.frame(datos)
# Asignamos la primera columna (atributos) como nombres de las filas
rownames(datos_df) <- datos_df[, 1]
# Eliminamos la primera columna para que la tabla SOLO tenga números puros
datos_df <- datos_df[, -1]
# Trasponemos
datos_t <- as.data.frame(t(datos_df))

Ahora, presentamos la cabecera y cola de los datos:

Ver código fuente
head(datos_t)
          Expensive    Value WellKnown  Premium   Watery EasyToDrink   Smooth
Mahou      2.671053 3.704918  4.356164 3.608696 2.582609    4.197183 3.159292
Cruzcampo  2.430769 3.807692  4.169231 3.590164 2.906250    4.123077 3.226804
Heineken   3.651685 3.306667  4.679012 4.048780 2.833333    4.100000 3.330579
Estrella   2.754098 3.686275  4.089286 3.476923 2.686047    3.803571 3.087912
SanMiguel  2.723077 3.694915  4.523077 3.522388 2.794118    3.971014 3.367925
Carlsberg  3.603175 3.188679  4.000000 3.683333 2.767442    3.785714 3.202128
            Bitter Refreshing EverydayHome ShareAtHome Home party drink
Mahou     2.415929   3.887097     3.375000    3.881579         3.987013
Cruzcampo 2.443299   4.017241     2.900000    3.500000         3.733333
Heineken  2.297521   4.162500     2.898305    3.923077         4.087500
Estrella  2.659341   3.901961     2.739130    3.389831         3.683333
SanMiguel 2.358491   3.951613     3.017544    3.619718         3.784615
Carlsberg 2.340426   3.517241     2.300000    3.444444         3.366667
          With food Night club International To be seen   Family Sophisticated
Mahou      3.897059   2.956522      2.688679   3.098361 4.126437      2.802817
Cruzcampo  3.898305   2.803571      2.629213   2.745455 4.180556      2.683333
Heineken   3.000000   4.243243      4.661157   3.459459 3.316832      3.625000
Estrella   3.686275   3.089286      2.913043   2.956522 3.466667      2.527273
SanMiguel  3.838710   2.898305      3.161905   2.912281 4.038462      2.720588
Carlsberg  2.862745   4.019231      4.284091   3.226415 2.833333      3.454545
          Youthful Availability (Out) Availability (Store) National Price
Mahou     3.358209           3.974359             4.363636 4.486842  2.40
Cruzcampo 3.406780           3.720588             4.254237 4.615385  2.37
Heineken  3.756757           4.022989             4.388235 1.865854  3.39
Estrella  3.129630           3.754098             4.203125 4.298246  2.25
SanMiguel 2.966102           3.739726             4.342857 4.721311  2.19
Carlsberg 3.254902           3.412698             3.721311 1.633333  3.29
          Market share
Mahou             18.2
Cruzcampo         16.5
Heineken           4.1
Estrella          11.4
SanMiguel          9.0
Carlsberg          4.0
Ver código fuente
tail(datos_t)
          Expensive    Value WellKnown  Premium   Watery EasyToDrink   Smooth
SanMiguel  2.723077 3.694915  4.523077 3.522388 2.794118    3.971014 3.367925
Carlsberg  3.603175 3.188679  4.000000 3.683333 2.767442    3.785714 3.202128
Coronita   4.088608 2.661017  4.107692 3.933333 3.583333    4.338462 4.240000
Amstel     2.600000 3.432432  3.979592 3.333333 2.893333    3.723404 2.945205
Bud        4.122449 2.875000  3.508772 4.018868 2.842105    3.531915 3.243590
VollDamm   3.812500 3.240000  3.672727 4.116667 1.523810    3.235294 1.943820
            Bitter Refreshing EverydayHome ShareAtHome Home party drink
SanMiguel 2.358491   3.951613     3.017544    3.619718         3.784615
Carlsberg 2.340426   3.517241     2.300000    3.444444         3.366667
Coronita  1.810000   4.138462     2.396226    3.449275         3.714286
Amstel    2.547945   3.545455     2.195122    3.314815         3.568182
Bud       2.410256   3.555556     2.000000    3.454545         3.423077
VollDamm  3.460674   3.369231     2.531915    3.622642         3.603448
          With food Night club International To be seen   Family Sophisticated
SanMiguel  3.838710   2.898305      3.161905   2.912281 4.038462      2.720588
Carlsberg  2.862745   4.019231      4.284091   3.226415 2.833333      3.454545
Coronita   2.460317   3.750000      4.372549   3.240000 2.604938      3.500000
Amstel     3.755556   2.921569      3.482353   2.853659 3.333333      2.816327
Bud        2.666667   3.717391      4.528090   3.250000 2.446154      3.479167
VollDamm   2.574468   3.446809      3.823529   3.215686 2.672131      3.716667
          Youthful Availability (Out) Availability (Store) National Price
SanMiguel 2.966102           3.739726             4.342857 4.721311  2.19
Carlsberg 3.254902           3.412698             3.721311 1.633333  3.29
Coronita  3.830769           3.776316             4.069444 1.671429  4.06
Amstel    2.916667           3.442308             4.288462 3.647059  2.30
Bud       2.847826           2.535714             3.240000 1.392857  3.30
VollDamm  2.472727           3.265625             3.563636 3.125000  3.50
          Market share
SanMiguel            9
Carlsberg            4
Coronita             4
Amstel              10
Bud                  4
VollDamm            17

Eliminamos las columnas Price y Market share

Ver código fuente
# Filtramos las columnas que no sean "Price" ni "Market.share"
datos_analisis <- datos_t[, !(names(datos_t) %in% c("Price", "Market share"))]
Ver código fuente
# Aseguramos que todo sea numérico
datos_analisis[] <- lapply(datos_analisis, function(x) as.numeric(as.character(x)))

Sumario de estadísticas:

Ver código fuente
library(skimr)
skim(datos_analisis)
Data summary
Name datos_analisis
Number of rows 10
Number of columns 22
_______________________
Column type frequency:
numeric 22
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
Expensive 0 1 3.25 0.67 2.43 2.68 3.18 3.77 4.12 ▇▁▁▃▅
Value 0 1 3.36 0.38 2.66 3.20 3.37 3.69 3.81 ▃▁▆▂▇
WellKnown 0 1 4.11 0.36 3.51 3.98 4.10 4.31 4.68 ▃▁▇▂▃
Premium 0 1 3.73 0.27 3.33 3.54 3.65 4.00 4.12 ▅▇▂▂▇
Watery 0 1 2.74 0.50 1.52 2.71 2.81 2.88 3.58 ▁▁▂▇▁
EasyToDrink 0 1 3.88 0.33 3.24 3.74 3.89 4.12 4.34 ▂▂▇▅▇
Smooth 0 1 3.17 0.56 1.94 3.11 3.21 3.31 4.24 ▁▁▇▂▁
Bitter 0 1 2.47 0.41 1.81 2.34 2.41 2.52 3.46 ▁▇▂▁▁
Refreshing 0 1 3.80 0.28 3.37 3.55 3.89 4.00 4.16 ▅▅▁▇▇
EverydayHome 0 1 2.64 0.42 2.00 2.32 2.64 2.90 3.38 ▅▇▂▇▂
ShareAtHome 0 1 3.56 0.20 3.31 3.45 3.48 3.62 3.92 ▃▇▃▁▃
Home party drink 0 1 3.70 0.22 3.37 3.58 3.70 3.77 4.09 ▃▃▇▁▃
With food 0 1 3.26 0.60 2.46 2.72 3.34 3.82 3.90 ▅▃▁▁▇
Night club 0 1 3.38 0.52 2.80 2.93 3.27 3.74 4.24 ▇▁▂▃▃
International 0 1 3.65 0.78 2.63 2.98 3.65 4.35 4.66 ▆▂▃▁▇
To be seen 0 1 3.10 0.22 2.75 2.92 3.16 3.24 3.46 ▃▃▂▇▂
Family 0 1 3.30 0.65 2.45 2.71 3.33 3.90 4.18 ▇▂▇▁▇
Sophisticated 0 1 3.13 0.46 2.53 2.74 3.14 3.49 3.72 ▆▃▁▂▇
Youthful 0 1 3.19 0.42 2.47 2.93 3.19 3.39 3.83 ▂▇▅▅▅
Availability (Out) 0 1 3.56 0.43 2.54 3.42 3.73 3.77 4.02 ▂▁▃▃▇
Availability (Store) 0 1 4.04 0.40 3.24 3.81 4.23 4.33 4.39 ▁▁▁▁▇
National 0 1 3.15 1.38 1.39 1.72 3.39 4.44 4.72 ▇▁▂▂▇

Por tanto, vemos que los datos están completos.

Por útlimo, analizamos las correlaciones que se dan entre las distintas puntuaciones

Ver código fuente
library(corrplot)
corrplot 0.95 loaded
Ver código fuente
matriz_correlacion <- cor(datos_analisis, use = "complete.obs")

cat("\n--- MATRIZ DE CORRELACIONES ---\n")

--- MATRIZ DE CORRELACIONES ---
Ver código fuente
print(round(matriz_correlacion, 2))
                     Expensive Value WellKnown Premium Watery EasyToDrink
Expensive                 1.00 -0.92     -0.42    0.87   0.00       -0.26
Value                    -0.92  1.00      0.46   -0.66  -0.27        0.12
WellKnown                -0.42  0.46      1.00   -0.25   0.30        0.73
Premium                   0.87 -0.66     -0.25    1.00  -0.24       -0.23
Watery                    0.00 -0.27      0.30   -0.24   1.00        0.74
EasyToDrink              -0.26  0.12      0.73   -0.23   0.74        1.00
Smooth                    0.12 -0.32      0.39   -0.09   0.95        0.81
Bitter                   -0.08  0.28     -0.42    0.13  -0.96       -0.81
Refreshing               -0.19  0.16      0.77   -0.05   0.62        0.88
EverydayHome             -0.56  0.71      0.75   -0.22  -0.13        0.52
ShareAtHome               0.03  0.22      0.60    0.38  -0.25        0.30
Home party drink         -0.29  0.39      0.82    0.04   0.07        0.61
With food                -0.98  0.91      0.47   -0.84   0.06        0.31
Night club                0.84 -0.71     -0.05    0.72   0.11       -0.05
International             0.90 -0.86     -0.22    0.71   0.16       -0.19
To be seen                0.84 -0.67     -0.02    0.82  -0.07       -0.07
Family                   -0.90  0.91      0.67   -0.64   0.04        0.49
Sophisticated             0.92 -0.79     -0.30    0.89  -0.18       -0.30
Youthful                  0.05 -0.14      0.62    0.05   0.73        0.90
Availability (Out)       -0.47  0.48      0.88   -0.30   0.21        0.73
Availability (Store)     -0.70  0.62      0.87   -0.57   0.32        0.70
National                 -0.91  0.90      0.33   -0.69  -0.23        0.14
                     Smooth Bitter Refreshing EverydayHome ShareAtHome
Expensive              0.12  -0.08      -0.19        -0.56        0.03
Value                 -0.32   0.28       0.16         0.71        0.22
WellKnown              0.39  -0.42       0.77         0.75        0.60
Premium               -0.09   0.13      -0.05        -0.22        0.38
Watery                 0.95  -0.96       0.62        -0.13       -0.25
EasyToDrink            0.81  -0.81       0.88         0.52        0.30
Smooth                 1.00  -0.98       0.71         0.01       -0.07
Bitter                -0.98   1.00      -0.66        -0.01        0.03
Refreshing             0.71  -0.66       1.00         0.56        0.38
EverydayHome           0.01  -0.01       0.56         1.00        0.72
ShareAtHome           -0.07   0.03       0.38         0.72        1.00
Home party drink       0.19  -0.18       0.74         0.82        0.80
With food             -0.04  -0.01       0.25         0.61        0.06
Night club             0.19  -0.22       0.00        -0.39        0.21
International          0.19  -0.22      -0.13        -0.62        0.02
To be seen             0.08  -0.10       0.00        -0.19        0.46
Family                 0.04  -0.07       0.46         0.83        0.34
Sophisticated         -0.08   0.07      -0.25        -0.45        0.21
Youthful               0.80  -0.80       0.83         0.30        0.29
Availability (Out)     0.29  -0.28       0.73         0.78        0.48
Availability (Store)   0.30  -0.32       0.69         0.70        0.32
National              -0.25   0.28       0.16         0.67        0.07
                     Home party drink With food Night club International
Expensive                       -0.29     -0.98       0.84          0.90
Value                            0.39      0.91      -0.71         -0.86
WellKnown                        0.82      0.47      -0.05         -0.22
Premium                          0.04     -0.84       0.72          0.71
Watery                           0.07      0.06       0.11          0.16
EasyToDrink                      0.61      0.31      -0.05         -0.19
Smooth                           0.19     -0.04       0.19          0.19
Bitter                          -0.18     -0.01      -0.22         -0.22
Refreshing                       0.74      0.25       0.00         -0.13
EverydayHome                     0.82      0.61      -0.39         -0.62
ShareAtHome                      0.80      0.06       0.21          0.02
Home party drink                 1.00      0.35      -0.08         -0.24
With food                        0.35      1.00      -0.81         -0.87
Night club                      -0.08     -0.81       1.00          0.92
International                   -0.24     -0.87       0.92          1.00
To be seen                       0.12     -0.78       0.91          0.83
Family                           0.58      0.92      -0.70         -0.83
Sophisticated                   -0.17     -0.93       0.86          0.88
Youthful                         0.53     -0.02       0.34          0.16
Availability (Out)               0.79      0.45      -0.17         -0.39
Availability (Store)             0.75      0.71      -0.41         -0.52
National                         0.34      0.88      -0.92         -0.96
                     To be seen Family Sophisticated Youthful
Expensive                  0.84  -0.90          0.92     0.05
Value                     -0.67   0.91         -0.79    -0.14
WellKnown                 -0.02   0.67         -0.30     0.62
Premium                    0.82  -0.64          0.89     0.05
Watery                    -0.07   0.04         -0.18     0.73
EasyToDrink               -0.07   0.49         -0.30     0.90
Smooth                     0.08   0.04         -0.08     0.80
Bitter                    -0.10  -0.07          0.07    -0.80
Refreshing                 0.00   0.46         -0.25     0.83
EverydayHome              -0.19   0.83         -0.45     0.30
ShareAtHome                0.46   0.34          0.21     0.29
Home party drink           0.12   0.58         -0.17     0.53
With food                 -0.78   0.92         -0.93    -0.02
Night club                 0.91  -0.70          0.86     0.34
International              0.83  -0.83          0.88     0.16
To be seen                 1.00  -0.64          0.86     0.25
Family                    -0.64   1.00         -0.79     0.17
Sophisticated              0.86  -0.79          1.00     0.03
Youthful                   0.25   0.17          0.03     1.00
Availability (Out)        -0.11   0.64         -0.35     0.63
Availability (Store)      -0.38   0.78         -0.60     0.51
National                  -0.81   0.86         -0.86    -0.22
                     Availability (Out) Availability (Store) National
Expensive                         -0.47                -0.70    -0.91
Value                              0.48                 0.62     0.90
WellKnown                          0.88                 0.87     0.33
Premium                           -0.30                -0.57    -0.69
Watery                             0.21                 0.32    -0.23
EasyToDrink                        0.73                 0.70     0.14
Smooth                             0.29                 0.30    -0.25
Bitter                            -0.28                -0.32     0.28
Refreshing                         0.73                 0.69     0.16
EverydayHome                       0.78                 0.70     0.67
ShareAtHome                        0.48                 0.32     0.07
Home party drink                   0.79                 0.75     0.34
With food                          0.45                 0.71     0.88
Night club                        -0.17                -0.41    -0.92
International                     -0.39                -0.52    -0.96
To be seen                        -0.11                -0.38    -0.81
Family                             0.64                 0.78     0.86
Sophisticated                     -0.35                -0.60    -0.86
Youthful                           0.63                 0.51    -0.22
Availability (Out)                 1.00                 0.90     0.44
Availability (Store)               0.90                 1.00     0.60
National                           0.44                 0.60     1.00
Ver código fuente
# Gráfico de correlación
corrplot(matriz_correlacion, method = "color", type = "upper", 
         tl.col = "black", tl.srt = 45, number.cex = 0.6,
         title = "\nCorrelación de Percepciones (Excluyendo Precio y Cuota)",
         mar = c(0,0,2,0))

Ver código fuente
# Extraer correlaciones mayores a 0.8
matriz_triangular <- matriz_correlacion
matriz_triangular[lower.tri(matriz_triangular, diag = TRUE)] <- NA
pares_correlacion <- as.data.frame(as.table(matriz_triangular))
pares_correlacion <- na.omit(pares_correlacion)
names(pares_correlacion) <- c("Variable_1", "Variable_2", "Correlacion")

correlaciones_altas <- pares_correlacion[abs(pares_correlacion$Correlacion) >= 0.8, ]
correlaciones_altas <- correlaciones_altas[order(-abs(correlaciones_altas$Correlacion)), ]

cat("\n--- VARIABLES CON CORRELACIÓN FUERTE (|r| >= 0.8) ---\n")

--- VARIABLES CON CORRELACIÓN FUERTE (|r| >= 0.8) ---
Ver código fuente
if (nrow(correlaciones_altas) > 0) {
  print(correlaciones_altas, row.names = FALSE)
} else {
  cat("No se encontraron pares de variables con una correlación >= 0.8\n")
}
         Variable_1           Variable_2 Correlacion
          Expensive            With food  -0.9800300
             Smooth               Bitter  -0.9778697
      International             National  -0.9598230
             Watery               Bitter  -0.9561162
             Watery               Smooth   0.9542240
          With food        Sophisticated  -0.9288299
          With food               Family   0.9239149
          Expensive        Sophisticated   0.9190182
          Expensive                Value  -0.9185220
         Night club        International   0.9180042
         Night club             National  -0.9171369
         Night club           To be seen   0.9142287
              Value            With food   0.9106775
          Expensive             National  -0.9062882
              Value               Family   0.9052775
 Availability (Out) Availability (Store)   0.9003648
              Value             National   0.8987205
        EasyToDrink             Youthful   0.8978660
          Expensive               Family  -0.8971395
          Expensive        International   0.8966092
            Premium        Sophisticated   0.8929115
          With food             National   0.8827281
      International        Sophisticated   0.8820083
        EasyToDrink           Refreshing   0.8810606
          WellKnown   Availability (Out)   0.8767508
          WellKnown Availability (Store)   0.8671863
          Expensive              Premium   0.8666223
          With food        International  -0.8661183
         To be seen        Sophisticated   0.8645182
              Value        International  -0.8609025
             Family             National   0.8588073
      Sophisticated             National  -0.8573877
         Night club        Sophisticated   0.8561515
            Premium            With food  -0.8444225
          Expensive           To be seen   0.8424814
          Expensive           Night club   0.8380734
         Refreshing             Youthful   0.8322543
      International           To be seen   0.8306677
      International               Family  -0.8280993
       EverydayHome               Family   0.8253696
       EverydayHome     Home party drink   0.8215280
          WellKnown     Home party drink   0.8196864
            Premium           To be seen   0.8187073
        EasyToDrink               Bitter  -0.8141680
        EasyToDrink               Smooth   0.8131510
          With food           Night club  -0.8097988
         To be seen             National  -0.8086188
        ShareAtHome     Home party drink   0.8046217
             Bitter             Youthful  -0.8024077

Vemos que hay muchísimas variables muy altamente correlacionadas.

2 Componentes Principales

El Análisis de Componentes Principales (ACP) es una técnica estadística multivariante enfocada en la reducción de dimensionalidad.

Su objetivo principal es transformar un conjunto de datos que contiene múltiples variables (frecuentemente correlacionadas entre sí) en un nuevo conjunto reducido de variables que no están correlacionadas, manteniendo la mayor cantidad posible de información o variabilidad del conjunto original.

A nivel conceptual y matemático, el ACP se rige por los siguientes principios:

  • Componentes Principales: Las nuevas variables creadas se denominan componentes principales. Estas son combinaciones lineales de las variables originales.

  • Maximización de la varianza: Los componentes se calculan de manera secuencial. El primer componente principal se traza en la dirección que captura la mayor proporción de la varianza (dispersión) de los datos. El segundo componente captura la máxima varianza restante, con la condición estricta de ser ortogonal (perpendicular y no correlacionado) al primero. Este proceso se repite hasta igualar el número de variables originales.

  • Reducción: Dado que los primeros componentes concentran la inmensa mayoría de la información útil, los analistas pueden descartar los últimos componentes (que suelen representar “ruido” estadístico), reduciendo así drásticamente la complejidad del modelo.

En la práctica, el análisis de componentes principales se utiliza ampliamente para simplificar el procesamiento de grandes volúmenes de datos, facilitar la visualización en dos o tres dimensiones, mejorar la eficiencia computacional y evitar problemas de multicolinealidad en modelos de aprendizaje automático predictivos.

Sea \(R\) la matriz de correlación, resolvemos la ecuación característica, donde \(\lambda\) son los valores propios (varianza) y que, por tanto, ccalculando \(\sqrt{\lambda}\) obtenemos la desviación típica: \[\det(R - \lambda I) = 0\] La suma de todos los autovalores es igual al número de variables que tenemos

Ver código fuente
# scale. = TRUE para estandarizar las variables
modelo_pca <- prcomp(datos_analisis, center = TRUE, scale. = TRUE)
summary(modelo_pca)
Importance of components:
                          PC1    PC2    PC3     PC4     PC5     PC6     PC7
Standard deviation     3.2525 2.5341 1.8097 0.76873 0.64869 0.50182 0.43725
Proportion of Variance 0.4808 0.2919 0.1489 0.02686 0.01913 0.01145 0.00869
Cumulative Proportion  0.4808 0.7728 0.9216 0.94848 0.96761 0.97906 0.98775
                           PC8     PC9      PC10
Standard deviation     0.38170 0.35200 1.423e-15
Proportion of Variance 0.00662 0.00563 0.000e+00
Cumulative Proportion  0.99437 1.00000 1.000e+00

Resultados:

  • Standard deviation. Un valor mayor que 1 nos indica que ese componente explica más información que una sola de las variables originales por separado (suponiendo que los datos están estandarizados). En este caso, tenemos que los tres primeros componentes (\(PC1\), \(PC2\) y \(PC3\)) son muy potentes porque tienen desviaciones significativamente altas (\(3.25\), \(2.53\) y \(1.80\)).

  • Proportion of Variance. Es el dato más importante del análisis. Indica qué porcentaje del “ruido” o información total del archivo original está contenido en ese componente específico.

    • PC1 (0.4808): El primer componente por sí solo captura el \(48.08\%\) de toda la variabilidad de las percepciones de las cervezas. Casi la mitad de las diferencias entre marcas se explican por un solo eje.

    • PC2 (0.2919): El segundo componente captura el \(29.19\%\).

    • A partir del \(PC4\), la proporción cae drásticamente (\(2.6\%\)), lo que significa que esos componentes ya solo capturan detalles irrelevantes o errores de medición.

  • Cumulative Proportion. Es la suma consecutiva de la fila anterior. Ayuda a decidir con cuántos componentes debemos quedarnos para el estudio:

    • PC1 + PC2 = 0.7728: Esto significa que si solo usamos los dos primeros componentes (los que aparecen en el Biplot), estaremos visualizando el \(77.28\%\) de toda la información del estudio. En ciencias sociales y marketing, superar el \(70\%\) es un resultado excelente.

    • PC1 + PC2 + PC3 = 0.9216: Si icluimos un tercer eje, llegaríamos al \(92.16\%\) de la información.

Ahora, mostramos los pesos de las variables en los componentes 1 y 2 así como las puntuaciones de las marcas

Ver código fuente
# Pesos
round(modelo_pca$rotation[, 1:2], 2)
                       PC1   PC2
Expensive             0.28  0.14
Value                -0.26 -0.15
WellKnown            -0.21  0.23
Premium               0.22  0.12
Watery               -0.05  0.27
EasyToDrink          -0.17  0.30
Smooth               -0.04  0.32
Bitter                0.05 -0.32
Refreshing           -0.16  0.30
EverydayHome         -0.24  0.07
ShareAtHome          -0.07  0.16
Home party drink     -0.18  0.20
With food            -0.28 -0.11
Night club            0.22  0.22
International         0.26  0.18
To be seen            0.21  0.21
Family               -0.30 -0.02
Sophisticated         0.26  0.12
Youthful             -0.08  0.36
Availability (Out)   -0.22  0.19
Availability (Store) -0.27  0.13
National             -0.27 -0.18

Para interpretar los pesos, debemos de fijarnos en los valores más grandes, en módulo:

  • Componente Principal 1 (PC1): El eje del “Estatus / Exclusividad vs. Cotidianidad”. Si observamos la columna PC1, vemos una clara oposición entre dos tipos de consumo:

    • Valores muy positivos (El lado del Estatus/Lujo):

      • Expensive (0.28)

      • International (0.26)

      • Sophisticated (0.26)

      • Premium (0.22)

      • Night club (0.22)

      • To be seen (0.21)

      • Significado: Las marcas que puntúen en positivo en este eje son percibidas como cervezas internacionales, caras, sofisticadas y orientadas al ocio nocturno y el “postureo” (para ser visto con ellas).

    • Valores muy negativos (El lado de lo Cotidiano/Familiar):

      • Family (-0.30)

      • With food (-0.28)

      • Availability (Store) (-0.27)

      • National (-0.27)

      • Value (buena relación calidad/precio) (-0.26)

      • EverydayHome (-0.24)

      • Significado: Las marcas con puntuaciones negativas aquí son las cervezas “de toda la vida”: nacionales, fáciles de encontrar en el súper, para comer, para estar en familia o consumir a diario en casa, y con buen precio.

  • Componente Principal 2 (PC2): El eje de “Ligereza / Juventud vs. Intensidad”. Si miramos la columna PC2, la diferenciación cambia y se centra más en el sabor y el público:

    • Valores muy positivos (El lado Ligero y Juvenil):

      • Youthful (0.36) - Es la variable que más define este eje.

      • Smooth (Suave) (0.32)

      • EasyToDrink (0.30)

      • Refreshing (0.30)

      • Watery (Aguada) (0.27)

      • WellKnown (0.23)

      • Significado: Cervezas muy fáciles de beber, refrescantes, suaves (incluso percibidas como algo aguadas) y fuertemente asociadas a un público muy joven y a fiestas (Home party drink tiene 0.20).

    • Valores muy negativos (El lado Intenso):

      • Bitter (Amarga) (-0.32)

      • Significado: Básicamente, este lado se define por la oposición a lo anterior. Son cervezas donde predomina un sabor intenso y amargo.

Finalmente, veamos los scores o puntuaciones:

Ver código fuente
# Puntuaciones
round(modelo_pca$x[, 1:2], 2)
            PC1   PC2
Mahou     -3.77  0.43
Cruzcampo -3.66 -0.70
Heineken   0.29  4.21
Estrella  -2.14 -1.41
SanMiguel -3.28 -0.38
Carlsberg  2.75  0.14
Coronita   1.95  4.33
Amstel    -0.95 -2.24
Bud        5.13 -0.82
VollDamm   3.68 -3.57

Tenemos, como hemos visto antes, dos ejes:

  • Eje X (PC1): Valores negativos = Cerveza Nacional, Tradicional, Cotidiana y Económica. / Valores positivos = Cerveza Internacional, Premium, Cara y Sofisticada.

  • Eje Y (PC2): Valores negativos = Sabor Amargo e Intenso. / Valores positivos = Sabor Suave, Refrescante, Fácil de beber y Juvenil.

Con estas coordenadas, podemos dividir el mercado en 3 grandes grupos o territorios competitivos:

  1. El bloque “Nacional y Cotidiano” (A la izquierda del mapa)
  • Mahou (-3.77, 0.43)

  • Cruzcampo (-3.66, -0.70)

  • San Miguel (-3.28, -0.38)

  • Estrella (-2.14, -1.41)

  • Amstel (-0.95, -2.24)

  • Interpretación: Todas tienen valores fuertemente negativos en el PC1. Estas son las cervezas “de toda la vida”. El consumidor las percibe como marcas para tomar en familia, comiendo o a diario en casa, con buena relación calidad-precio y muy disponibles. Diferenciación interna: Amstel y Estrella se perciben como las más amargas/intensas de este grupo (PC2 negativo), mientras que Mahou es la más suave (PC2 ligeramente positivo). Estas marcas compiten a muerte entre ellas por el mismo espacio mental.

  1. El bloque “Fiesta, Juventud y Refresco” (Arriba en el mapa)
  • Coronita (1.95, 4.33)

  • Heineken (0.29, 4.21)

  • Interpretación: Destacan brutalmente por tener un PC2 altísimo (más de 4 puntos). Son las reinas absolutas de la percepción “Suave, Refrescante, Fácil de beber y Juvenil”. Diferenciación interna: Coronita (PC1 de 1.95) se percibe como más cara, de club nocturno o de “postureo” que Heineken, la cual es más neutra en el eje del precio/exclusividad (PC1 cercano a 0).

  1. El bloque “Premium, Internacional y/o Intenso” (A la derecha / Abajo)
  • Bud (5.13, -0.82)

  • VollDamm (3.68, -3.57)

  • Carlsberg (2.75, 0.14)

  • Interpretación: Tienen valores positivos muy altos en el PC1. Se perciben como cervezas de alto estatus, caras o fuertemente internacionales. Diferenciación interna: VollDamm tiene un perfil muy extremo: es Premium/Cara (3.68) pero también es la cerveza percibida como más amarga y fuerte de todo el estudio con mucha diferencia (PC2 de -3.57). Su posicionamiento es “Intensidad y Calidad”. Bud es percibida como la más cara o exclusiva del estudio (el PC1 más alto de todos: 5.13), con un toque de amargor. Carlsberg se percibe como Premium (2.75), pero no se asocia ni con un sabor amargo ni con uno especialmente ligero o juvenil (está justo en el medio con un PC2 de 0.14).

En el gráfico Biplot siguiente, veremos todo lo anterior de forma visual:

Ver código fuente
# BIPLOT
par(mar = c(2, 2, 2, 1))
biplot(modelo_pca, scale = 0, cex = 0.8, col = c("black", "red"),
       main = "Biplot PCA")

3 Análisis Factorial

El Análisis Factorial (AF) es una técnica estadística multivariante cuyo propósito fundamental es identificar la estructura subyacente de un conjunto de datos. Busca explicar las correlaciones observadas entre un gran número de variables manifiestas (observables) a través de un número menor de variables latentes (no observables), que reciben el nombre de factores.

A diferencia del Análisis de Componentes Principales (ACP) —que es un método de reducción de datos empíricos que intenta maximizar la varianza total explicada—, el Análisis Factorial parte de un modelo teórico. Asume que la varianza de cada variable se divide en dos partes:

  1. Comunalidad: La varianza compartida con otras variables (la que explican los factores comunes).

  2. Especificidad (y error): La varianza única e intrínseca de esa variable concreta.

El AF busca modelar únicamente esa varianza compartida. Es especialmente útil en ciencias sociales y estudios de mercado para medir constructos abstractos (como la “lealtad de marca”, la “sofisticación” o la “calidad percibida”) que no pueden medirse con una sola pregunta.

En el caso de nuestros datos no es estadísticamente ni matemáticamente viable realizar un Análisis Factorial utilizando la matriz de percepciones completa (22 atributos sobre 10 marcas de cerveza), por dos razones principales:

  1. Problema de dimensionalidad (Matriz Singular): En nuestro conjunto de datos, el número de variables o atributos (\(P = 22\)) es superior al número de observaciones o marcas (\(N = 10\)). Matemáticamente, esto genera una matriz de correlación “singular” o no definida positiva. Su determinante es cero o cercano a cero, lo que imposibilita la inversión de la matriz, un paso algebraico indispensable para los algoritmos de extracción de factores (como Máxima Verosimilitud).

  2. Tamaño muestral crítico: La literatura estadística (ej. Hair et al., Comrey y Lee) establece reglas estrictas para el Análisis Factorial Exploratorio. Se exige un tamaño de muestra mínimo absoluto de 50 observaciones (idealmente \(>100\)) y una ratio de al menos 5 observaciones por cada variable analizada. Con 10 marcas, carecemos de grados de libertad suficientes; los factores extraídos serían fruto del azar (“ruido” estadístico) y no serían generalizables. Pruebas formales de adecuación muestral, como el estadístico KMO (Kaiser-Meyer-Olkin), arrojarían resultados inaceptables o darían error computacional.

Por tanto, para que el modelo sea matemáticamente ejecutable (es decir, \(N > P\) y evitando la colinealidad perfecta), debemos realizar una reducción drástica y teórica a un subconjunto de variables. Así, basándonos en el Análisis de Componentes Principales realizado en la sección anterior, y en el número de grados de libertad necesarios para realizar el análisis facatorial: \[df = \frac{1}{2} [(P - M)^2 - (P + M)]\] donde \(P\) es el número de variables analizadas y \(M\) es el número de factores que deseamos extraer, tenemos que necesitamos como mínimo (\(df = 1\)), 5 variables (\(P = 5\) y \(M = 2\):): \[df = \frac{1}{2} [(5 - 2)^2 - (5 + 2)] = \frac{1}{2} [9 - 7] = 1\]

Si observamos la matriz de rotación, observamos que:

  • Para el componente 1 (eje x): Vimos que las variables con mayor carga positiva eran Expensive (\(0.28\)) e International (\(0.26\)) y una de las mayores cargas negativas era EverydayHome (\(-0.24\)). Al elegir estas variables, nos aseguramos de que el Factor 1 mida el constructo latente de “Estatus/Precio frente a Consumo Diario”.

  • Para el Componente 2 (eje y): La variable con mayor carga positiva fue Youthful (\(0.36\)) y la mayor carga negativa fue Bitter (\(-0.32\)). Al elegir este par, nos aseguramos de que el Factor 2 mida el constructo de “Identidad Juvenil/Suave frente a Intensidad/Amargor”.

Sabemos que estas variables son ortogonales por dos razones, una matemática y otra empírica: i) Por la construcción matemática del ACP (Ejes Ortogonales. El algoritmo del Análisis de Componentes Principales fuerza a que el Componente 1 (PC1) y el Componente 2 (PC2) sean estrictamente perpendiculares entre sí. Su correlación es exactamente \(0\)) y ii) Por la Matriz de Correlación (Evidencia Empírica. Si revisamos los datos numéricos de la matriz de correlación original para estas cuatro variables, veríamos que sus coeficientes de correlación de Pearson (\(r\)) son muy cercanos a cero cuando cruzamos variables de distintos ejes).

Por tanto, al seleccionar estas cuatro variables, “sintetizamos” los 22 atributos originales en los 5 conceptos que provocan la mayor diferenciación de las marcas en el mercado.

Aplicamos el código:

Ver código fuente
# datos_analisis contiene las 10 marcas como filas y los atributos numéricos como columnas
# Seleccionamos las variables representativas elegidas
variables_seleccionadas <- c("Expensive", "International", "Family", "Youthful", "Bitter")
datos_reducidos <- datos_analisis[, variables_seleccionadas]

# Prueba de adecuación
library(psych)
print(KMO(datos_reducidos)) 
Kaiser-Meyer-Olkin factor adequacy
Call: KMO(r = datos_reducidos)
Overall MSA =  0.65
MSA for each item = 
    Expensive International        Family      Youthful        Bitter 
         0.68          0.81          0.70          0.46          0.52 

El estadístico KMO (Kaiser-Meyer-Olkin) evalúa si las correlaciones parciales entre las variables son lo suficientemente fuertes como para agruparse en factores. Sus valores van de 0 a 1.

Vemos que tenemos un KMO Global de \(0.65>0.50\) (mínimo aceptable). Por encima de \(0.60\) se considera “mediocre pero válido” para continuar. Obtener un \(0.65\) con solo \(10\) marcas demuestra que las 5 variables que seleccionamos conforman una estructura coherente.

Por otro lado, tenemos que el KMO por variable (MSA) arroja que International (\(0.81\)) y Family (\(0.70\)) tienen una adecuación muy buena. La única nota de atención es Youthful (\(0.46\)), que al estar por debajo de \(0.50\) nos indica que, estrictamente hablando, tiene poca correlación compartida con el resto. No obstante, para los fines de este estudio, lo mantenemos en el modelo.

Ver código fuente
# Ejecutamos el Análisis Factorial (Extrayendo 2 factores)
# Usamos el método de factores principales u OLS debido al pequeño tamaño muestral
# Aplicamos rotación "varimax" para facilitar la interpretación
# Matriz de Cargas Factoriales (Loadings)
modelo_af <- factanal(datos_reducidos, factors = 2, rotation = "varimax")

print(modelo_af$loadings, cutoff = 0.3)

Loadings:
              Factor1 Factor2
Expensive      0.968         
International  0.914         
Family        -0.937         
Youthful               0.997 
Bitter                -0.806 

               Factor1 Factor2
SS loadings      2.653   1.702
Proportion Var   0.531   0.340
Cumulative Var   0.531   0.871

Los loadings son la parte central del análisis. Representan la correlación entre cada variable y el factor latente descubierto. Valores por encima de \(0.50\) se consideran fuertes, y por encima de \(0.8\) son extraordinarios:

Vemos que el Factor1 absorbe tres variables de forma casi perfecta (Expensive, International y Family). Este factor mide el posicionamiento corporativo de la marca. Si una cerveza puntúa alto en este factor, el consumidor la percibe inmediatamente como una cerveza cara, internacional y exclusiva. Si puntúa bajo (negativo), es dominada por el concepto de “Familia”, siendo percibida como la marca nacional de toda la vida, cercana y tradicional.

Por otro lado, el Factor2 absorbe las otras dos variables con gran fuerza (Youthful y Bitter). Este factor mide el perfil organoléptico y su target. Una puntuación alta aquí define a la cerveza casi exclusivamente como una bebida para jóvenes, suave y ligera (alejada del amargor). Por el contrario, una puntuación baja (hacia los valores negativos) define a una cerveza para paladares hechos al amargor y, por tanto, más adultos.

Finalmente, la varainza explicada por el modelo viene dadda por SS loadings, Proportion Var y Cumulative Var. Como vemos en Proportion Var, el Factor 1 explica por sí solo el \(53.1\%\) de la información total contenida en esas cinco variables, y el Factor 2 añade un \(34.0\%\) de explicación.

En general, según Cumulative Var, el modelo factorial explica el \(87.1\%\) de la varianza total.

4 Clúster. K-Means. Aprendizaje automático no supervisado

Un clúster (o conglomerado) es un grupo de marcas (u observaciones) que comparten un perfil de percepción extremadamente similar entre sí, al mismo tiempo que son lo más diferentes posible de las marcas que pertenecen a otros grupos.

Si el Análisis de Componentes Principales (ACP) o el Análisis Factorial nos servían para agrupar variables (reducir atributos), el análisis de clúster sirve para agrupar sujetos u objetos (en este caso, marcas de cerveza). En este caso, descubrir los clústers del mercado, permite:

  1. Identificar competidores directos: Las marcas que caen en el mismo clúster luchan exactamente por el mismo espacio mental del consumidor.

  2. Detectar nichos vacíos: Zonas del mapa donde no hay ningún clúster de marcas y donde se podría lanzar un nuevo producto.

El método K-Means es un algoritmo de aprendizaje no supervisado de particionamiento. Su objetivo es dividir un conjunto de datos en \(K\) grupos fijos (definidos previamente por el analista). Matemáticamente:

  1. Asignación inicial: El algoritmo selecciona de forma aleatoria \(K\) puntos en el espacio de datos, los cuales actúan como los “centros” iniciales de los grupos (llamados centroides).

  2. Cálculo de Distancia: Cada marca de cerveza se asigna al centroide más cercano utilizando una medida de distancia geométrica (normalmente la distancia euclídea).

  3. Recálculo de Centros: Una vez asignadas todas las cervezas, el centroide se mueve al centro real (la media matemática) de todas las marcas que componen ese grupo.

  4. Iteración: Los pasos 2 y 3 se repiten una y otra vez de forma automática hasta que las marcas ya no cambian de grupo. El algoritmo se detiene cuando ha logrado minimizar la varianza interna de cada clúster.

Según hemos ido viendo en nuestro análisis, podemos separar las marcas de cerveza en tres grupos, tal y como vimos en el biplot:

Ver código fuente
par(mar = c(2, 2, 2, 1))
biplot(modelo_pca, scale = 0, cex = 0.8, col = c("black", "red"),
       main = "Biplot PCA")

Como vemos, tenemos tres grupos: i) Un grupo grande en el lado izquierdo (las cervezas tradicionales y familiares como Mahou, San Miguel, Cruzcampo); ii) Un par de marcas muy juntas en la parte superior derecha (Heineken y Coronita, enfocadas en jóvenes y noche); y iii) Un grupo en la parte inferior derecha (cervezas caras, internacionales o intensas como Bud y Voll-Damm).

Por tanto, configuraremos el algoritmo para buscar \(K = 3\) clústeres, coincidiendo con la estructura tripartita que descubrimos intuitivamente en los análisis anteriores.

Ver código fuente
library(stats)
# Eestandarizamos los datos (Paso obligatorio antes de aplicar K-Means)
datos_escalados <- scale(datos_analisis)

# Semilla para reproducibilidad y ejecutar K-Means (K = 3 grupos)
set.seed(123)
segmentacion_km <- kmeans(datos_escalados, centers = 3, nstart = 25) # Lo iteramos 25 veces

# Guardamos el clúster asignado en el dataframe original como factor
datos_analisis$Cluster <- as.factor(segmentacion_km$cluster)

# Mostramos la lista de asignación por pantalla
datos_analisis[, "Cluster", drop = FALSE]
          Cluster
Mahou           3
Cruzcampo       3
Heineken        1
Estrella        3
SanMiguel       3
Carlsberg       2
Coronita        1
Amstel          3
Bud             2
VollDamm        2
Ver código fuente
# GRAFICAMOS
modelo_pca <- prcomp(datos_escalados, center = FALSE, scale. = FALSE)
df_grafico <- as.data.frame(modelo_pca$x[, 1:2])
df_grafico$Marca   <- rownames(df_grafico)
df_grafico$Cluster <- datos_analisis$Cluster

library(ggplot2)
Warning: package 'ggplot2' was built under R version 4.5.3

Adjuntando el paquete: 'ggplot2'
The following objects are masked from 'package:psych':

    %+%, alpha
Ver código fuente
ggplot(df_grafico, aes(x = PC1, y = PC2, color = Cluster, label = Marca)) +
  geom_point(size = 4) +
  geom_text(vjust = -1, fontface = "bold", size = 3) +
  stat_ellipse(aes(fill = Cluster), alpha = 0.1, geom = "polygon", linetype = "blank") +
  labs(title = "Segmentación Estratégica de Marcas de Cerveza (K-Means)",
       subtitle = "Agrupación basada en perfiles de percepción independientes",
       x = "Dimensión 1 (Estatus / Globalidad)",
       y = "Dimensión 2 (Sabor / Juventud)") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5),
        legend.position = "right") +
  geom_vline(xintercept = 0, linetype = "dashed", alpha = 0.2) +
  geom_hline(yintercept = 0, linetype = "dashed", alpha = 0.2)
Too few points to calculate an ellipse
Too few points to calculate an ellipse
Warning: The following aesthetics were dropped during statistical transformation: label.
ℹ This can happen when ggplot fails to infer the correct grouping structure in
  the data.
ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
  variable into a factor?

Obesrvamos así cómo se segmenta el mercado en tres zonas. La división más profunda y radical aísla a Heineken y Coronita del resto de competidores, posicionándolas en un nicho único centrado en la imagen social, la juventud y el ocio nocturno. Alejado de este enfoque lúdico, el resto del mercado se escinde en dos frentes opuestos: las marcas orientadas al consumo masivo diario y aquellas que apelan a un consumidor en busca de una experiencia de sabor más exigente.

En el territorio cotidiano compiten ferozmente marcas nacionales como Mahou, San Miguel, Cruzcampo, Estrella y Amstel por dominar la rutina y el entorno familiar; aquí destaca la percepción casi idéntica del consumidor entre Mahou y San Miguel, frente al perfil ligeramente más amargo de Amstel. Por su parte, el bloque formado por Bud, Carlsberg y Voll-Damm se reserva para quienes priorizan el precio premium, la internacionalidad o un carácter organoléptico intenso.

En definitiva, los datos confirman que el éxito en este sector no pasa por gustar a todo el mundo, sino por elegir y dominar una batalla específica: la socialización, la rutina o la exclusividad.