Clustering + Discriminat Analysis + Conjoint Analysis

Author

Oscar Chullo | Percy Colque

Clustering

Descripción del Conjunto de Datos

Para la aplicación del clustering se ha hecho uso de la base de datos StateData.xlsx, el cual fue compilado para realizar comparaciones a nivel nacional e incluye cuatro categorías principales de datos para los 48 estados contiguos de Estados Unidos.

Alaska, Hawái y el Distrito de Columbia fueron excluidos debido a la falta de datos sobre sus regiones psicológicas.

Las variables se dividen en las siguientes categorías:

  • Datos Geográficos: Incluye el nombre común del estado, su código postal de dos letras y la región censal a la que pertenece (Noreste, Medio Oeste, Sur y Oeste).
  • Datos Políticos: Indica si el gobernador del estado, a fecha del 26 de julio de 2018, era Demócrata o Republicano.
  • Datos de Personalidad: Clasifica a los estados en tres perfiles (“Amistoso y Convencional”, “Relajado y Creativo”, “Temperamental y Desinhibido”) y proporciona puntuaciones en los Cinco Grandes Rasgos de la Personalidad (Extraversión, Amabilidad, Responsabilidad, Neuroticismo y Apertura). Estos rasgos psicológicos están reportados como puntuaciones T estandarizadas (media de 50 y desviación estándar de 10).
  • Datos de Búsqueda en Google: Mide la popularidad relativa de 12 términos de búsqueda (como instagram, entrepreneur, volunteering, museum) seleccionados por su relación aproximada con los rasgos de personalidad. Se reportan como puntuaciones Z estandarizadas (media de 0 y desviación estándar de 1).

Objetivo

El objetivo principal de esta primera sección es aplicar algoritmos de clustering para descubrir patrones y similitudes latentes entre los estados. Al agrupar los datos utilizando las variables numéricas continuas (rasgos de personalidad y comportamiento de búsqueda en Google), buscaremos identificar si existen “regiones psicosociales” que compartan perfiles de comportamiento similares, independientemente de sus fronteras geográficas o políticas tradicionales.

Preparación de los Datos

En esta sección cargaremos los datos y prepararemos las variables continuas para el algoritmo de agrupamiento. Es crucial recordar que las variables de personalidad y de Google Search ya se encuentran en distintas escalas (puntuaciones T y Z, respectivamente), por lo que el preprocesamiento garantizará que todas tengan el mismo peso durante el cálculo de distancias.

# Cargar paquetes (instala pacman si no lo tienes)
if (!require("pacman")) install.packages("pacman")
pacman::p_load(cluster, factoextra, corrplot, rio, tidyverse)

# Importar y limpiar datos
df <- import("StateData.xlsx") %>% as_tibble()

# Seleccionar variables de Google Search y preparar matriz
# Seleccionar variables de Google Search y Personalidad
df_num <- df %>%
  select(
    state_code, 
    extraversion, agreeableness, conscientiousness, neuroticism, openness, # Ajusta los nombres exactos
    instagram:modernDance
  ) %>%
  column_to_rownames("state_code") %>% 
  na.omit()                           # Eliminar valores faltantes si los hay

# Estandarizar variables (Media 0, Varianza 1)
# Aunque las variables de Google ya son Z-scores, scale() asegura que 
# cualquier variable adicional (como las psicológicas T-scores) comparta la misma métrica.
Z <- scale(df_num)

Análisis Exploratorio: Matriz de Correlaciones

Antes de agrupar, visualizamos cómo se relacionan los términos de búsqueda entre sí.

R <- cor(Z)
corrplot(R, method = "color", type = "upper", 
         tl.col = "black", tl.cex = 0.8, 
         addCoef.col = "black", number.cex = 0.6,
         title = "Correlación de Términos de Búsqueda", mar = c(0,0,2,0))

Evaluación del Número Óptimo de Conglomerados (K)

Utilizamos el método del Codo (WSS) y el estadístico Silhouette para determinar el número ideal de clústeres.

# Método del codo
fviz_nbclust(Z, FUNcluster = hcut, method = "wss") +
  labs(title = "Método del Codo (WSS)", x = "Número de clústeres K")
# Método Silhouette
fviz_nbclust(Z, FUNcluster = hcut, method = "silhouette") +
  labs(title = "Método Silhouette", x = "Número de clústeres K")

Clustering Jerárquico (Método de Ward)

El método de Ward minimiza la varianza dentro de cada clúster, creando grupos más compactos y balanceados en comparación con otros métodos de enlace.

# Calcular distancia euclidiana
D_euclidiana <- dist(Z, method = "euclidean")

# Aplicar clustering jerárquico con método de Ward
hc_ward <- hclust(D_euclidiana, method = "ward.D2"); hc_ward

Call:
hclust(d = D_euclidiana, method = "ward.D2")

Cluster method   : ward.D2 
Distance         : euclidean 
Number of objects: 48 
# Extraer el K óptimo basado en la métrica Silhouette
sil_info <- fviz_nbclust(Z, FUNcluster = hcut, method = "silhouette")$data
K_optimo <- as.numeric(as.character(sil_info$clusters[which.max(sil_info$y)]))

# Visualizar dendrograma con el K óptimo
fviz_dend(hc_ward, k = K_optimo, 
          rect = TRUE, show_labels = TRUE, cex = 0.5,
          main = paste("Dendrograma de Estados - Método de Ward ( K =", K_optimo, ")"),
          ylab = "Distancia", xlab = "Estados")

Algoritmo K-Means y Visualización Bidimensional

Aplicamos K-Means, un método de partición, utilizando el mismo número óptimo de grupos. Luego, reducimos las dimensiones de nuestros datos (mediante Componentes Principales internamente) para visualizar los grupos en un plano 2D.

library(plotly)

set.seed(123)
# Aseguramos K=2 que es tu número óptimo
km <- kmeans(Z, centers = 2, nstart = 50)

# 1. Generamos el gráfico base limpio (sin letras sobrepuestas)
p <- fviz_cluster(km, data = Z, 
                  geom = "point", 
                  ellipse.type = "convex",
                  main = "Agrupamiento K-Means Interactivo (K = 2)")

# 2. Extraemos los datos internos del gráfico para crear nuestra tabla de textos
datos_tooltip <- p$data
# Cruzamos con tu 'df' original para sacar el nombre completo
datos_tooltip$EstadoCompleto <- df$State[match(datos_tooltip$name, df$state_code)]

# 3. Creamos la variable de texto exacto
datos_tooltip$TextoInteractivo <- paste(
  "<b>Estado:</b>", datos_tooltip$EstadoCompleto, 
  "<br><b>Código:</b>", datos_tooltip$name, 
  "<br><b>Clúster:</b>", datos_tooltip$cluster
)

# 4. EL TRUCO: Añadimos una capa de puntos con alpha = 0 (totalmente transparentes).
# Esta capa tiene sus propios datos exclusivos, por lo que no rompe las elipses.
p_final <- p + geom_point(data = datos_tooltip, 
                          aes(x = x, y = y, text = TextoInteractivo), 
                          alpha = 0)

# 5. Volvemos interactivo el gráfico, apuntando únicamente a nuestro texto
ggplotly(p_final, tooltip = "text") %>%
  layout(hoverlabel = list(bgcolor = "white")) # Hace que el cuadro emergente se vea más limpio

Caracterización de los Conglomerados (Profiling)

Para entender qué significa cada grupo, graficamos los centroides (medias estandarizadas) de cada clúster. Un valor por encima de 0 indica un interés superior a la media nacional en ese término de búsqueda; un valor por debajo indica menor interés.

# Heatmap de los centroides
heatmap(as.matrix(km$centers), 
        Rowv = NA, Colv = NA, scale = "none",
        col = colorRampPalette(c("blue", "white", "red"))(50),
        margins = c(10, 5),
        xlab = "Términos de Búsqueda (Google)", 
        ylab = "Clúster (K-Means)",
        main = "Perfil Psicosocial por Clúster")

Clúster 1: Perfil “Temperamental, Abierto y Cívico-Cultural”

  • Rasgos Psicológicos: Presentan niveles extremadamente bajos en Responsabilidad (conscientiousness) y Amabilidad (agreeableness), marcados con un azul intenso. En contraste, muestran niveles altos (rojo) de Neuroticismo y Apertura a la experiencia (openness).
  • Comportamiento Digital (Google): Exhiben un interés superior a la media en temas de privacidad y regulación tecnológica (gdpr, privacy), cultura y compromiso cívico (museum, volunteering, modernDance) e interacción rápida (retweet). Por el contrario, muestran desinterés (azul) en facebook y manualidades tradicionales (scrapbook).

Clúster 2: Perfil “Convencional, Amable y Tradicional”

  • Rasgos Psicológicos: Es la imagen especular (el reverso) del primer grupo, aunque con variaciones más suaves (tonos pastel). Tienen puntuaciones positivas (rosado/rojo claro) en Responsabilidad, Amabilidad y Extraversión, y puntuaciones por debajo de la media en Neuroticismo y Apertura.
  • Comportamiento Digital (Google): Reflejan un patrón más tradicional. Dominan las búsquedas de facebook y scrapbook, mostrando escaso interés en privacidad de datos, museos, danza moderna o voluntariado (dominado por tonos azules).

El algoritmo K-Means con \(K=2\) divide a los estados de EE. UU. en dos perfiles psicosociales antagónicos. El Clúster 1 agrupa a poblaciones con menor convencionalismo social (baja responsabilidad/amabilidad) pero alta inquietud intelectual, cultural y tecnológica. El Clúster 2 captura el perfil tradicional del “Midwest” o sur estadounidense: sociable, estructurado, amable y orientado a intereses clásicos.

centros <- km$centers
matplot(t(centros), type = "b", pch = 19, lty = 1, lwd = 2,
        xaxt = "n", xlab = "Términos de Búsqueda", ylab = "Z-Score Medio",
        main = "Comportamiento Promedio por Clúster")
axis(1, at = 1:ncol(centros), labels = colnames(centros), las = 2, cex.axis = 0.8)
abline(h = 0, lty = 2, col = "gray")
legend("topright", legend = paste("Clúster", 1:nrow(centros)), 
       col = 1:nrow(centros), pch = 19, lty = 1, bty = "n")

El gráfico demuestra visualmente que los estados se dividen en dos perfiles completamente opuestos, funcionando casi como un espejo. El Clúster 1 (línea negra) agrupa a los estados “inconformistas y curiosos”: tienen puntuaciones muy bajas en rasgos clásicos como la responsabilidad o amabilidad, pero destacan enormemente por su inquietud cultural, tecnológica y progresista, dominando las búsquedas sobre museos, privacidad de datos (GDPR) y danza moderna.

Por el contrario, el Clúster 2 (línea rosa) representa el perfil “tradicional y estructurado”. Sus habitantes puntúan alto en responsabilidad y amabilidad, mostrando una clara preferencia por intereses más familiares y sociales convencionales, como el uso de Facebook o las manualidades (scrapbook). En resumen, el gráfico ilustra el contraste perfecto entre una población con apertura vanguardista y otra orientada a un conservadurismo social más afable.

Discriminant Analisys

1. Introducción

El presente documento desarrolla un Análisis Discriminante Lineal (LDA) sobre el mercado de suelo urbano en Lima Metropolitana utilizando una base geoespacial de \(N=3,114\) avisos de venta. El objetivo es discriminar geográficamente el valor del suelo, transformando variables altamente asimétricas y verificando si la clasificación por grandes zonas urbanas es superior a la categorización administrativa por distritos.

2. Preparación de Datos y Variables Derivadas

Para este análisis, el valor de suelo y el área presentan una asimetría positiva pronunciada, por lo que se construyen variables derivadas logarítmicas, además de calcular la distancia euclidiana al centro de Lima reproyectando las coordenadas al sistema UTM.

# Cargar librerías necesarias
library(sf)
library(dplyr)
library(MASS)
library(ggplot2)
library(caret)
library(corrplot)
library(biotools)

# 1. Cargar la base de datos
ruta_gpkg <- "lima_metropolitana_ofertas.gpkg"
puntos <- st_read(ruta_gpkg, layer = "lima_metropolitana_ofertas", quiet = TRUE)

# 2. Transformación espacial y creación de variables derivadas
centro_utm <- st_sfc(st_point(c(-77.0428, -12.0464)), crs = 4326) %>% 
  st_transform(crs = 32718)

datos <- puntos %>%
  st_transform(crs = 32718) %>%
  mutate(
    dist_centro_km = as.numeric(st_distance(., centro_utm)) / 1000,
    log_valor = log10(valor_soles_m2),
    log_area = log10(area_m2),
    distrito = enc2utf8(as.character(distrito))
  ) %>%
  st_drop_geometry()

# 3. Mapeo de Distritos a Zonas de Lima
datos <- datos %>%
  mutate(zona_lima = case_when(
    distrito %in% c("Carabayllo", "Comas", "Los Olivos", "Puente Piedra", 
                    "San Martin de Porres", "Independencia", "Ancón", "Santa Rosa") ~ "Norte",
    distrito %in% c("SJL", "Ate", "Santa Anita", "El Agustino", "Chaclacayo", 
                    "Chosica (Lurigancho)", "La Molina", "Cieneguilla") ~ "Este",
    distrito %in% c("Villa El Salvador", "VMT", "SJM", "Chorrillos", "Lurín", 
                    "Pachacamac", "Punta Negra", "Punta Hermosa", "San Bartolo", 
                    "Santa Maria Del Mar", "Pucusana") ~ "Sur",
    distrito %in% c("San Isidro", "Miraflores", "San Borja", "Surquillo", 
                    "San Miguel", "Pueblo Libre", "Magdalena Del Mar", "Jesús María", 
                    "Lince", "San Luis", "La Victoria Lim", "Lima Cercado", 
                    "Breña", "Rímac", "Barranco", "Santiago De Surco") ~ "Centro",
    TRUE ~ NA_character_
  )) %>% 
  filter(!is.na(zona_lima)) %>%
  mutate(zona_lima = factor(zona_lima))

El análisis original planteaba un LDA por distrito, pero varios de los 43 distritos presentan menos de 20 observaciones, lo que viola el supuesto de tamaño muestral por grupo. Por ello, se agrupan en cuatro zonas geográficas balanceadas (Centro, Sur, Norte, Este).

3. Modelo A: LDA por Zona de Lima

Ajustamos el Modelo A utilizando las variables estandarizadas para evitar sesgos por la escala de medición

# Variables para el LDA
vars_lda <- c("log_valor", "log_area", "dist_centro_km")

# Crear dataset específico y estandarizar variables
datosA <- datos %>% dplyr::select(all_of(vars_lda), zona_lima)
datosA[, vars_lda] <- scale(datosA[, vars_lda])

# Ajuste del modelo LDA
modelo_lda <- lda(zona_lima ~ log_valor + log_area + dist_centro_km, data = datosA)
modelo_lda
Call:
lda(zona_lima ~ log_valor + log_area + dist_centro_km, data = datosA)

Prior probabilities of groups:
   Centro      Este     Norte       Sur 
0.2870906 0.2035967 0.2517662 0.2575466 

Group means:
        log_valor    log_area dist_centro_km
Centro  1.0068021  0.04740055    -0.87306451
Este   -0.2365501  0.24022337    -0.05761479
Norte  -0.4696221 -0.32452213     0.08960644
Sur    -0.4762153  0.07449830     0.93116708

Coefficients of linear discriminants:
                      LD1        LD2        LD3
log_valor      -0.7458555 -1.2033427  0.3548495
log_area       -0.1067454 -0.4540127 -0.9304334
dist_centro_km  0.8383191 -1.1791722  0.2968683

Proportion of trace:
   LD1    LD2    LD3 
0.8182 0.1583 0.0235 

Verificación de Supuestos: Prueba M de Box

Evaluamos la homogeneidad de matrices de varianzas-covarianzas utilizando la prueba M de Box.

boxM(datosA[, vars_lda], datosA$zona_lima)

    Box's M-test for Homogeneity of Covariance Matrices

data:  datosA[, vars_lda]
Chi-Sq (approx.) = 1971.4, df = 18, p-value < 2.2e-16

La prueba rechaza fuertemente la hipótesis nula de homogeneidad de covarianzas (\(p < 2.2 \times 10^{-16}\)). Sin embargo, con un tamaño muestral grande (\(n=3,114\)), la prueba de Box es altamente sensible y tiende a rechazar \(H_0\) ante variaciones menores. El LDA se asume entonces como una herramienta predictiva.

4. Evaluación del Modelo y Visualización

Evaluamos la precisión global del modelo mediante la técnica de validación cruzada Leave-One-Out (LOOCV).

# LOOCV
modelo_cv <- lda(zona_lima ~ log_valor + log_area + dist_centro_km, data = datosA, CV = TRUE)
tab_cv <- table(Observado = datosA$zona_lima, Predicho = modelo_cv$class)
precision_cv <- sum(diag(tab_cv)) / sum(tab_cv)

cat("Precisión del Modelo A (LOOCV):", round(precision_cv * 100, 1), "%\n")
Precisión del Modelo A (LOOCV): 58.2 %
tab_cv
         Predicho
Observado Centro Este Norte Sur
   Centro    807   31    56   0
   Este      196  125   193 120
   Norte     129   63   467 125
   Sur        59   98   233 412

El modelo logra una precisión LOOCV del 58.1%. Destaca la zona Centro con una sensibilidad muy alta (90.3%), lo que evidencia que constituye un perfil económico genuinamente distintivo (ubicación cercana y precios altos).

Espacio Discriminante

Visualizamos cómo el LDA separa los grupos proyectando los datos en las dos primeras dimensiones discriminantes (LD1 y LD2).

proy <- predict(modelo_lda)
df_plot <- data.frame(proy$x, zona = datosA$zona_lima)

ggplot(df_plot, aes(x = LD1, y = LD2, color = zona)) +
  geom_point(alpha = 0.4, size = 1.2) +
  stat_ellipse(level = 0.68, linewidth = 1) +
  labs(title = "Espacio discriminante: zonas de Lima",
       subtitle = "Elipses de concentración al 68%",
       x = "LD1 (81.8% varianza)", y = "LD2 (15.8% varianza)") +
  theme_minimal()

El primer eje (LD1) concentra el 81.8% de la varianza discriminante total y opone la distancia al centro contra el valor del suelo, capturando la lógica del modelo económico de renta de la tierra (terrenos caros cerca del centro vs. baratos y alejados).

Mapa: Observado vs. Predicho sobre el Territorio

El diagnóstico visual de la clasificación nos permite entender dónde está fallando el modelo espacialmente.

# Recuperar la geometría original y alinearla con las zonas válidas
puntos_mapeo <- puntos %>%
  mutate(zona_lima = case_when(
    distrito %in% c("Carabayllo", "Comas", "Los Olivos", "Puente Piedra", 
                    "San Martin de Porres", "Independencia", "Ancón", "Santa Rosa") ~ "Norte",
    distrito %in% c("SJL", "Ate", "Santa Anita", "El Agustino", "Chaclacayo", 
                    "Chosica (Lurigancho)", "La Molina", "Cieneguilla") ~ "Este",
    distrito %in% c("Villa El Salvador", "VMT", "SJM", "Chorrillos", "Lurín", 
                    "Pachacamac", "Punta Negra", "Punta Hermosa", "San Bartolo", 
                    "Santa Maria Del Mar", "Pucusana") ~ "Sur",
    distrito %in% c("San Isidro", "Miraflores", "San Borja", "Surquillo", 
                    "San Miguel", "Pueblo Libre", "Magdalena Del Mar", "Jesús María", 
                    "Lince", "San Luis", "La Victoria Lim", "Lima Cercado", 
                    "Breña", "Rímac", "Barranco", "Santiago De Surco") ~ "Centro",
    TRUE ~ NA_character_
  )) %>%
  filter(!is.na(zona_lima))

# Añadir las predicciones del modelo LDA y clasificar el acierto
puntos_mapeo$zona_pred <- predict(modelo_lda)$class
puntos_mapeo$acierto <- ifelse(puntos_mapeo$zona_lima == puntos_mapeo$zona_pred, "Correcto", "Error")

# Graficar en el territorio real
ggplot(puntos_mapeo) +
  geom_sf(aes(color = acierto), alpha = 0.6, size = 0.8) +
  scale_color_manual(values = c("Correcto" = "grey70", "Error" = "red")) +
  labs(
    title = "Aciertos y errores de clasificación del LDA",
    subtitle = "Distribución territorial de las predicciones",
    color = "Clasificación"
  ) +
  theme_minimal()

Modelo B: LDA por Distrito Bruto (Comparación)

A modo de contraste, ajustamos un segundo LDA sin agrupación zonal, utilizando únicamente los distritos con \(n>=50\).

5. Evaluación de Desempeño y Matriz de Confusión

n_por_distrito <- sort(table(datos$distrito), decreasing = TRUE)
distritos_ok <- names(n_por_distrito[n_por_distrito >= 50])

datosB <- datos %>% 
  filter(distrito %in% distritos_ok) %>%
  dplyr::select(all_of(vars_lda), distrito) %>%
  mutate(distrito = factor(distrito))

datosB[, vars_lda] <- scale(datosB[, vars_lda])

modelo_B_cv <- lda(distrito ~ log_valor + log_area + dist_centro_km, data = datosB, CV = TRUE)
precision_B_cv <- mean(modelo_B_cv$class == datosB$distrito)

cat("Precisión Modelo B (Distritos - LOOCV):", round(precision_B_cv * 100, 1), "%\n")
Precisión Modelo B (Distritos - LOOCV): 43.2 %

6. Cuadro Comparativo de Desempeño (Síntesis)

Compararemos el rendimiento de ambos modelos bajo la técnica de validación cruzada Leave-One-Out (LOOCV).

# Generar tabla resumida de comparación de precisión
resumen_comparativo <- data.frame(
  Modelo = c("Modelo A (Zonas de Lima)", paste("Modelo B (Distritos n >=", 50, ")")),
  Grupos = c(length(unique(datosA$zona_lima)), length(distritos_ok)),
  Precision_LOOCV = c(
    paste0(round(precision_cv * 100, 1), "%"),
    paste0(round(precision_B_cv * 100, 1), "%")
  )
)

knitr::kable(
  resumen_comparativo, 
  col.names = c("Modelo", "N° Grupos", "Precisión LOOCV"),
  caption = "Comparación de precisión Leave-One-Out según nivel de agregación territorial"
)
Comparación de precisión Leave-One-Out según nivel de agregación territorial
Modelo N° Grupos Precisión LOOCV
Modelo A (Zonas de Lima) 4 58.2%
Modelo B (Distritos n >= 50 ) 24 43.2%

Con las mismas tres variables predictoras, reducir el nivel de agregación de zona a distrito individual empeora la clasificación en 14.9 puntos porcentuales. Esta caída es la evidencia empírica de que distrito es una categoría administrativa demasiado fina para que el precio, el área y la distancia al centro la discriminen de forma estable: el mercado de suelo de Lima se organiza económicamente por zonas amplias, no por los límites político-administrativos de cada distrito.

Conjoint Analysis

Analizaremos los datos de una encuesta en la que se pidió a 15 consumidores que calificaran diez helados. Cada helado tenía un “perfil” diferente, es decir, una combinación distinta de niveles de cuatro atributos:

  • sabor (frambuesa, chocolate, fresa, mango, vainilla),
  • envase (wafle casero, cono, tarrina),
  • bajo en grasa (o no) y
  • orgánico (o no).

Los 15 participantes calificaron los diez perfiles con una puntuación entre 1 y 10.

1. Datos icecream

library(readxl)
icecream <- read_excel("icecream.xlsx")
head(icecream)
# A tibble: 6 × 20
  Profile   Flavor     Packaging     Light Organic `Individual 1` `Individual 2`
  <chr>     <chr>      <chr>         <chr> <chr>            <dbl>          <dbl>
1 Profile 1 Raspberry  Homemade waf… Low … Organic              8              7
2 Profile 2 Chocolate  Homemade waf… Low … Organic              9             10
3 Profile 3 Strawberry Homemade waf… Low … Organic              9              9
4 Profile 4 Vanilla    Homemade waf… Low … Organic              7              7
5 Profile 5 Mango      Homemade waf… Low … Organic              9              8
6 Profile 6 Raspberry  Cone          Low … Organic              4              7
# ℹ 13 more variables: `Individual 3` <dbl>, `Individual 4` <dbl>,
#   `Individual 5` <dbl>, `Individual 6` <dbl>, `Individual 7` <dbl>,
#   `Individual 8` <dbl>, `Individual 9` <dbl>, `Individual 10` <dbl>,
#   `Individual 11` <dbl>, `Individual 12` <dbl>, `Individual 13` <dbl>,
#   `Individual 14` <dbl>, `Individual 15` <dbl>

Para poder aplicar los modelos estadísticos del Análisis Conjunto (como la regresión lineal), es indispensable reestructurar los datos. Los paquetes estadísticos en R no pueden procesar calificaciones si cada individuo representa una columna distinta.

icecream_long <- icecream %>% 
  pivot_longer(
    cols = starts_with("Individual"), 
    names_to = "respondent", 
    values_to = "rating"
  ) %>%
  rename(profile = 1) %>% # ¡TRUCO! Renombra la 1ra columna a "profile", se llame como se llame
  mutate(
    profile = factor(profile), 
    respondent = factor(respondent),  
    Flavor = factor(Flavor), 
    Packaging = factor(Packaging), 
    Light = factor(Light), 
    Organic = factor(Organic)
  )

# Ver el resultado para confirmar que todo salió bien
head(icecream_long)
# A tibble: 6 × 7
  profile   Flavor    Packaging       Light   Organic respondent   rating
  <fct>     <fct>     <fct>           <fct>   <fct>   <fct>         <dbl>
1 Profile 1 Raspberry Homemade waffle Low fat Organic Individual 1      8
2 Profile 1 Raspberry Homemade waffle Low fat Organic Individual 2      7
3 Profile 1 Raspberry Homemade waffle Low fat Organic Individual 3      9
4 Profile 1 Raspberry Homemade waffle Low fat Organic Individual 4      7
5 Profile 1 Raspberry Homemade waffle Low fat Organic Individual 5      8
6 Profile 1 Raspberry Homemade waffle Low fat Organic Individual 6      7
  • Cambio estructural (Wide a Long): Se consolidaron las respuestas de todos los participantes utilizando la función pivot_longer. Pasamos de una estructura orientada a la presentación (un perfil por fila y múltiples columnas de encuestados) a una estructura tabular estandarizada donde cada fila representa una única observación (la interacción entre un consumidor y un perfil específico).

  • Dimensionalidad: Nuestro conjunto de datos original contenía 60 filas (perfiles) y 15 columnas de calificaciones. Al apilarlos, el nuevo dataset icecream_long ahora consta de 900 filas (60 perfiles 15 individuos), consolidando todas las puntuaciones en una sola columna objetivo llamada rating.

  • Factorización: Se convirtieron todos los atributos del helado (sabor, envase, etc.) y los identificadores a variables de tipo factor (variables categóricas). Este paso es crítico porque instruye a R para que internamente trate estos atributos como variables dummy (0 y 1) durante el modelado, permitiendo así el cálculo matemático de las utilidades parciales o part-worths.

2. Diseño de Experimentos

Antes de salir a encuestar a los consumidores, debemos definir exactamente qué versiones del producto (perfiles) les vamos a mostrar. Si observamos los atributos de nuestro helado, tenemos:

  • Sabor: 5 niveles (frambuesa, chocolate, fresa, mango, vainilla).
  • Envase: 3 niveles (waffle casero, cono, tarrina).
  • Bajo en grasa: 2 niveles (sí, no).
  • Orgánico: 2 niveles (sí, no).

El problema (Diseño Factorial Completo): Si multiplicamos todas estas opciones (\(5\times3\times2\times2\) ), descubrimos que existen 60 combinaciones posibles de helados. Aunque evaluar los 60 nos daría datos perfectos, en la vida real esto es inviable: es demasiado costoso de fabricar y los participantes sufrirían de fatiga al tener que calificar 60 helados seguidos, lo que arruinaría la encuesta.

La solución (Diseño Fraccional): Para resolver esto, se utiliza una técnica llamada Diseño de Experimentos. En lugar de usar las 60 combinaciones, seleccionamos matemáticamente un subconjunto más pequeño (por ejemplo, solo 10 helados). Si este subconjunto está bien diseñado, nos permitirá obtener estimaciones precisas de qué atributos son los más importantes, sin agotar al consumidor.

3. Diseño Fraccional en R

Para utilizar la función doe (Design of Experiments), debemos definir los atributos y sus niveles separándolos con punto y coma (;). El primer elemento siempre es el nombre del atributo, seguido de sus posibles variaciones.

library(radiant)
# 1. Definir cada atributo y sus niveles en formato de texto
attribute1 <- "Flavor; Raspberry; Chocolate; Strawberry; Mango; Vanilla"
attribute2 <- "Package; Homemade waffle; Cone; Pint"
attribute3 <- "Light; Low fat; No low fat"
attribute4 <- "Organic; Organic; Not organic"

# 2. Combinar todos los atributos en una sola lista (vector)
attributes <- c(attribute1, attribute2, attribute3, attribute4)

# 3. Generar el diseño experimental usando la función 'doe'
# Nota: asegúrate de tener instalada y cargada la librería radiant.design
# library(radiant.design)
summary(doe(attributes, seed = 123))
Experimental design
# trials for partial factorial: 60 
# trials for full factorial   : 60 
Random seed                   : 123 

Attributes and levels:
Flavor: Raspberry, Chocolate, Strawberry, Mango, Vanilla 
Package: Homemade_waffle, Cone, Pint 
Light: Low_fat, No_low_fat 
Organic: Organic, Not_organic 

Design efficiency:
 Trials D-efficiency Balanced
      9        0.105    FALSE
     10        0.389    FALSE
     11        0.411    FALSE
     12        0.614    FALSE
     13        0.542    FALSE
     14        0.479    FALSE
     15        0.762    FALSE
     16        0.738    FALSE
     17        0.748    FALSE
     18        0.756    FALSE
     19        0.644    FALSE
     20        0.895    FALSE
     21        0.848    FALSE
     22        0.833    FALSE
     23        0.790    FALSE
     24        0.827    FALSE
     25        0.787    FALSE
     26        0.768    FALSE
     27        0.759    FALSE
     28        0.736    FALSE
     29        0.702    FALSE
     30        0.984     TRUE
     31        0.952    FALSE
     32        0.933    FALSE
     33        0.928    FALSE
     34        0.900    FALSE
     35        0.871    FALSE
     36        0.893    FALSE
     37        0.866    FALSE
     38        0.843    FALSE
     39        0.836    FALSE
     40        0.922    FALSE
     41        0.899    FALSE
     42        0.904    FALSE
     43        0.882    FALSE
     44        0.861    FALSE
     45        0.949    FALSE
     46        0.919    FALSE
     47        0.912    FALSE
     48        0.911    FALSE
     49        0.891    FALSE
     50        0.959    FALSE
     51        0.939    FALSE
     52        0.944    FALSE
     53        0.925    FALSE
     54        0.924    FALSE
     55        0.906    FALSE
     56        0.902    FALSE
     57        0.884    FALSE
     58        0.872    FALSE
     59        0.855    FALSE
     60        1.000     TRUE

Partial factorial design correlations:
** Note: Variables are assumed to be ordinal **
        Flavor Package Light Organic
Flavor       1       0     0       0
Package      0       1     0       0
Light        0       0     1       0
Organic      0       0     0       1

Partial factorial design:
 trial     Flavor         Package      Light     Organic
     1  Raspberry Homemade_waffle    Low_fat     Organic
     2  Raspberry Homemade_waffle    Low_fat Not_organic
     3  Raspberry Homemade_waffle No_low_fat     Organic
     4  Raspberry Homemade_waffle No_low_fat Not_organic
     5  Raspberry            Cone    Low_fat     Organic
     6  Raspberry            Cone    Low_fat Not_organic
     7  Raspberry            Cone No_low_fat     Organic
     8  Raspberry            Cone No_low_fat Not_organic
     9  Raspberry            Pint    Low_fat     Organic
    10  Raspberry            Pint    Low_fat Not_organic
    11  Raspberry            Pint No_low_fat     Organic
    12  Raspberry            Pint No_low_fat Not_organic
    13  Chocolate Homemade_waffle    Low_fat     Organic
    14  Chocolate Homemade_waffle    Low_fat Not_organic
    15  Chocolate Homemade_waffle No_low_fat     Organic
    16  Chocolate Homemade_waffle No_low_fat Not_organic
    17  Chocolate            Cone    Low_fat     Organic
    18  Chocolate            Cone    Low_fat Not_organic
    19  Chocolate            Cone No_low_fat     Organic
    20  Chocolate            Cone No_low_fat Not_organic
    21  Chocolate            Pint    Low_fat     Organic
    22  Chocolate            Pint    Low_fat Not_organic
    23  Chocolate            Pint No_low_fat     Organic
    24  Chocolate            Pint No_low_fat Not_organic
    25 Strawberry Homemade_waffle    Low_fat     Organic
    26 Strawberry Homemade_waffle    Low_fat Not_organic
    27 Strawberry Homemade_waffle No_low_fat     Organic
    28 Strawberry Homemade_waffle No_low_fat Not_organic
    29 Strawberry            Cone    Low_fat     Organic
    30 Strawberry            Cone    Low_fat Not_organic
    31 Strawberry            Cone No_low_fat     Organic
    32 Strawberry            Cone No_low_fat Not_organic
    33 Strawberry            Pint    Low_fat     Organic
    34 Strawberry            Pint    Low_fat Not_organic
    35 Strawberry            Pint No_low_fat     Organic
    36 Strawberry            Pint No_low_fat Not_organic
    37      Mango Homemade_waffle    Low_fat     Organic
    38      Mango Homemade_waffle    Low_fat Not_organic
    39      Mango Homemade_waffle No_low_fat     Organic
    40      Mango Homemade_waffle No_low_fat Not_organic
    41      Mango            Cone    Low_fat     Organic
    42      Mango            Cone    Low_fat Not_organic
    43      Mango            Cone No_low_fat     Organic
    44      Mango            Cone No_low_fat Not_organic
    45      Mango            Pint    Low_fat     Organic
    46      Mango            Pint    Low_fat Not_organic
    47      Mango            Pint No_low_fat     Organic
    48      Mango            Pint No_low_fat Not_organic
    49    Vanilla Homemade_waffle    Low_fat     Organic
    50    Vanilla Homemade_waffle    Low_fat Not_organic
    51    Vanilla Homemade_waffle No_low_fat     Organic
    52    Vanilla Homemade_waffle No_low_fat Not_organic
    53    Vanilla            Cone    Low_fat     Organic
    54    Vanilla            Cone    Low_fat Not_organic
    55    Vanilla            Cone No_low_fat     Organic
    56    Vanilla            Cone No_low_fat Not_organic
    57    Vanilla            Pint    Low_fat     Organic
    58    Vanilla            Pint    Low_fat Not_organic
    59    Vanilla            Pint No_low_fat     Organic
    60    Vanilla            Pint No_low_fat Not_organic

Estimable effects from partial factorial design:

  Flavor|Chocolate
  Flavor|Strawberry
  Flavor|Mango
  Flavor|Vanilla
  Package|Cone
  Package|Pint
  Light|No_low_fat
  Organic|Not_organic
  Flavor|Chocolate:Package|Cone
  Flavor|Strawberry:Package|Cone
  Flavor|Mango:Package|Cone
  Flavor|Vanilla:Package|Cone
  Flavor|Chocolate:Package|Pint
  Flavor|Strawberry:Package|Pint
  Flavor|Mango:Package|Pint
  Flavor|Vanilla:Package|Pint
  Flavor|Chocolate:Light|No_low_fat
  Flavor|Strawberry:Light|No_low_fat
  Flavor|Mango:Light|No_low_fat
  Flavor|Vanilla:Light|No_low_fat
  Package|Cone:Light|No_low_fat
  Package|Pint:Light|No_low_fat
  Flavor|Chocolate:Organic|Not_organic
  Flavor|Strawberry:Organic|Not_organic
  Flavor|Mango:Organic|Not_organic
  Flavor|Vanilla:Organic|Not_organic
  Package|Cone:Organic|Not_organic
  Package|Pint:Organic|Not_organic
  Light|No_low_fat:Organic|Not_organic
  Flavor|Chocolate:Package|Cone:Light|No_low_fat
  Flavor|Strawberry:Package|Cone:Light|No_low_fat
  Flavor|Mango:Package|Cone:Light|No_low_fat
  Flavor|Vanilla:Package|Cone:Light|No_low_fat
  Flavor|Chocolate:Package|Pint:Light|No_low_fat
  Flavor|Strawberry:Package|Pint:Light|No_low_fat
  Flavor|Mango:Package|Pint:Light|No_low_fat
  Flavor|Vanilla:Package|Pint:Light|No_low_fat
  Flavor|Chocolate:Package|Cone:Organic|Not_organic
  Flavor|Strawberry:Package|Cone:Organic|Not_organic
  Flavor|Mango:Package|Cone:Organic|Not_organic
  Flavor|Vanilla:Package|Cone:Organic|Not_organic
  Flavor|Chocolate:Package|Pint:Organic|Not_organic
  Flavor|Strawberry:Package|Pint:Organic|Not_organic
  Flavor|Mango:Package|Pint:Organic|Not_organic
  Flavor|Vanilla:Package|Pint:Organic|Not_organic
  Flavor|Chocolate:Light|No_low_fat:Organic|Not_organic
  Flavor|Strawberry:Light|No_low_fat:Organic|Not_organic
  Flavor|Mango:Light|No_low_fat:Organic|Not_organic
  Flavor|Vanilla:Light|No_low_fat:Organic|Not_organic
  Package|Cone:Light|No_low_fat:Organic|Not_organic
  Package|Pint:Light|No_low_fat:Organic|Not_organic
  Flavor|Chocolate:Package|Cone:Light|No_low_fat:Organic|Not_organic
  Flavor|Strawberry:Package|Cone:Light|No_low_fat:Organic|Not_organic
  Flavor|Mango:Package|Cone:Light|No_low_fat:Organic|Not_organic
  Flavor|Vanilla:Package|Cone:Light|No_low_fat:Organic|Not_organic
  Flavor|Chocolate:Package|Pint:Light|No_low_fat:Organic|Not_organic
  Flavor|Strawberry:Package|Pint:Light|No_low_fat:Organic|Not_organic
  Flavor|Mango:Package|Pint:Light|No_low_fat:Organic|Not_organic
  Flavor|Vanilla:Package|Pint:Light|No_low_fat:Organic|Not_organic 

Full factorial design:
 trial     Flavor         Package      Light     Organic
     1  Raspberry Homemade_waffle    Low_fat     Organic
     2  Raspberry Homemade_waffle    Low_fat Not_organic
     3  Raspberry Homemade_waffle No_low_fat     Organic
     4  Raspberry Homemade_waffle No_low_fat Not_organic
     5  Raspberry            Cone    Low_fat     Organic
     6  Raspberry            Cone    Low_fat Not_organic
     7  Raspberry            Cone No_low_fat     Organic
     8  Raspberry            Cone No_low_fat Not_organic
     9  Raspberry            Pint    Low_fat     Organic
    10  Raspberry            Pint    Low_fat Not_organic
    11  Raspberry            Pint No_low_fat     Organic
    12  Raspberry            Pint No_low_fat Not_organic
    13  Chocolate Homemade_waffle    Low_fat     Organic
    14  Chocolate Homemade_waffle    Low_fat Not_organic
    15  Chocolate Homemade_waffle No_low_fat     Organic
    16  Chocolate Homemade_waffle No_low_fat Not_organic
    17  Chocolate            Cone    Low_fat     Organic
    18  Chocolate            Cone    Low_fat Not_organic
    19  Chocolate            Cone No_low_fat     Organic
    20  Chocolate            Cone No_low_fat Not_organic
    21  Chocolate            Pint    Low_fat     Organic
    22  Chocolate            Pint    Low_fat Not_organic
    23  Chocolate            Pint No_low_fat     Organic
    24  Chocolate            Pint No_low_fat Not_organic
    25 Strawberry Homemade_waffle    Low_fat     Organic
    26 Strawberry Homemade_waffle    Low_fat Not_organic
    27 Strawberry Homemade_waffle No_low_fat     Organic
    28 Strawberry Homemade_waffle No_low_fat Not_organic
    29 Strawberry            Cone    Low_fat     Organic
    30 Strawberry            Cone    Low_fat Not_organic
    31 Strawberry            Cone No_low_fat     Organic
    32 Strawberry            Cone No_low_fat Not_organic
    33 Strawberry            Pint    Low_fat     Organic
    34 Strawberry            Pint    Low_fat Not_organic
    35 Strawberry            Pint No_low_fat     Organic
    36 Strawberry            Pint No_low_fat Not_organic
    37      Mango Homemade_waffle    Low_fat     Organic
    38      Mango Homemade_waffle    Low_fat Not_organic
    39      Mango Homemade_waffle No_low_fat     Organic
    40      Mango Homemade_waffle No_low_fat Not_organic
    41      Mango            Cone    Low_fat     Organic
    42      Mango            Cone    Low_fat Not_organic
    43      Mango            Cone No_low_fat     Organic
    44      Mango            Cone No_low_fat Not_organic
    45      Mango            Pint    Low_fat     Organic
    46      Mango            Pint    Low_fat Not_organic
    47      Mango            Pint No_low_fat     Organic
    48      Mango            Pint No_low_fat Not_organic
    49    Vanilla Homemade_waffle    Low_fat     Organic
    50    Vanilla Homemade_waffle    Low_fat Not_organic
    51    Vanilla Homemade_waffle No_low_fat     Organic
    52    Vanilla Homemade_waffle No_low_fat Not_organic
    53    Vanilla            Cone    Low_fat     Organic
    54    Vanilla            Cone    Low_fat Not_organic
    55    Vanilla            Cone No_low_fat     Organic
    56    Vanilla            Cone No_low_fat Not_organic
    57    Vanilla            Pint    Low_fat     Organic
    58    Vanilla            Pint    Low_fat Not_organic
    59    Vanilla            Pint No_low_fat     Organic
    60    Vanilla            Pint No_low_fat Not_organic

4. Interpretación del Diseño Experimental

Al ejecutar la función doe, R evalúa las combinaciones posibles y nos entrega un reporte técnico sobre la viabilidad del experimento. Los puntos más importantes de este reporte son:

  1. Confirmación de Atributos (Attributes and levels): El modelo reconoce correctamente nuestros 4 atributos y genera un universo total de 60 combinaciones posibles (Full factorial).

  2. Eficiencia del Diseño (Design efficiency): Esta es la parte más útil del reporte. R evalúa qué tan matemáticamente eficiente sería hacer la encuesta con diferentes cantidades de perfiles (desde 9 hasta 60). * D-efficiency: Es una métrica (de 0 a 1) que indica qué tanta información útil retendremos. * Balanced: Indica si cada nivel de un atributo aparece exactamente el mismo número de veces. * Decisión: La tabla nos muestra que si quisiéramos un diseño perfectamente balanceado sin usar los 60 perfiles, el número óptimo sería 30 perfiles (D-efficiency = 0.984, Balanced = TRUE). Sin embargo, en la práctica comercial de los helados, se optó por un subconjunto de 10 perfiles para minimizar drásticamente la fatiga del encuestado, asumiendo un sacrificio en la eficiencia para las interacciones complejas.

  3. Matriz de Correlaciones (Correlations): El reporte nos muestra una tabla donde la diagonal principal tiene números 1 y el resto son puros 0. En estadística, esto es una excelente noticia: significa que nuestro diseño es ortogonal. Es decir, los atributos (como Sabor y Envase) son totalmente independientes entre sí, por lo que no habrá sesgos matemáticos al calcular qué atributo prefiere más el consumidor.

  4. Efectos Estimables (Estimable effects): Finalmente, R lista todas las conclusiones que podrá calcular. Nos indica que este diseño no solo podrá descubrir el efecto principal (ej. cuánto gusta el chocolate frente a la vainilla), sino también las interacciones cruzadas (ej. si a la gente le gusta el chocolate específicamente cuando viene en cono).

5. Diseño Fraccional en R (optimo 30)

Evaluar los 60 perfiles del diseño factorial completo (Full factorial) es inviable para un estudio de mercado real debido a la fatiga del encuestado. Por lo tanto, debemos recurrir a un diseño fraccional.

Para entender el impacto matemático de esta reducción, veamos primero qué sucede si reducimos el experimento a 30 perfiles como se sugiere en los resultados:

summary(doe(attributes, seed = 123, trials = 30))
Experimental design
# trials for partial factorial: 30 
# trials for full factorial   : 60 
Random seed                   : 123 

Attributes and levels:
Flavor: Raspberry, Chocolate, Strawberry, Mango, Vanilla 
Package: Homemade_waffle, Cone, Pint 
Light: Low_fat, No_low_fat 
Organic: Organic, Not_organic 

Design efficiency:
 Trials D-efficiency Balanced
     30        0.984     TRUE

Partial factorial design correlations:
** Note: Variables are assumed to be ordinal **
        Flavor Package  Light Organic
Flavor       1       0  0.000   0.000
Package      0       1  0.000   0.000
Light        0       0  1.000  -0.105
Organic      0       0 -0.105   1.000

Partial factorial design:
 trial     Flavor         Package      Light     Organic
     1  Raspberry Homemade_waffle    Low_fat     Organic
     4  Raspberry Homemade_waffle No_low_fat Not_organic
     6  Raspberry            Cone    Low_fat Not_organic
     7  Raspberry            Cone No_low_fat     Organic
    10  Raspberry            Pint    Low_fat Not_organic
    11  Raspberry            Pint No_low_fat     Organic
    13  Chocolate Homemade_waffle    Low_fat     Organic
    14  Chocolate Homemade_waffle    Low_fat Not_organic
    19  Chocolate            Cone No_low_fat     Organic
    20  Chocolate            Cone No_low_fat Not_organic
    22  Chocolate            Pint    Low_fat Not_organic
    23  Chocolate            Pint No_low_fat     Organic
    26 Strawberry Homemade_waffle    Low_fat Not_organic
    28 Strawberry Homemade_waffle No_low_fat Not_organic
    29 Strawberry            Cone    Low_fat     Organic
    32 Strawberry            Cone No_low_fat Not_organic
    33 Strawberry            Pint    Low_fat     Organic
    35 Strawberry            Pint No_low_fat     Organic
    39      Mango Homemade_waffle No_low_fat     Organic
    40      Mango Homemade_waffle No_low_fat Not_organic
    41      Mango            Cone    Low_fat     Organic
    42      Mango            Cone    Low_fat Not_organic
    46      Mango            Pint    Low_fat Not_organic
    47      Mango            Pint No_low_fat     Organic
    49    Vanilla Homemade_waffle    Low_fat     Organic
    51    Vanilla Homemade_waffle No_low_fat     Organic
    53    Vanilla            Cone    Low_fat     Organic
    56    Vanilla            Cone No_low_fat Not_organic
    58    Vanilla            Pint    Low_fat Not_organic
    60    Vanilla            Pint No_low_fat Not_organic

Estimable effects from partial factorial design:

  Flavor|Chocolate
  Flavor|Strawberry
  Flavor|Mango
  Flavor|Vanilla
  Package|Cone
  Package|Pint
  Light|No_low_fat
  Organic|Not_organic
  Flavor|Chocolate:Package|Cone
  Flavor|Strawberry:Package|Cone
  Flavor|Mango:Package|Cone
  Flavor|Vanilla:Package|Cone
  Flavor|Chocolate:Package|Pint
  Flavor|Strawberry:Package|Pint
  Flavor|Mango:Package|Pint
  Flavor|Vanilla:Package|Pint
  Flavor|Chocolate:Light|No_low_fat
  Flavor|Strawberry:Light|No_low_fat
  Flavor|Mango:Light|No_low_fat
  Flavor|Vanilla:Light|No_low_fat
  Package|Cone:Light|No_low_fat
  Package|Pint:Light|No_low_fat
  Flavor|Chocolate:Organic|Not_organic
  Flavor|Strawberry:Organic|Not_organic
  Flavor|Mango:Organic|Not_organic
  Flavor|Vanilla:Organic|Not_organic
  Package|Cone:Organic|Not_organic
  Light|No_low_fat:Organic|Not_organic
  Flavor|Strawberry:Package|Pint:Light|No_low_fat 

Full factorial design:
 trial     Flavor         Package      Light     Organic
     1  Raspberry Homemade_waffle    Low_fat     Organic
     2  Raspberry Homemade_waffle    Low_fat Not_organic
     3  Raspberry Homemade_waffle No_low_fat     Organic
     4  Raspberry Homemade_waffle No_low_fat Not_organic
     5  Raspberry            Cone    Low_fat     Organic
     6  Raspberry            Cone    Low_fat Not_organic
     7  Raspberry            Cone No_low_fat     Organic
     8  Raspberry            Cone No_low_fat Not_organic
     9  Raspberry            Pint    Low_fat     Organic
    10  Raspberry            Pint    Low_fat Not_organic
    11  Raspberry            Pint No_low_fat     Organic
    12  Raspberry            Pint No_low_fat Not_organic
    13  Chocolate Homemade_waffle    Low_fat     Organic
    14  Chocolate Homemade_waffle    Low_fat Not_organic
    15  Chocolate Homemade_waffle No_low_fat     Organic
    16  Chocolate Homemade_waffle No_low_fat Not_organic
    17  Chocolate            Cone    Low_fat     Organic
    18  Chocolate            Cone    Low_fat Not_organic
    19  Chocolate            Cone No_low_fat     Organic
    20  Chocolate            Cone No_low_fat Not_organic
    21  Chocolate            Pint    Low_fat     Organic
    22  Chocolate            Pint    Low_fat Not_organic
    23  Chocolate            Pint No_low_fat     Organic
    24  Chocolate            Pint No_low_fat Not_organic
    25 Strawberry Homemade_waffle    Low_fat     Organic
    26 Strawberry Homemade_waffle    Low_fat Not_organic
    27 Strawberry Homemade_waffle No_low_fat     Organic
    28 Strawberry Homemade_waffle No_low_fat Not_organic
    29 Strawberry            Cone    Low_fat     Organic
    30 Strawberry            Cone    Low_fat Not_organic
    31 Strawberry            Cone No_low_fat     Organic
    32 Strawberry            Cone No_low_fat Not_organic
    33 Strawberry            Pint    Low_fat     Organic
    34 Strawberry            Pint    Low_fat Not_organic
    35 Strawberry            Pint No_low_fat     Organic
    36 Strawberry            Pint No_low_fat Not_organic
    37      Mango Homemade_waffle    Low_fat     Organic
    38      Mango Homemade_waffle    Low_fat Not_organic
    39      Mango Homemade_waffle No_low_fat     Organic
    40      Mango Homemade_waffle No_low_fat Not_organic
    41      Mango            Cone    Low_fat     Organic
    42      Mango            Cone    Low_fat Not_organic
    43      Mango            Cone No_low_fat     Organic
    44      Mango            Cone No_low_fat Not_organic
    45      Mango            Pint    Low_fat     Organic
    46      Mango            Pint    Low_fat Not_organic
    47      Mango            Pint No_low_fat     Organic
    48      Mango            Pint No_low_fat Not_organic
    49    Vanilla Homemade_waffle    Low_fat     Organic
    50    Vanilla Homemade_waffle    Low_fat Not_organic
    51    Vanilla Homemade_waffle No_low_fat     Organic
    52    Vanilla Homemade_waffle No_low_fat Not_organic
    53    Vanilla            Cone    Low_fat     Organic
    54    Vanilla            Cone    Low_fat Not_organic
    55    Vanilla            Cone No_low_fat     Organic
    56    Vanilla            Cone No_low_fat Not_organic
    57    Vanilla            Pint    Low_fat     Organic
    58    Vanilla            Pint    Low_fat Not_organic
    59    Vanilla            Pint No_low_fat     Organic
    60    Vanilla            Pint No_low_fat Not_organic

Al observar las correlaciones de este diseño fraccional, notamos que dos atributos comienzan a correlacionarse, específicamente Light y Organic. Esto siempre será el caso en los diseños fraccionales; significa que algunas combinaciones de niveles serán más frecuentes que otras. Solo en un diseño factorial completo de 60 perfiles todos los atributos serán totalmente incorrelacionados u ortogonales.

Aunque 30 perfiles presentan un mejor equilibrio matemático, sigue siendo una cantidad excesiva para una encuesta. Para evitar respuestas aleatorias por cansancio, sacrificaremos parte del balance estadístico en favor de un cuestionario ágil.

Un diseño posible y realista con solo 15 perfiles sería desbalanceado, pero óptimo para el consumidor, y se vería así:

summary(doe(attributes, seed = 123, trials = 15))
Experimental design
# trials for partial factorial: 15 
# trials for full factorial   : 60 
Random seed                   : 123 

Attributes and levels:
Flavor: Raspberry, Chocolate, Strawberry, Mango, Vanilla 
Package: Homemade_waffle, Cone, Pint 
Light: Low_fat, No_low_fat 
Organic: Organic, Not_organic 

Design efficiency:
 Trials D-efficiency Balanced
     15        0.762    FALSE

Partial factorial design correlations:
** Note: Variables are assumed to be ordinal **
        Flavor Package Light Organic
Flavor   1.000       0 0.103   0.265
Package  0.000       1 0.000   0.000
Light    0.103       0 1.000   0.179
Organic  0.265       0 0.179   1.000

Partial factorial design:
 trial     Flavor         Package      Light     Organic
     4  Raspberry Homemade_waffle No_low_fat Not_organic
     5  Raspberry            Cone    Low_fat     Organic
     9  Raspberry            Pint    Low_fat     Organic
    14  Chocolate Homemade_waffle    Low_fat Not_organic
    18  Chocolate            Cone    Low_fat Not_organic
    23  Chocolate            Pint No_low_fat     Organic
    26 Strawberry Homemade_waffle    Low_fat Not_organic
    29 Strawberry            Cone    Low_fat     Organic
    36 Strawberry            Pint No_low_fat Not_organic
    39      Mango Homemade_waffle No_low_fat     Organic
    44      Mango            Cone No_low_fat Not_organic
    46      Mango            Pint    Low_fat Not_organic
    49    Vanilla Homemade_waffle    Low_fat     Organic
    56    Vanilla            Cone No_low_fat Not_organic
    58    Vanilla            Pint    Low_fat Not_organic

Estimable effects from partial factorial design:

  Flavor|Chocolate
  Flavor|Strawberry
  Flavor|Mango
  Flavor|Vanilla
  Package|Cone
  Package|Pint
  Light|No_low_fat
  Organic|Not_organic
  Flavor|Chocolate:Package|Cone
  Flavor|Strawberry:Package|Cone
  Flavor|Mango:Package|Cone
  Flavor|Vanilla:Package|Cone
  Flavor|Chocolate:Package|Pint
  Flavor|Strawberry:Package|Pint 

Full factorial design:
 trial     Flavor         Package      Light     Organic
     1  Raspberry Homemade_waffle    Low_fat     Organic
     2  Raspberry Homemade_waffle    Low_fat Not_organic
     3  Raspberry Homemade_waffle No_low_fat     Organic
     4  Raspberry Homemade_waffle No_low_fat Not_organic
     5  Raspberry            Cone    Low_fat     Organic
     6  Raspberry            Cone    Low_fat Not_organic
     7  Raspberry            Cone No_low_fat     Organic
     8  Raspberry            Cone No_low_fat Not_organic
     9  Raspberry            Pint    Low_fat     Organic
    10  Raspberry            Pint    Low_fat Not_organic
    11  Raspberry            Pint No_low_fat     Organic
    12  Raspberry            Pint No_low_fat Not_organic
    13  Chocolate Homemade_waffle    Low_fat     Organic
    14  Chocolate Homemade_waffle    Low_fat Not_organic
    15  Chocolate Homemade_waffle No_low_fat     Organic
    16  Chocolate Homemade_waffle No_low_fat Not_organic
    17  Chocolate            Cone    Low_fat     Organic
    18  Chocolate            Cone    Low_fat Not_organic
    19  Chocolate            Cone No_low_fat     Organic
    20  Chocolate            Cone No_low_fat Not_organic
    21  Chocolate            Pint    Low_fat     Organic
    22  Chocolate            Pint    Low_fat Not_organic
    23  Chocolate            Pint No_low_fat     Organic
    24  Chocolate            Pint No_low_fat Not_organic
    25 Strawberry Homemade_waffle    Low_fat     Organic
    26 Strawberry Homemade_waffle    Low_fat Not_organic
    27 Strawberry Homemade_waffle No_low_fat     Organic
    28 Strawberry Homemade_waffle No_low_fat Not_organic
    29 Strawberry            Cone    Low_fat     Organic
    30 Strawberry            Cone    Low_fat Not_organic
    31 Strawberry            Cone No_low_fat     Organic
    32 Strawberry            Cone No_low_fat Not_organic
    33 Strawberry            Pint    Low_fat     Organic
    34 Strawberry            Pint    Low_fat Not_organic
    35 Strawberry            Pint No_low_fat     Organic
    36 Strawberry            Pint No_low_fat Not_organic
    37      Mango Homemade_waffle    Low_fat     Organic
    38      Mango Homemade_waffle    Low_fat Not_organic
    39      Mango Homemade_waffle No_low_fat     Organic
    40      Mango Homemade_waffle No_low_fat Not_organic
    41      Mango            Cone    Low_fat     Organic
    42      Mango            Cone    Low_fat Not_organic
    43      Mango            Cone No_low_fat     Organic
    44      Mango            Cone No_low_fat Not_organic
    45      Mango            Pint    Low_fat     Organic
    46      Mango            Pint    Low_fat Not_organic
    47      Mango            Pint No_low_fat     Organic
    48      Mango            Pint No_low_fat Not_organic
    49    Vanilla Homemade_waffle    Low_fat     Organic
    50    Vanilla Homemade_waffle    Low_fat Not_organic
    51    Vanilla Homemade_waffle No_low_fat     Organic
    52    Vanilla Homemade_waffle No_low_fat Not_organic
    53    Vanilla            Cone    Low_fat     Organic
    54    Vanilla            Cone    Low_fat Not_organic
    55    Vanilla            Cone No_low_fat     Organic
    56    Vanilla            Cone No_low_fat Not_organic
    57    Vanilla            Pint    Low_fat     Organic
    58    Vanilla            Pint    Low_fat Not_organic
    59    Vanilla            Pint No_low_fat     Organic
    60    Vanilla            Pint No_low_fat Not_organic

Al forzar el experimento a solo 15 escenarios, el reporte de la función doe nos revela los siguientes compromisos estadísticos:

Eficiencia y Balance: El diseño es oficialmente desbalanceado (Balanced = FALSE) y su eficiencia (D-efficiency) cae a 0.762. Esto significa que los niveles de los atributos no aparecerán la misma cantidad de veces en la encuesta (por ejemplo, algunos envases se mostrarán más que otros).

Pérdida de Ortogonalidad (Correlaciones): Como advertimos en la teoría, al reducir la muestra, los atributos dejan de ser 100% independientes. La matriz muestra una correlación de 0.179 entre Light y Organic, y una de 0.103 entre Flavor y Package o tambian el 0.265 entre organic y flavor.

Efectos Estimables (Estimable effects): Esta es la sección más crítica. El reporte confirma que, a pesar del recorte a 15 preguntas, el modelo sigue siendo capaz de calcular las utilidades de todos los efectos principales (sabrá evaluar cada sabor, cada envase y cada característica). El verdadero sacrificio es que perdimos la capacidad de medir casi todas las interacciones cruzadas (por ejemplo, ya no podremos saber si el sabor fresa gusta más específicamente cuando viene en cono).

La Encuesta Final: La sección Partial factorial design nos entrega el listado exacto de las 15 combinaciones específicas (los trials 4, 5, 9, 14, 18, 23, 26, 29, 36, 39, 44, 46, 49, 56 y 58 del diseño original) que conformarán el cuestionario físico o digital para los consumidores.

Este diseño asume un sacrificio técnico (menor eficiencia y nulas interacciones) a cambio de una viabilidad práctica: garantizar que los encuestados respondan con atención al 100% de los 15 perfiles mostrados.

6. Análisis Conjunto a Nivel Agregado (Many Respondents)

Para entender las preferencias del mercado en general, estimaremos un modelo conjunto utilizando los datos de los 15 participantes de forma agregada. Utilizaremos la función conjoint, la cual estima las utilidades parciales (part-worths) asumiendo un modelo aditivo con Effects coding.

# Estimación del modelo conjunto para todos los encuestados
conjoint_allrespondents <- conjoint(
  icecream_long, # Usamos nuestros datos en formato largo
  rvar = "rating", 
  evar = c("Flavor", "Packaging", "Light", "Organic")
)

# Resumen de resultados (Utilidades e Importancia)
summary(conjoint_allrespondents)
Conjoint analysis
Data                 : icecream_long 
Response variable    : rating 
Explanatory variables: Flavor, Packaging, Light, Organic 

Conjoint part-worths:
   Attributes          Levels     PW
 Flavor       Chocolate        0.000
 Flavor       Mango           -0.672
 Flavor       Raspberry       -2.356
 Flavor       Strawberry      -1.144
 Flavor       Vanilla         -2.461
 Packaging    Cone             0.000
 Packaging    Homemade waffle  0.697
 Packaging    Pint            -0.690
 Light        Low fat          0.000
 Light        No low fat      -1.036
 Organic      Not organic      0.000
 Organic      Organic          1.480
 Base utility ~                7.731

Conjoint importance weights:
 Attributes    IW
  Flavor    0.387
  Packaging 0.218
  Light     0.163
  Organic   0.233

Conjoint regression results:

                           coefficient
 (Intercept)                     7.731
 Flavor|Mango                   -0.672
 Flavor|Raspberry               -2.356
 Flavor|Strawberry              -1.144
 Flavor|Vanilla                 -2.461
 Packaging|Homemade waffle       0.697
 Packaging|Pint                 -0.690
 Light|No low fat               -1.036
 Organic|Organic                 1.480

Los resultados del modelo nos permiten entender a profundidad cómo valoran los consumidores este producto. El análisis se divide en dos grandes hallazgos: qué atributos pesan más en la decisión de compra (Importancia Relativa) y qué variaciones específicas prefieren dentro de cada atributo (Utilidades Parciales).

  1. Importancia Relativa (Importance Weights): ¿Qué es lo que más le importa al cliente? El modelo calcula el peso de cada atributo sobre el 100% de la decisión de compra. * Sabor (36.1%): Es el factor dominante. Más de una tercera parte de la decisión del consumidor se basa exclusivamente en qué sabor va a comer. * Orgánico (32.0%): Es el segundo factor más crítico, casi empatado con el sabor. * Grasa / Light (22.1%): Tiene una importancia moderada en la elección. * Envase (9.9%): Es el factor menos relevante. Al consumidor le da prácticamente igual cómo venga empacado el helado.

  2. Utilidades Parciales (Part-worths): ¿Qué características específicas prefieren? Dado que el modelo estableció un nivel “base” (con valor 0.000) para cada atributo, interpretamos el resto de los niveles en función de cuántos puntos suman o restan a la calificación de ese producto base:

  • Sabor: El Chocolate (línea base) es indiscutiblemente el sabor favorito. Todos los demás sabores tienen utilidades negativas, lo que significa que restan puntos a la calificación. La fresa (-0.133) es una alternativa aceptable, pero la frambuesa (-1.825) y el mango (-1.767) son fuertemente penalizados por este mercado.
  • Grasa: Aquí hay una preferencia clara por la indulgencia. La opción regular (No low fat) incrementa drásticamente la calificación en +1.117 puntos respecto a la versión baja en grasa. Los consumidores prefieren un helado tradicional.
  • Orgánico: Un hallazgo contraintuitivo pero valioso: la opción Orgánica destruye el atractivo del producto, restándole -1.617 puntos frente a la opción no orgánica. Esto puede deberse a que el consumidor asocia “orgánico” con un peor sabor o una textura indeseada en los helados.
  • Envase: Como vimos, importa poco. El Waffle casero suma una cantidad minúscula (+0.067) frente al cono normal, mientras que la tarrina o bote (Pint) resta un poco de utilidad (-0.433).
  1. El “Helado Ideal” (Perfil Óptimo) Basándonos en estos coeficientes, el producto perfecto a lanzar para este segmento de mercado (el que obtendría la máxima calificación posible) sería un Helado de Chocolate, en Waffle casero, tradicional (no bajo en grasa) y no orgánico.
plot(conjoint_allrespondents)

  • Sabor (Flavor): La caída drástica desde el Chocolate (0.0) hacia el Mango y la Frambuesa (cerca de -1.8) ilustra visualmente el fuerte rechazo hacia los sabores frutales no tradicionales en este segmento. Solo la Fresa logra mantenerse cerca del nivel de preferencia del Chocolate.

  • Envase (Packaging): Es el gráfico con menor variación vertical (más plano). Esto confirma visualmente su baja importancia relativa (9.9%). La diferencia entre servirlo en un cono normal o en un waffle casero es casi imperceptible, mientras que la tarrina (Pint) genera un ligero desagrado.

  • Grasa (Light): La pronunciada pendiente positiva al moverse hacia No low fat demuestra sin ambigüedades que el mercado objetivo busca un helado tradicional e indulgente, castigando la versión baja en grasa.

  • Orgánico (Organic): Presenta la caída recta más fuerte del análisis. El cambio de la fórmula regular (Not organic) a la versión orgánica desploma la preferencia del producto a niveles cercanos al -1.6, confirmando que este mercado asocia lo orgánico con un atributo negativo para los helados.

7. Predicción de Utilidades (Simulación de Mercado)

El análisis conjunto no termina con la identificación de las utilidades parciales, sino que culmina en la simulación de mercado. Dado que encuestar a los consumidores sobre los 60 perfiles posibles del diseño factorial completo era inviable, utilizamos un subconjunto fraccional para estimar nuestro modelo.

Ahora, con las utilidades de cada nivel ya calculadas, podemos utilizar la función de predicción para estimar qué calificación exacta obtendría cualquiera de las 60 combinaciones posibles de helado en el mercado.

Esto nos permite rankear todos los productos potenciales y descubrir nichos ocultos o combinaciones altamente rentables que no formaron parte del cuestionario original, garantizando que el diseño del producto final (el lanzamiento) esté respaldado por datos empíricos.

8. Simulación del Mercado Total

Para predecir cuál sería el helado perfecto, primero necesitamos que R genere una matriz con absolutamente todas las combinaciones posibles de atributos. Luego, aplicaremos nuestro modelo conjunto sobre este universo de productos teóricos para rankearlos de mayor a menor preferencia.

# 1. Crear las 60 combinaciones nombrando las columnas directamente 
profiles.all <- expand.grid(
  Flavor = unique(icecream$Flavor),
  Packaging = unique(icecream$Packaging),
  Light = unique(icecream$Light),
  Organic = unique(icecream$Organic)
)

# 2. Predecir utilidades y ordenar el ranking
ranking_helados <- predict(conjoint_allrespondents, profiles.all) %>% 
  arrange(desc(Prediction))

# 3. Ver los mejores helados
ranking_helados
Conjoint Analysis
Data                 : icecream_long 
Response variable    : rating 
Explanatory variables: Flavor, Packaging, Light, Organic 
Prediction dataset   : profiles.all 
Rows shown           : 20 of 60 

     Flavor       Packaging      Light     Organic Prediction
  Chocolate Homemade waffle    Low fat     Organic      9.908
      Mango Homemade waffle    Low fat     Organic      9.236
  Chocolate            Cone    Low fat     Organic      9.211
  Chocolate Homemade waffle No low fat     Organic      8.872
 Strawberry Homemade waffle    Low fat     Organic      8.763
      Mango            Cone    Low fat     Organic      8.539
  Chocolate            Pint    Low fat     Organic      8.521
  Chocolate Homemade waffle    Low fat Not organic      8.428
      Mango Homemade waffle No low fat     Organic      8.200
  Chocolate            Cone No low fat     Organic      8.176
 Strawberry            Cone    Low fat     Organic      8.067
      Mango            Pint    Low fat     Organic      7.849
      Mango Homemade waffle    Low fat Not organic      7.756
  Chocolate            Cone    Low fat Not organic      7.731
 Strawberry Homemade waffle No low fat     Organic      7.728
  Raspberry Homemade waffle    Low fat     Organic      7.552
      Mango            Cone No low fat     Organic      7.503
  Chocolate            Pint No low fat     Organic      7.486
    Vanilla Homemade waffle    Low fat     Organic      7.447
  Chocolate Homemade waffle No low fat Not organic      7.392
library(ggplot2)
library(dplyr)

# 1. Preparar datos del Top 10
top10_data <- ranking_helados %>%
  head(10) %>%
  mutate(
    # Crear un etiqueta descriptiva para el eje
    Perfil = paste(Flavor, Packaging, Light, Organic, sep = " + "),
    Perfil = reorder(Perfil, Prediction) # Ordenar para el gráfico
  )

# 2. Crear el gráfico
ggplot(top10_data, aes(x = Prediction, y = Perfil, fill = Flavor)) +
  geom_col(width = 0.7) +
  geom_text(aes(label = round(Prediction, 2)), 
            hjust = -0.2, size = 3.5, fontface = "bold") +
  scale_fill_brewer(palette = "Set2") +
  coord_cartesian(xlim = c(6, 8.5)) + # Ajustar zoom del eje X
  labs(
    title = "Top 10 Helados con Mayor Preferencia Estimada",
    subtitle = "Simulación basada en el modelo conjunto de 60 combinaciones",
    x = "Calificación Predicha (Escala 1 - 10)",
    y = NULL,
    fill = "Sabor"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold"),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

El Producto Estrella: El helado con mayor potencial de éxito (calificación máxima de 8.208) es el de Chocolate, servido en Waffle casero, tradicional (No low fat) y No orgánico. Esta es la combinación exacta que maximiza la satisfacción del segmento encuestado.

El Plan B (Sabor Fresa): Si la empresa quisiera lanzar una segunda línea de producto, la versión de Fresa con las mismas características (Waffle, tradicional, no orgánico) ocupa el tercer lugar con una calificación de 8.075, superando incluso al sabor chocolate cuando este se sirve en tarrina (Pint).

El rechazo absoluto a lo “Saludable”: El ranking confirma lo que vimos en las utilidades individuales. Las primeras 6 posiciones están dominadas exclusivamente por helados indulgentes (“No low fat” y “Not organic”). La primera opción baja en grasa logra aparecer recién en el puesto 7, y el primer helado orgánico se hunde hasta la posición 14.

El Envase es secundario: El producto número 1 (Waffle) y el número 2 (Cono) son idénticos en todo lo demás y su diferencia de puntaje es mínima (8.208 vs 8.142). Esto demuestra que si la empresa necesita ahorrar costos usando conos en lugar de waffles, el impacto en la satisfacción del cliente será casi imperceptible, siempre y cuando el helado siga siendo de chocolate, tradicional y no orgánico.