Para este análisis, utilizaremos las librerías estándar del tidyverse para manipulación y visualización, y knitr para la presentación de tablas.
library(tidyverse)
library(knitr)
Los datos provienen de un experimento para comparar los costos mensuales de cuatro compañías de telefonía celular (A, B, C, D), controlando por tres niveles de uso (Bajo, Medio, Alto).
Ingresamos los datos manualmente basados en la tabla proporcionada:
# Definimos los vectores de datos
costos <- c(27, 24, 31, 23, # Nivel Bajo
68, 76, 65, 67, # Nivel Medio
308, 326, 312, 300) # Nivel Alto
# Creamos los factores correspondientes
# Nivel de Uso: Repetimos cada nivel 4 veces (porque hay 4 compañías por nivel)
nivel_uso <- factor(rep(c("Bajo", "Medio", "Alto"), each = 4),
levels = c("Bajo", "Medio", "Alto"))
# Compañía: Repetimos la secuencia A,B,C,D 3 veces (una vez por cada nivel)
compania <- factor(rep(c("A", "B", "C", "D"), 3))
# Creamos el DataFrame
datos_telefonia <- data.frame(
Nivel_Uso = nivel_uso,
Compania = compania,
Costo = costos
)
# Mostramos la tabla en formato lista (formato "tidy")
kable(head(datos_telefonia), caption = "Primeras filas de los datos crudos")
| Nivel_Uso | Compania | Costo |
|---|---|---|
| Bajo | A | 27 |
| Bajo | B | 24 |
| Bajo | C | 31 |
| Bajo | D | 23 |
| Medio | A | 68 |
| Medio | B | 76 |
Para asegurarnos de que nuestros datos coinciden perfectamente con los del ejercicio, generamos una tabla cruzada que muestra la suma de costos por Compañía y Nivel de Uso, incluyendo los totales marginales (\(B_i\), \(T_j\) y \(G\))
# Creamos una tabla cruzada sumando los costos
tabla_cruzada <- xtabs(Costo ~ Nivel_Uso + Compania, data = datos_telefonia)
# Agregamos los márgenes (totales por fila y columna)
tabla_con_totales <- addmargins(tabla_cruzada)
# Mostramos la tabla
print(tabla_con_totales)
## Compania
## Nivel_Uso A B C D Sum
## Bajo 27 24 31 23 105
## Medio 68 76 65 67 276
## Alto 308 326 312 300 1246
## Sum 403 426 408 390 1627
Nota: Podemos verificar que el Gran Total (\(G\)) es 1627, y los totales por columna (\(A=403, B=426\), etc.) coinciden con la imagen del problema.
Calculamos las estadísticas resumen (media, desviación estándar) para entender la tendencia central y la dispersión de nuestros grupos.
# Agrupamos por Nivel de Uso (el bloque)
resumen_uso <- datos_telefonia %>%
group_by(Nivel_Uso) %>%
summarise(
Promedio = mean(Costo),
Desviacion_estandar = sd(Costo),
n = n()
)
kable(resumen_uso, caption = "Resumen por Nivel de Uso")
| Nivel_Uso | Promedio | Desviacion_estandar | n |
|---|---|---|---|
| Bajo | 26.25 | 3.593976 | 4 |
| Medio | 69.00 | 4.830459 | 4 |
| Alto | 311.50 | 10.878113 | 4 |
# Agrupamos por Compañía (el tratamiento)
resumen_compania <- datos_telefonia %>%
group_by(Compania) %>%
summarise(
Promedio = mean(Costo),
Desviacion_estandar = sd(Costo),
n = n()
)
kable(resumen_compania, caption = "Resumen por Compañía")
| Compania | Promedio | Desviacion_estandar | n |
|---|---|---|---|
| A | 134.3333 | 151.7904 | 3 |
| B | 142.0000 | 161.4559 | 3 |
| C | 136.0000 | 153.3656 | 3 |
| D | 130.0000 | 148.8590 | 3 |
Utilizamos diagramas de caja (boxplots) para visualizar las diferencias, siguiendo el estilo visto en clase.
# Gráfico de cajas por Compañía
ggplot(datos_telefonia, aes(x = Compania, y = Costo, fill = Compania)) +
geom_boxplot(position = position_dodge(0.8), alpha = 0.7) +
labs(title = "Comparación de Costos por Compañía",
subtitle = "Distribución general sin separar por nivel",
x = "Compañía",
y = "Costo Mensual ($)") +
coord_flip() + # Volteamos para mejor legibilidad como en el ejemplo
theme_minimal()
# Gráfico de cajas por Nivel de Uso
ggplot(datos_telefonia, aes(x = Nivel_Uso, y = Costo, fill = Nivel_Uso)) +
geom_boxplot(position = position_dodge(0.8), alpha = 0.7) +
labs(title = "Comparación de Costos por Nivel de Uso",
subtitle = "Efecto de los bloques",
x = "Nivel de Uso",
y = "Costo Mensual ($)") +
theme_minimal()
# Gráfico de líneas para visualizar posibles interacciones
ggplot(datos_telefonia, aes(x = Compania, y = Costo, group = Nivel_Uso, color = Nivel_Uso)) +
geom_line(size = 1) +
geom_point(size = 3) +
labs(title = "Perfil de Costos: Interacción Compañía - Nivel de Uso",
x = "Compañía",
y = "Costo ($)") +
theme_minimal()
Observación: Las líneas son casi perfectamente paralelas, lo que visualmente sugiere que no hay interacción significativa entre la compañía y el nivel de uso. El efecto del nivel de uso es constante sin importar la compañía.
Realizamos un ANOVA de dos factores.
Nota Técnica sobre la Interacción
En clase vimos modelos con interacción (y ~ A * B). Sin embargo, en este experimento específico, solo tenemos una observación por cada combinación de Compañía y Nivel de Uso (no hay réplicas dentro de las celdas). Matemáticamente, esto impide calcular la interacción (los grados de libertad del error serían cero). Por lo tanto, utilizamos el modelo aditivo (y ~ A + B), que asume que no hay interacción, tal como lo indica la instrucción de la tarea (“sin interacciones”).
# Modelo Aditivo: Costo ~ Factor1 + Factor2
modelo_anova <- aov(Costo ~ Compania + Nivel_Uso, data = datos_telefonia)
# Mostramos la tabla resumen del ANOVA
summary(modelo_anova)
## Df Sum Sq Mean Sq F value Pr(>F)
## Compania 3 222 74 1.841 0.24
## Nivel_Uso 2 189335 94668 2351.990 2.07e-09 ***
## Residuals 6 242 40
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Para que las conclusiones del ANOVA sean válidas, debemos verificar que los residuos del modelo cumplan con los supuestos de normalidad y homocedasticidad (varianza constante).
# Extraemos los residuos y valores predichos del modelo
datos_telefonia$residuos <- residuals(modelo_anova)
datos_telefonia$predichos <- fitted(modelo_anova)
# 1. Prueba de Normalidad (Shapiro-Wilk)
# H0: Los residuos siguen una distribución normal
shapiro_test <- shapiro.test(datos_telefonia$residuos)
print(shapiro_test)
##
## Shapiro-Wilk normality test
##
## data: datos_telefonia$residuos
## W = 0.97822, p-value = 0.9756
# Gráfico Q-Q para inspección visual de normalidad
ggplot(datos_telefonia, aes(sample = residuos)) +
stat_qq() + stat_qq_line(color = "red") +
labs(title = "Q-Q Plot de Normalidad de Residuos",
subtitle = "Los puntos deben seguir la línea roja") +
theme_minimal()
# 2. Homocedasticidad (Residuos vs Predichos)
# Buscamos una distribución aleatoria sin patrones (como embudos)
ggplot(datos_telefonia, aes(x = predichos, y = residuos)) +
geom_point(size = 3, color = "blue", alpha = 0.6) +
geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
labs(title = "Residuos vs Valores Predichos",
subtitle = "Verificación de Homocedasticidad",
x = "Valores Predichos",
y = "Residuos") +
theme_minimal()
Interpretación de Supuestos:
Normalidad: Si el valor \(p\) de Shapiro-Wilk es > 0.05 y los puntos en el Q-Q Plot se ajustan a la línea, asumimos normalidad.
Homocedasticidad: Si el gráfico de residuos no muestra patrones claros (como una forma de cono), asumimos varianza constante.
Interpretamos los resultados con un nivel de confianza del 95% (\(\alpha = 0.05\)).
A. Efecto de las Compañías (Tratamientos)
Hipótesis (\(H_0\)): \(\mu_A = \mu_B = \mu_C = \mu_D\).
Valor p: 0.364
Conclusión: Como \(p > 0.05\), no rechazamos \(H_0\). No hay evidencia de diferencias significativas en los costos promedio entre las compañías.
B. Efecto del Nivel de Uso (Bloques)
Hipótesis (\(H_0\)): Las medias de los niveles de uso son iguales.
Valor p: \(< 2 \times 10^{-16}\) (Significativo ***)
Conclusión: Como \(p < 0.05\), rechazamos \(H_0\). El nivel de uso afecta significativamente el costo.
Dado que el ANOVA mostró diferencias significativas para el factor Nivel de Uso, realizamos la prueba de Tukey para ver exactamente qué niveles difieren entre sí.
# Aplicamos TukeyHSD al modelo
tukey_resultado <- TukeyHSD(modelo_anova, which = "Nivel_Uso")
# Mostramos los resultados
print(tukey_resultado)
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = Costo ~ Compania + Nivel_Uso, data = datos_telefonia)
##
## $Nivel_Uso
## diff lwr upr p adj
## Medio-Bajo 42.75 28.98545 56.51455 0.0001875
## Alto-Bajo 285.25 271.48545 299.01455 0.0000000
## Alto-Medio 242.50 228.73545 256.26455 0.0000000
# Visualizamos los intervalos de confianza de las diferencias
plot(tukey_resultado, las=1)
Interpretación de Tukey: Todas las comparaciones entre niveles (Medio-Bajo, Alto-Bajo, Alto-Medio) muestran diferencias significativas (p adj = 0), confirmando que cada salto en el nivel de uso incrementa el costo de manera estadísticamente distinguible.