En este documento se muestra el desarrollo de un ejemplo de análisis de regresión lineal simple como parte de una de las etapas del concurso de oposición para ingreso o abierto como profesor de asignatura “A” definitivo de la asignatura Fundamentos de Estadística de la carrera en Ingeniería en Computación.
Un ingeniero de software móvil quería determinar empíricamente si existe una relación entre el tiempo de uso de una aplicación y el porcentaje de batería consumido. Para lo anterior, realizó 30 mediciones controladas en un dispositivo Android, registrando cuántos minutos estuvo activa la app y midiendo el porcentaje de batería consumido con la herramienta Battery Historian.
a) Identificación de variables
En esta investigación, la variable dependiente o de respuesta es el porcentaje de batería consumido y la variable independiente o predictiva es el tiempo de uso de la aplicación en minutos.
b) Gráfico de dispersión y cálculo de la correlación lineal
minutos <- c(5, 8, 10, 12, 15, 18, 20, 22, 25, 28,
30, 32, 35, 38, 40, 42, 45, 48, 50, 52,
55, 58, 60, 62, 65, 68, 70, 75, 80, 90)
bateria <- c(5.8, 1.2, 7.5, 3.1, 9.8, 5.4, 11.2, 6.9, 13.5, 8.6,
15.1, 10.3, 16.8, 12.0, 18.4, 13.7, 20.1, 15.4, 21.8, 17.1,
23.5, 18.8, 25.2, 20.5, 26.9, 22.2, 28.6, 30.9, 33.2, 38.7)
consumo <- data.frame(minutos, bateria)
library(ggplot2)
ggplot(consumo, aes(x = minutos, y = bateria)) +
geom_point(color = "blue", size = 3) +
labs(x = "Tiempo de uso (min)", y = "Batería consumida (%)") +
theme_minimal() +
scale_x_continuous(limits = c(0, 95), breaks = seq(0, 90, 15)) +
scale_y_continuous(limits = c(0, 45), breaks = seq(0, 45, 5)) +
theme(
axis.title.x = element_text(face = "bold", vjust = 0, colour = "green", size = rel(1.5)),
axis.title.y = element_text(face = "bold", vjust = 2, colour = "red", size = rel(1.5)),
axis.text.x = element_text(angle = 0, vjust = 0.5, size = 11),
axis.text.y = element_text(angle = 0, vjust = 0.5, size = 11)
)
En el diagrama de dispersión se observa que hay correlación positiva y alta entre las dos variables, con una dispersión alrededor de la tendencia lineal.
cor(consumo$minutos, consumo$bateria)
## [1] 0.9506489
El coeficiente de correlación de Pearson es aproximadamente 0.95, por lo que se confirma que hay una correlación positiva fuerte entre el tiempo de uso y el porcentaje de batería consumido. Esto implica que a mayor tiempo de uso de la app, mayor consumo de batería, aunque existe variabilidad atribuible a otros factores (procesos en segundo plano, temperatura, hardware, notificaciones, sincronización, etc.).
c) Ajuste del modelo de regresión y la línea recta estimada. Interpretación de los coeficientes
reg <- lm(bateria ~ minutos, data = consumo)
summary(reg)
##
## Call:
## lm(formula = bateria ~ minutos, data = consumo)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.442 -2.802 1.497 2.619 3.701
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.81062 1.11855 0.725 0.475
## minutos 0.37987 0.02343 16.213 9.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.928 on 28 degrees of freedom
## Multiple R-squared: 0.9037, Adjusted R-squared: 0.9003
## F-statistic: 262.9 on 1 and 28 DF, p-value: 9.196e-16
La ecuación de la recta de regresión es la siguiente:
\[Batería\ consumida = 0.81 + 0.37 \cdot Tiempo\ de\ uso\]
En esta ecuación se observa que la intersección de la recta con el eje de las Y es 0.81%, lo cual es consistente con el hecho de que si la app no se usa, prácticamente no consume batería. Por otro lado, por cada minuto de uso, la batería consumida aumenta 0.37 puntos porcentuales en promedio.
ggplot(consumo, aes(x = minutos, y = bateria)) +
geom_point(shape = 15, size = 2.5, color = "darkblue") +
geom_smooth(method = lm, se = FALSE, color = "red", linewidth = 1) +
scale_x_continuous(limits = c(0, 95), breaks = seq(0, 90, 15)) +
scale_y_continuous(limits = c(0, 45), breaks = seq(0, 45, 5)) +
labs(x = "Tiempo de uso (min)", y = "Batería consumida (%)") +
theme_minimal() +
theme(
axis.title.x = element_text(face = "bold", colour = "orange", size = 14),
axis.title.y = element_text(face = "bold", colour = "blue", size = 14),
axis.text = element_text(size = 12),
panel.grid.major = element_line(color = "grey90")
)
En la gráfica se observa la recta de regresión sobre el diagrama de
dispersión.
d) Intervalos de confianza al 95% para la pendiente y la ordenada al origen
Una vez verificados los supuestos del modelo, se procede a calcular los intervalos de confianza al 95% para los coeficientes de la recta de regresión.
intervalos_confianza <- confint(reg, level = 0.95)
print(intervalos_confianza)
## 2.5 % 97.5 %
## (Intercept) -1.4806321 3.1018726
## minutos 0.3318791 0.4278687
ic_interseccion <- intervalos_confianza["(Intercept)", ]
ic_pendiente <- intervalos_confianza["minutos", ]
cat("\n--- Intervalo de confianza al 95% para la ordenada al origen (α) ---\n")
##
## --- Intervalo de confianza al 95% para la ordenada al origen (α) ---
cat("Estimación puntual:", round(coef(reg)["(Intercept)"], 4), "\n")
## Estimación puntual: 0.8106
cat("IC 95%: [", round(ic_interseccion[1], 4), ", ",
round(ic_interseccion[2], 4), "]\n")
## IC 95%: [ -1.4806 , 3.1019 ]
cat("\n--- Intervalo de confianza al 95% para la pendiente (β) ---\n")
##
## --- Intervalo de confianza al 95% para la pendiente (β) ---
cat("Estimación puntual:", round(coef(reg)["minutos"], 4), "\n")
## Estimación puntual: 0.3799
cat("IC 95%: [", round(ic_pendiente[1], 4), ", ",
round(ic_pendiente[2], 4), "]\n")
## IC 95%: [ 0.3319 , 0.4279 ]
**e) Determinar el valor de \(R^2\) de la recta de regresión.
modelo <- lm(bateria ~ minutos, data = consumo)
resumen <- summary(modelo)
r_cuadrado <- resumen$adj.r.squared
print(paste("R² ajustado =", round(r_cuadrado, 4)))
## [1] "R² ajustado = 0.9003"
Esto significa que el modelo explica aproximadamente el 90% de la varianza total de la variable batería consumida. Es un ajuste aceptable, lo cual tiene sentido porque el consumo de batería en una app está determinado por el tiempo de uso, pero también existe un 10% de variabilidad atribuible a otros factores que no se están considerando en este modelo. Es importante notar que \(R^2 = r^2 = 0.95^2 = 0.90\).
f) Pruebas de hipótesis para los coeficientes de la recta de regresión con un nivel de significancia del 0.05.
Prueba de hipótesis para el tiempo de uso (pendiente)
\[H_0: \beta = 0\] \[H_1: \beta \neq 0\]
\[Si,\ t_0 < t_1^* \ \text{ó} \ t_0 > t_2^*, \ se \ rechaza \ la \ hipótesis \ nula\]
En donde, \(t_1^* = - t_{\frac{\alpha}{2},\ n-2}\) \(t_2^* = t_{\frac{\alpha}{2},\ n-2}\)
modelo <- lm(bateria ~ minutos, data = consumo)
resumen <- summary(modelo)
coeficientes <- coef(resumen)
pendiente <- coeficientes["minutos", ]
print(paste("Estimación:", round(pendiente["Estimate"], 4)))
## [1] "Estimación: 0.3799"
print(paste("Estadístico t:", round(pendiente["t value"], 4)))
## [1] "Estadístico t: 16.2129"
t_critico <- qt(0.025, 28, lower.tail = FALSE)
print(paste("Valor crítico t (α=0.05, 28 gl):", round(t_critico, 4)))
## [1] "Valor crítico t (α=0.05, 28 gl): 2.0484"
if(abs(pendiente["t value"]) > t_critico) {
cat("Decisión: No se acepta H₀ - Coeficiente significativo\n")
} else {
cat("Decisión: No se rechaza H₀ - Coeficiente no significativo\n")
}
## Decisión: No se acepta H₀ - Coeficiente significativo
Debido a que la estadística de prueba es mayor que el valor crítico (aproximadamente 16.25 > 2.0484), no se acepta la hipótesis nula, por lo que la pendiente es diferente de cero en este modelo con un nivel de significancia del 5%.
Prueba de hipótesis para la intersección
\[H_0: \alpha = 0\] \[H_1: \alpha \neq 0\]
\[Si,\ t_0 < t_1^* \ \text{ó} \ t_0 > t_2^*, \ se \ rechaza \ la \ hipótesis \ nula\]
En donde, \(t_1^* = - t_{\frac{\alpha}{2},\ n-2}\) \(t_2^* = t_{\frac{\alpha}{2},\ n-2}\)
modelo <- lm(bateria ~ minutos, data = consumo)
resumen <- summary(modelo)
coeficientes <- coef(resumen)
interseccion <- coeficientes["(Intercept)", ]
print(paste("Estimación:", round(interseccion["Estimate"], 4)))
## [1] "Estimación: 0.8106"
print(paste("Estadístico t:", round(interseccion["t value"], 4)))
## [1] "Estadístico t: 0.7247"
t_critico <- qt(0.025, 28, lower.tail = FALSE)
print(paste("Valor crítico t (α=0.05, 28 gl):", round(t_critico, 4)))
## [1] "Valor crítico t (α=0.05, 28 gl): 2.0484"
if(abs(interseccion["t value"]) > t_critico) {
cat("Decisión: No se acepta H₀ - Coeficiente significativo\n")
} else {
cat("Decisión: No se rechaza H₀ - Coeficiente no significativo\n")
}
## Decisión: No se rechaza H₀ - Coeficiente no significativo
Debido a que la estadística de prueba es menor que el valor crítico (aproximadamente 0.7247 < 2.0484), no se rechaza la hipótesis nula, por lo que la intersección en el eje de las Y no es significativamente diferente de cero con un nivel de significancia del 5%. Esto es consistente con el hecho de que una app que no se usa no debería consumir batería de forma significativa.
g) En una segunda etapa del estudio, el ingeniero realizó cuatro mediciones más, y los tiempos de uso fueron: 15, 35, 60 y 85 minutos. ¿Qué media del porcentaje de batería consumido se estima mediante intervalo de confianza al 95% para estos cuatro nuevos tiempos de uso? Proporcione una interpretación de dicha estimación mediante el intervalo de confianza (mostrar el código de R).
predict(reg, newdata=data.frame(minutos=15), interval='confidence', level=0.95)
## fit lwr upr
## 1 6.508729 4.814528 8.202931
predict(reg, newdata=data.frame(minutos=15), interval='prediction', level=0.95)
## fit lwr upr
## 1 6.508729 0.2756935 12.74176
Para un tiempo de uso de 15 minutos, el valor medio del consumo de batería está entre aproximadamente 4.81% y 8.20% y se predice un consumo individual entre aproximadamente 0.27% y 12.74 %.
predict(reg, newdata=data.frame(minutos=35), interval='confidence', level=0.95)
## fit lwr upr
## 1 14.10621 12.96162 15.25079
predict(reg, newdata=data.frame(minutos=35), interval='prediction', level=0.95)
## fit lwr upr
## 1 14.10621 7.999613 20.2128
Para un tiempo de uso de 35 minutos, el valor medio del consumo de batería está entre aproximadamente 12.96% 20.21%.
predict(reg, newdata=data.frame(minutos=60), interval='confidence', level=0.95)
## fit lwr upr
## 1 23.60306 22.20619 24.99992
predict(reg, newdata=data.frame(minutos=60), interval='prediction', level=0.95)
## fit lwr upr
## 1 23.60306 17.44419 29.76192
Para un tiempo de uso de 60 minutos, el valor medio del consumo de batería está entre aproximadamente 22.20% y 24.99% y se predice un consumo individual entre aproximadamente 17.44% y 29.76%.
predict(reg, newdata=data.frame(minutos=85), interval='confidence', level=0.95)
## fit lwr upr
## 1 33.0999 30.76073 35.43908
predict(reg, newdata=data.frame(minutos=85), interval='prediction', level=0.95)
## fit lwr upr
## 1 33.0999 26.66157 39.53824
Para un tiempo de uso de 85 minutos, el valor medio del consumo de batería está entre aproximadamente 30.76% y 35.43% y se predice un consumo individual entre aproximadamente 26.66% y 39.53%.
h) Gráficas de las bandas de confianza
Interpretación
En esta gráfica se superponen dos bandas: Banda roja (media, IC 95%): es estrecha y representa la incertidumbre sobre el consumo promedio en cada tiempo de uso. Es útil para responder preguntas como “¿cuál es el consumo esperado promedio si la app se usa 40 minutos?”.
Banda azul (predicción individual, IC 95%): es considerablemente más ancha y representa la incertidumbre sobre el consumo de una nueva medición individual en cada tiempo de uso. Es útil para responder preguntas como “si un usuario usa la app 40 minutos, ¿en qué rango caerá su consumo de batería?”.
i) Verificación los supuestos
Normalidad en los residuales
resi_esta <- rstandard(modelo)
qqnorm(resi_esta)
qqline(resi_esta)
shapiro.test(resi_esta)
##
## Shapiro-Wilk normality test
##
## data: resi_esta
## W = 0.84624, p-value = 0.0005177
Dado que en la gráfica cuantil-cuantil Normal se observa que los residuales se distribuyen alrededor de la recta de 45 grados y que el valor p de la prueba Shapiro-Wilk es mayor que 0.05, se concluye que la distribución de los residuales estandarizados se aproxima a una distribución normal.
Homogeneidad de varianzas
residuos <- residuals(modelo)
ajustados <- fitted(modelo)
plot(ajustados, residuos,
xlab = "Valores ajustados",
ylab = "Residuos",
main = "Homocedasticidad: Residuos vs Ajustados",
pch = 19, col = "blue")
abline(h = 0, col = "red", lwd = 2)
library(lmtest)
modelo <- lm(bateria ~ minutos, data = consumo)
bp_test <- bptest(modelo)
print(bp_test)
##
## studentized Breusch-Pagan test
##
## data: modelo
## BP = 0.060891, df = 1, p-value = 0.8051
if(bp_test$p.value < 0.05) {
cat("Se rechaza H0: Las varianzas NO son homogéneas (heterocedasticidad)\n")
} else {
cat("No se rechaza H0: Las varianzas son homogéneas\n")
}
## No se rechaza H0: Las varianzas son homogéneas
Se observa que los pares de valores ajustados y los residuales estandarizados tienen un comportamiento similar de forma horizontal, lo que implica que las varianzas son homogéneas. Adicionalmente, se realizó la prueba Breusch-Pagan para verificar si hay o no heterocedasticidad y se halló que a un nivel de significancia del 5%, las varianzas son homogéneas.
library(lmtest)
dwtest(modelo)
##
## Durbin-Watson test
##
## data: modelo
## DW = 3.3413, p-value = 1
## alternative hypothesis: true autocorrelation is greater than 0
Debido a que el valor del estadístico Durbin-Watson es cercano a 2, se concluye que no existe autocorrelación de los errores, lo que implica que se verifica el supuesto de independencia.
Por lo anterior, los residuales cumplen con los supuestos de normalidad, homocedasticidad e independencia. En general, la conclusión que se obtiene es que el modelo ajusta aceptablemente el consumo de batería a partir del tiempo de uso de la app y que explica aproximadamente el 90% de la varianza total. Cabe señalar que se encontró una correlación fuerte (\(r = 0.95\)) y que, además, la pendiente de la ecuación de la recta de regresión es significativa. También se comprobaron los supuestos relacionados con los residuales.
Bibliografía
Marqués Asensio, F. (2022). R en profundidad. Programación, gráficos y estadística. Editorial Alfaomega.