Código
# Cargar librerías necesarias
library(MASS)
library(ggplot2)
library(lmtest)
library(knitr)
library(nortest)Para este análisis, utilizaremos el conjunto de datos de costos de seguro insurance.csv, el cual contiene información sobre las características personales, de salud y demográficas de 1338 individuos, así como los gastos médicos facturados por su seguro.
Nuestro objetivo es evaluar el modelo de regresión lineal simple que predice los costos médicos facturados en dólares (\(Y = \text{charges}\)) en función de la edad del beneficiario (\(X = \text{age}\)), y aplicar la transformación de Box-Cox en caso de que los supuestos del modelo no se cumplan.
Primero, se cargan las librerías necesarias para el análisis y se importa la base de datos insurance.csv, que contiene 1,338 observaciones de beneficiarios de un seguro médico. La variable independiente (\(X\)) es la edad (age) y la variable dependiente (\(Y\)) son los costos médicos facturados (charges).
# Cargar librerías necesarias
library(MASS)
library(ggplot2)
library(lmtest)
library(knitr)
library(nortest)A continuación, realizamos la lectura del archivo e inspeccionamos los primeros registros (función head) exclusivamente para nuestras dos variables de interés, lo cual nos permite confirmar que los datos se han cargado correctamente en memoria.
# Cargar la base de datos
insurance = read.csv("insurance.csv")
# Primeras observaciones
head(insurance[, c("age", "charges")]) age charges
1 19 16884.924
2 18 1725.552
3 28 4449.462
4 33 21984.471
5 32 3866.855
6 31 3756.622
Para comenzar nuestro diagnóstico, ajustaremos un modelo de regresión lineal simple (\(Y = \beta_0 + \beta_1 X\)) para cuantificar la relación entre la edad y los costos médicos. Utilizaremos la función lm() y extraeremos el resumen estadístico para evaluar los coeficientes iniciales y la bondad de ajuste (\(R^2\)).
modelo_edad = lm(charges ~ age, data = insurance)
summary(modelo_edad)
Call:
lm(formula = charges ~ age, data = insurance)
Residuals:
Min 1Q Median 3Q Max
-8059 -6671 -5939 5440 47829
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3165.9 937.1 3.378 0.000751 ***
age 257.7 22.5 11.453 < 2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 11560 on 1336 degrees of freedom
Multiple R-squared: 0.08941, Adjusted R-squared: 0.08872
F-statistic: 131.2 on 1 and 1336 DF, p-value: < 2.2e-16
Una vez estimado el modelo, generaremos los gráficos de diagnóstico estándar.
# Diagnóstico inicial
par(mfrow = c(2, 2))
plot(modelo_edad)# Restaurar configuración gráfica
par(mfrow = c(1, 1)) Análisis visual de los supuestos del modelo inicial:
ggplot(insurance, aes(x = age, y = charges)) +
geom_point(color = "#16a085", size = 2, alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE, color = "#2c3e50", fill = "#16a085", alpha = 0.2) +
labs(
title = "Relación entre la Edad y los Costos Médicos",
subtitle = "Dataset Insurance",
x = "Edad del beneficiario (años)",
y = "Costos médicos facturados (charges)"
) +
theme_minimal()`geom_smooth()` using formula = 'y ~ x'
Revisaremos los tres supuestos fundamentales: Normalidad, Homocedasticidad e Independencia sobre los residuales del modelo original.
Para cualquier prueba de normalidad sobre los residuales del modelo (\(\varepsilon_i\)):
Regla de Decisión (\(\alpha = 0.05\)):
# Prueba Principal: Lilliefors (n > 50)
lillie_res <- lillie.test(modelo_edad$residuals)
print(lillie_res)
Lilliefors (Kolmogorov-Smirnov) normality test
data: modelo_edad$residuals
D = 0.35265, p-value < 2.2e-16
Como el \(p\text{-valor} < 2.2\text{e-16}\) es menor que el nivel de significancia \(\alpha = 0.05\), se rechaza la hipótesis nula (\(\text{H}_0\)). Por lo tanto, con un nivel de confianza del 95%, existe suficiente evidencia estadística para afirmar que los residuales del modelo no satisfacen el supuesto de normalidad.
\(\text{H}_0\) (Hipótesis Nula): Los residuales son homocedásticos (la varianza de los errores es constante, \(\sigma_i^2 = \sigma^2\)).
\(\text{H}_1\) (Hipótesis Alternativa): Los residuales son heterocedásticos (la varianza de los errores no es constante).
Regla de Decisión (\(\alpha = 0.05\)):
Si \(p\text{-valor} > 0.05\): No se rechaza \(\text{H}_0 \rightarrow\) Se cumple el supuesto de homocedasticidad.
Si \(p\text{-valor} \le 0.05\): Se rechaza \(\text{H}_0 \rightarrow\) Existe evidencia de heterocedasticidad.
# Prueba formal de Breusch-Pagan
prueba_bp <- bptest(modelo_edad)
print(prueba_bp)
studentized Breusch-Pagan test
data: modelo_edad
BP = 0.0004135, df = 1, p-value = 0.9838
Mediante la prueba de Breusch-Pagan se obtuvo un \(p\text{-valor} = 0.9838\). Al ser el \(p\text{-valor} > 0.05\), no existe evidencia estadística para rechazar la hipótesis nula con un nivel de confianza del 95%. Por lo tanto, se cumple satisfactoriamente el supuesto de homocedasticidad.
Para la prueba de Durbin-Watson:
Regla de Decisión (\(\alpha = 0.05\)):
# Prueba de Durbin-Watson
prueba_dw <- dwtest(modelo_edad)
print(prueba_dw)
Durbin-Watson test
data: modelo_edad
DW = 2.0333, p-value = 0.7285
alternative hypothesis: true autocorrelation is greater than 0
# Residuales en función del orden
datos_indep <- data.frame(
Orden = 1:nrow(insurance),
Residuales = residuals(modelo_edad)
)
# Gráfico de residuales seguidos por orden de observación
ggplot(datos_indep, aes(x = Orden, y = Residuales)) +
geom_point(color = "steelblue", size = 2) +
geom_line(color = "gray60", linetype = "dashed", alpha = 0.5) +
geom_hline(yintercept = 0, linetype = "dashed", color = "#C0392B", linewidth = 1) +
labs(
title = "Residuales por Orden de Observación",
subtitle = "Evaluación de Independencia de Errores",
x = "Orden de la Observación",
y = "Residuales"
) +
theme_minimal()El estadístico de Durbin-Watson es \(\text{DW} \approx 2.03\), un valor muy cercano a 2, lo que indica ausencia de autocorrelación. Al ser el \(p\text{-valor} = 0.7285 > 0.05\), no se rechaza la hipótesis nula (\(\text{H}_0\)). El gráfico de residuales por orden de observación tampoco muestra un patrón claro. Es importante señalar que, al tratarse de un conjunto de datos transversal sin estructura temporal, el orden de los registros no tiene un significado intrínseco, por lo que este resultado debe tomarse solo como una referencia.
smoker y bmi.Dado que el modelo original no cumple el supuesto de Normalidad, procedemos a aplicar la transformación de Box-Cox para encontrar la potencia \(\lambda\) que mejor normalice la variable de respuesta (charges).
# Calcular Box-Cox
bc <- boxcox(modelo_edad, lambda = seq(-2, 2, by = 0.1))En el gráfico de Log-Verosimilitud generado, el valor de \(\lambda\) que maximiza la función se encuentra en el pico de la curva (línea central punteada). Las dos líneas verticales punteadas a los lados representan el intervalo de confianza del \(95\%\) para este parámetro.
Dado que el pico de la curva se ubica ligeramente a la izquierda del cero, y el \(0\) está contenido de manera muy estrecha dentro del intervalo de confianza, la teoría sugiere que una transformación logarítmica podría ser la más adecuada.
lambda_optimo <- bc$x[which.max(bc$y)]
cat("El valor óptimo estimado de lambda es:", lambda_optimo, "\n")El valor óptimo estimado de lambda es: -0.1010101
Como este valor es muy cercano a 0, la teoría de Box-Cox recomienda utilizar la transformación de logaritmo natural.
Aplicamos la transformación matemática a la variable \(Y\) (charges) utilizando la potencia óptima encontrada y ajustamos el nuevo modelo.
# Aplicamos la transformación logarítmica (lambda ~ 0)
insurance$charges_trans <- log(insurance$charges)
# Ajustamos el nuevo modelo transformado
modelo_trans <- lm(charges_trans ~ age, data = insurance)
summary(modelo_trans)
Call:
lm(formula = charges_trans ~ age, data = insurance)
Residuals:
Min 1Q Median 3Q Max
-1.3433 -0.4166 -0.3094 0.5000 2.1999
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 7.744247 0.063336 122.27 <2e-16 ***
age 0.034545 0.001521 22.71 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.7813 on 1336 degrees of freedom
Multiple R-squared: 0.2786, Adjusted R-squared: 0.2781
F-statistic: 516 on 1 and 1336 DF, p-value: < 2.2e-16
Una vez estimado el modelo, generaremos los gráficos de diagnóstico estándar.
# Diagnóstico inicial
par(mfrow = c(2, 2))
plot(modelo_trans)# Restaurar configuración gráfica
par(mfrow = c(1, 1)) Análisis visual de los supuestos del modelo transformado (Log):
ggplot(insurance, aes(x = age, y = charges_trans)) +
geom_point(color = "#27ae60", size = 2, alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE, color = "#2c3e50", fill = "#27ae60", alpha = 0.2) +
labs(
title = "Relación entre la Edad y el Logaritmo de los Costos",
subtitle = "Modelo Transformado (Box-Cox)",
x = "Edad del beneficiario (años)",
y = "Ln(Costos médicos facturados)"
) +
theme_minimal()`geom_smooth()` using formula = 'y ~ x'
Verificaremos nuevamente los tres supuestos fundamentales: Normalidad, Homocedasticidad e Independencia, sobre los residuales del nuevo modelo para comprobar si la transformación de Box-Cox solucionó los problemas del modelo original.
Para cualquier prueba de normalidad sobre los residuales del modelo (\(\varepsilon_i\)):
Regla de Decisión (\(\alpha = 0.05\)):
# Prueba de Lilliefors para el modelo transformado
lillie_res_trans <- lillie.test(modelo_trans$residuals)
print(lillie_res_trans)
Lilliefors (Kolmogorov-Smirnov) normality test
data: modelo_trans$residuals
D = 0.26713, p-value < 2.2e-16
Como el \(p\text{-valor} < 2.2\text{e-16}\) es menor que el nivel de significancia \(\alpha = 0.05\), se rechaza la hipótesis nula (\(\text{H}_0\)). Por lo tanto, con un nivel de confianza del 95%, existe suficiente evidencia estadística para afirmar que los residuales del modelo no satisfacen el supuesto de normalidad.
\(\text{H}_0\) (Hipótesis Nula): Los residuales son homocedásticos (la varianza de los errores es constante, \(\sigma_i^2 = \sigma^2\)).
\(\text{H}_1\) (Hipótesis Alternativa): Los residuales son heterocedásticos (la varianza de los errores no es constante).
Regla de Decisión (\(\alpha = 0.05\)):
Si \(p\text{-valor} > 0.05\): No se rechaza \(\text{H}_0 \rightarrow\) Se cumple el supuesto de homocedasticidad.
Si \(p\text{-valor} \le 0.05\): Se rechaza \(\text{H}_0 \rightarrow\) Existe evidencia de heterocedasticidad.
# Prueba formal de Breusch-Pagan
prueba_bp_trans <- bptest(modelo_trans)
print(prueba_bp_trans)
studentized Breusch-Pagan test
data: modelo_trans
BP = 222.72, df = 1, p-value < 2.2e-16
Mediante la prueba Breusch-Pagan se obtuvo un \(p\text{-valor} =2.2\text{e-16}\). Como el \(p\text{-valor}< 2.2\text{e-16}\) es menor que el nivel de significancia \(\alpha = 0.05\), se rechaza la hipótesis nula (\(\text{H}_0\)). Por lo tanto, con un nivel de confianza del 95%, existe suficiente evidencia estadística para afirmar que los residuales del modelo no satisfacen el supuesto de homocedasticidad.
Para la prueba de Durbin-Watson:
Regla de Decisión (\(\alpha = 0.05\)):
prueba_dw_trans <- dwtest(modelo_trans)
print(prueba_dw_trans)
Durbin-Watson test
data: modelo_trans
DW = 2.0293, p-value = 0.7037
alternative hypothesis: true autocorrelation is greater than 0
# Residuales en función del orden
datos_indep <- data.frame(
Orden = 1:nrow(insurance),
Residuales = residuals(modelo_trans) # Actualizado al modelo transformado
)
# Gráfico de residuales seguidos por orden de observación
ggplot(datos_indep, aes(x = Orden, y = Residuales)) +
geom_point(color = "steelblue", size = 2) +
geom_line(color = "gray60", linetype = "dashed", alpha = 0.5) +
geom_hline(yintercept = 0, linetype = "dashed", color = "#C0392B", linewidth = 1) +
labs(
title = "Residuales por Orden de Observación (Modelo Transformado)",
subtitle = "Evaluación de Independencia de Errores",
x = "Orden de la Observación",
y = "Residuales"
) +
theme_minimal()El estadístico de Durbin-Watson es \(\text{DW} \approx 2.029\), un valor muy cercano a 2, lo que indica ausencia de autocorrelación. Al ser el \(p\text{-valor} = 0.7037 > 0.05\), no se rechaza la hipótesis nula (\(\text{H}_0\)). El gráfico de residuales por orden de observación tampoco muestra un patrón claro. Es importante señalar que, al tratarse de un conjunto de datos transversal sin estructura temporal, el orden de los registros no tiene un significado intrínseco, por lo que este resultado debe tomarse solo como una referencia.
Como observamos en las pruebas anteriores, el modelo transformado sigue presentando problemas de normalidad y homocedasticidad. En la regresión lineal, esto frecuentemente es causado por observaciones atípicas (outliers) o puntos altamente influyentes.
A continuación, utilizaremos los residuos estudentizados (RStudent) y la Distancia de Cook para identificar si existen pacientes o individuos en la base de datos cuyos costos facturados estén alterando drásticamente nuestro modelo.
Criterios de evaluación:
Residuos Estudentizados (RStudent): Permiten cuantificar qué tan alejada se encuentra una observación respecto al comportamiento esperado del modelo. Se considera una observación sospechosa si \(\vert{}r_i\vert{} > 2\), y una observación extrema (outlier severo) si \(\vert{}r_i\vert{} > 3\).
Distancia de Cook: Es una medida global que evalúa cuánto cambiaría el modelo si una observación fuera eliminada, combinando la magnitud del residuo y el leverage. Una observación se considera potencialmente influyente cuando supera el umbral \(D_i > 4/n\).
# 1. Parámetros de la base de datos
n <- nrow(insurance)
p <- 2 # Intercepto y pendiente (beta_0 y beta_1)
# 2. Distancia de Cook
cook_vals <- cooks.distance(modelo_trans)
umbral_cook <- 4 / (n - p)
# 3. Residuos Estudentizados (RStudent)
r_student <- rstudent(modelo_trans)
# 4. Identificar filas problemáticas
atipicos_rstudent <- which(abs(r_student) > 3)
cat("Observaciones con RStudent > 3 (Outliers severos):", length(atipicos_rstudent), "\n")Observaciones con RStudent > 3 (Outliers severos): 0
Visualizaremos gráficamente la Distancia de Cook para observar qué tan alejados están estos puntos del límite aceptable (línea roja punteada).
# Gráfico de Distancia de Cook
plot(modelo_trans, which = 4, col = "#2980b9", pch = 16,
main = "Distancia de Cook - Detección de puntos influyentes")
abline(h = umbral_cook, col = "#c0392b", lty = 2, lwd = 2)Al observar los resultados, notamos un efecto directo de la transformación logarítmica: ha eliminado los valores atípicos severos en la variable respuesta (el código arrojó 0 observaciones con \(|RStudent| > 3\)). La transformación logró “comprimir” los cobros extremos exitosamente.
Sin embargo, en el gráfico de la Distancia de Cook, es evidente que existe una gran cantidad de observaciones (picos) que superan la línea de corte roja. Esto nos indica que hay muchos pacientes que, aunque ya no son valores atípicos extremos, siguen ejerciendo una influencia desproporcionada sobre el ajuste de la recta.
En lugar de eliminarlos para “forzar” a que el modelo funcione, este hallazgo confirma la conclusión estadística central de nuestro estudio: existen grupos distintos de pacientes cuyos costos médicos no se pueden predecir únicamente con la edad.
smoker y bmi.La transformación Box-Cox actúa únicamente sobre la variable respuesta. Cuando el incumplimiento de los supuestos proviene de variables omitidas, estructuras no lineales o grupos con comportamientos distintos dentro de la población, la transformación puede ser insuficiente para corregir completamente los problemas del modelo.
Al observar los datos originales coloreados por la variable de tabaquismo, la estructura subyacente que alteraba nuestros residuales se vuelve evidente:
ggplot(insurance, aes(age, charges, color = smoker)) + geom_point(size = 2, alpha = 0.8) + theme_minimal() + labs(title = "Evidencia de variable omitida (Fumadores vs No fumadores)", x = "Edad", y = "Costos Originales")