ANÁLISIS DE REGRESIÓN VARIACIÓN AGUACATE EN FUNCIÓN DEL DOLAR
VARIACIÓN PORCENTUAL
CARGANDO DATOS
# Funciones
## Función para crear flextable
ftable <- function(x) {
x %>%
flextable() %>%
theme_vanilla() %>%
color(part = "footer", color = "#666666") %>%
color( part = "header", color = "#FFFFFF") %>%
bg( part = "header", bg = "#2c7fb8") %>%
fontsize(size = 11) %>%
font(fontname = 'Calibri') %>%
# Ajustes de ancho y tipo de alineación de las columnas
set_table_properties(layout = "autofit") %>%
# width(j=1, width = 3) %>%
align(i = NULL, j = c(2:ncol(x)), align = "right", part = "all")
}dolar <- read.csv2("datasets/Tasa_de_Cambio_Representativa_del__Mercado_-Historico.csv") %>%
clean_names()
hass <- read.xlsx("datasets/Hass_Precios_Historicos.xlsx") %>% clean_names()valor | unidad | vigenciadesde | vigenciahasta |
|---|---|---|---|
4,761.64 | COP | 22/12/22 | 22/12/22 |
4,781.28 | COP | 20/12/22 | 20/12/22 |
4,802.48 | COP | 17/12/22 | 19/12/22 |
4,836.24 | COP | 13/12/22 | 13/12/22 |
4,815.99 | COP | 10/12/22 | 12/12/22 |
4,818.32 | COP | 7/12/22 | 7/12/22 |
4,767.19 | COP | 3/12/22 | 5/12/22 |
4,815.59 | COP | 1/12/22 | 1/12/22 |
4,958.42 | COP | 22/11/22 | 22/11/22 |
5,022.03 | COP | 18/11/22 | 18/11/22 |
mercado | producto | fecha | precio_kg |
|---|---|---|---|
Bogotá, D.C., Corabastos | Aguacate hass (Semanal | Mensual) | Sabado, Junio 23 de 2012 | 3,483 |
Bogotá, D.C., Corabastos | Aguacate hass (Semanal | Mensual) | Sabado, Junio 30 de 2012 | 3,459 |
Bogotá, D.C., Corabastos | Aguacate hass (Semanal | Mensual) | Sabado, Julio 7 de 2012 | 3,478 |
Bogotá, D.C., Corabastos | Aguacate hass (Semanal | Mensual) | Sabado, Julio 14 de 2012 | 3,493 |
Bogotá, D.C., Corabastos | Aguacate hass (Semanal | Mensual) | Sabado, Julio 21 de 2012 | 3,470 |
Bogotá, D.C., Corabastos | Aguacate hass (Semanal | Mensual) | Sabado, Julio 28 de 2012 | 3,397 |
Bogotá, D.C., Corabastos | Aguacate hass (Semanal | Mensual) | Sabado, Agosto 4 de 2012 | 3,485 |
Bogotá, D.C., Corabastos | Aguacate hass (Semanal | Mensual) | Sabado, Agosto 11 de 2012 | 3,402 |
Bogotá, D.C., Corabastos | Aguacate hass (Semanal | Mensual) | Sabado, Agosto 18 de 2012 | 3,567 |
Bogotá, D.C., Corabastos | Aguacate hass (Semanal | Mensual) | Sabado, Agosto 25 de 2012 | 3,720 |
# Vamos a sacar un valor promedio de cada variable por mes
#Para el dolar vamos a tomar el campo vigenciadesde
dolar <- dolar %>%
mutate(fecha = as.Date(vigenciadesde, format = "%d/%m/%y")) %>%
mutate(mes = month(fecha), anio = year(fecha)) %>%
mutate(valor = as.double(str_replace(valor,",",""))) %>%
group_by(anio,mes) %>%
summarise(precio_dolar = mean(valor), .groups = "drop") # 🖇️ Se genera el vector de meses para usarlo más abajo con la función match.
meses <- c("Enero", "Febrero", "Marzo", "Abril", "Mayo",
"Junio", "Julio", "Agosto", "Septiembre", "Octubre",
"Noviembre", "Diciembre")
hass <- hass %>%
# Otra forma de sacar el anio.
#mutate(anio = substring(fecha,nchar(fecha)-4,nchar(fecha))) %>%
#mutate(espacio = grepl(", ",fecha))
mutate(mes = sapply(strsplit(fecha, " "), "[", 2),
anio = sapply(strsplit(fecha, " "), "[", 5)) %>%
mutate(anio = as.double(anio)) %>%
# 🖇 Se utiliza la función match con el vector meses.
mutate(mes = match(mes,meses)) %>%
group_by(anio,mes) %>%
summarise(precio_aguacate_kg = mean(precio_kg), .groups = "drop") anio | mes | precio_dolar | precio_aguacate_kg |
|---|---|---|---|
2,012 | 6 | 1,792.232 | 3,471.00 |
2,012 | 7 | 1,785.141 | 3,459.50 |
2,012 | 8 | 1,806.341 | 3,543.50 |
2,012 | 9 | 1,801.948 | 3,792.00 |
2,012 | 10 | 1,805.674 | 3,661.25 |
2,012 | 11 | 1,820.526 | 3,580.50 |
2,012 | 12 | 1,793.939 | 2,946.00 |
2,013 | 1 | 1,770.226 | 2,776.25 |
2,013 | 2 | 1,791.794 | 2,731.75 |
2,013 | 3 | 1,811.066 | 3,312.80 |
hass_dolar<- hass_dolar %>%
mutate(variacion_aguacate = (precio_aguacate_kg - lag(precio_aguacate_kg))/lag(precio_aguacate_kg),
variacion_dolar = (precio_dolar - lag(precio_dolar ))/lag(precio_dolar )) %>%
filter(!is.na(variacion_aguacate))
# dolar<- dolar%>%
# mutate(variacion_dolar= (precio_dolar- lag(precio_dolar))/lag(precio_dolar)) %>%
# filter(!is.na(variacion_dolar))
hass_dolar<- hass_dolar %>%
mutate(variacion_aguacate = (precio_aguacate_kg - lag(precio_aguacate_kg))/lag(precio_aguacate_kg)) %>%
filter(!is.na(variacion_aguacate))1.4 Diagrama de dispersión.
El gráfico de dispersión no revela claramente una tendencia positiva o
negativa entre las variables, lo que sugiere que la relación entre ellas
podría ser más compleja o menos lineal de lo que se esperaba
inicialmente.
1.5 Generando un modelo súper simplificado (modelo ingenuo)
media_aguacate <- mean(hass_dolar$variacion_aguacate)
ggplot(data = hass_dolar, aes(x = variacion_dolar, y = variacion_aguacate)) +
geom_point() +
geom_hline(yintercept = media_aguacate, color = "red", linetype = "dashed") +
annotate("text", x = max(hass_dolar$variacion_dolar), y = media_aguacate,
label = paste("Valor medio del Aguacate:", round(media_aguacate, 4)),
hjust = 1, vjust = 0, color = "red") +
labs(title = "Modelo Ingenuo", x = "Variación del Dólar", y = "Variación del Aguacate") +
theme_minimal()
En la gráfica presentada, se observa una línea horizontal que representa
el promedio de la variación del aguacate. Teniendo en cuenta que esta
propuesta no considera la relación entre la variación del precio del
aguacate y su variable explicativa (variación del precio del dolar), no
es posible realizar predicciones empleando la presente propuesta, por lo
que se debe considerar la mejora sustancial del modelo.
1.6 Calidad de ajuste del modelo ingenuo
Se aplica el siguiente método que permita hallar el desajute del modelo ingenuo.
\[MSE_{\text{ingenuo}} = \color{red}{\frac{1}{n}}\sum^{n}_{i=1}(y_i - \bar{y})²\]
Es decir, nuestra medida de desajuste será el promedio de la suma de las diferencias cuadradas de cada dato \(y_i\) respecto a su media \(\bar{y}\), dicho de otra manera la varianza de la variable \(Y\) a predecir!!!!
n <- length(hass_dolar$variacion_aguacate)
MSE_ingenuo <- (1/n) * sum((hass_dolar$variacion_aguacate - mean(hass_dolar$variacion_aguacate))^2)
MSE_ingenuo## [1] 0.006495478
A continuaciòn, se presenta el gràfico que ilustra las distancias que hay entre cada dato \(y_i\) observado y el promedio histórico (valor predicho \(\bar{y}\) por el modelo ingenuo)
hass_dolar %>%
ggplot(aes(x = variacion_dolar, y = variacion_aguacate)) +
geom_point() +
geom_hline(yintercept = mean(hass_dolar$variacion_aguacate), color = "red") +
geom_segment(aes(xend = variacion_dolar, yend = mean(hass_dolar$variacion_aguacate)), col = 'red', linetype = 'dashed') +
labs(title = "Distancia entre cada dato observado y el promedio histórico", x = "Capital", y = "FNCER") +
theme_light()1.7 Introducción a las medidas de ajuste para modelos no ingenuos
A continuación, se presenta un posible modelo ajustado usando valores \(\beta_0=-0.002\) y \(\beta_1 = 0.007\). Se eligieron los valores por inspección visual y prueba y error:
b0 <- 0.002
b1 <- 0.007
# Predicciones
x <- hass_dolar$variacion_dolar
y_pred <- b0 + b1 * x
# Gráfica
hass_dolar %>%
ggplot(aes(x =variacion_dolar , y = variacion_aguacate)) +
geom_point() +
geom_line(aes(x = x, y = y_pred), col = "red") +
labs(x = "Variación_Dolar", y = "Variación_aguacate") +
theme_light()1.8 Una animación que ilustra la estimación de betas
# Extraemos las variables de interés
x = hass_dolar$variacion_dolar
y = hass_dolar$variacion_aguacate
# Calculamos medias muestrales para las variables X y Y
x_barra <- mean(x)
y_barra <- mean(y)
# Definimos posibles valores para b1, y por consiguiente para b0
b1_values <- seq(-1, 0.5, by = 0.08)
b0_values <- y_barra - b1_values * x_barra
# Fijamos límites en X y Y para cada gráfico
xmin = min(x) - 0
xmax = max(x) + 1
ymin = min(y) - 0
ymax = max(y) + 1
# Construimos una función para calcular el MSE en función de x_i, y_i, b1 y b0
MSE <- function(x, y, b0, b1) {
errors <- y - (b0 + b1*x)
squared_errors <- errors^2
mse <- mean(squared_errors)
return(mse)
}
# Crear un dataframe para almacenar los resultados
list_mse <- vector()
# Calcular el MSE para diferentes valores de B1
for (i in 1:length(b1_values)) {
mse <- MSE(x, y, b0_values[i], b1_values[i])
list_mse[i] <- mse
}
mse_df = data.frame(
B0 = b0_values,
B1 = b1_values,
mse = list_mse,
color_point = 'green'
)
# Capturando el renglón donde ocurre el mínimo mse
pos_min <- which(mse_df$mse==min(mse_df$mse))
# Impriendo las filas cercanas al mínimo
mse_df$color_point[(pos_min - 4):(pos_min + 4)] <- rep('red',9)
ftable(mse_df[(pos_min - 8):(pos_min + 8), ])B0 | B1 | mse | color_point |
|---|---|---|---|
0.011268624 | -0.52 | 0.006925731 | green |
0.010723051 | -0.44 | 0.006818214 | green |
0.010177477 | -0.36 | 0.006725723 | green |
0.009631904 | -0.28 | 0.006648260 | green |
0.009086330 | -0.20 | 0.006585824 | red |
0.008540757 | -0.12 | 0.006538415 | red |
0.007995183 | -0.04 | 0.006506033 | red |
0.007449610 | 0.04 | 0.006488679 | red |
0.006904036 | 0.12 | 0.006486352 | red |
0.006358462 | 0.20 | 0.006499052 | red |
0.005812889 | 0.28 | 0.006526779 | red |
0.005267315 | 0.36 | 0.006569534 | red |
0.004721742 | 0.44 | 0.006627316 | red |
Ahora graficaremos una curva que muestre esta relación:
if (!file.exists("beta_vs_mse.gif")){
# Crear la animación
animation <- ggplot(mse_df, aes(B1, mse)) +
geom_line(aes( x= B1, y = mse, color = color_point), linetype = "dashed") +
geom_point(aes(color = color_point), size = 3) +
labs(title = "Valores de MSE para cada posible B1, respetando E(e) = 0") +
geom_text(data = mse_df,
aes(label = paste("MSE: ", round(mse,0))), x= 0.5, y = 0.5, color='black'
) +
geom_text(data = mse_df,
aes(label = paste("MSE mínimo: ", round(min(mse),0))),
x= 1, y = min(mse_df$mse) - 1e5, color='red') +
scale_color_manual(values = c("red" = "red", "green" = "green"),
labels = c("Subóptima", "Óptima"),
name = "Soluciones") +
theme_minimal() +
transition_reveal(B1)
animate(animation, duration = 10, fps = 2, renderer = gifski_renderer())
anim_save("beta_vs_mse.gif")
}if (!file.exists("beta_estimation.gif")){
# Las pausas en la animación son fotogramas con información repetida correspondiente
# al renglón óptimo
fotogramas_pausa <- data.frame(
B0 = rep(b0_values[pos_min], 2*nrow(mse_df)),
B1 = rep(b1_values[pos_min], 2*nrow(mse_df)),
mse = rep(list_mse[pos_min], 2*nrow(mse_df)),
color_point = rep('red',2*nrow(mse_df))
)
mse_df <- rbind(mse_df, fotogramas_pausa)
mse_df <- mse_df[order(mse_df$B1), ]
mse_df$estado <- seq(1,nrow(mse_df))
# Construimos el dataframe de predicciones para cada estado, con el cual poder
# graficar los residuales en cada fotograma
df_predicciones <- data.frame(B0_pred=numeric(0),
B1_pred=numeric(0),
x_from_pred=numeric(0),
y_from_pred=numeric(0),
y_pred_mod=numeric(0),
estado = numeric(0))
for (i in 1:nrow(mse_df)){
df_nuevo <-data.frame(
BO_pred = rep(mse_df[i, 'B0'], length(x)),
B1_pred = rep(mse_df[i, 'B1'], length(x)),
x_from_pred = x,
y_from_pred = y,
y_pred_mod = mse_df[i, 'B0'] + mse_df[i,'B1']*x,
estado = rep(mse_df[i, 'estado'], length(x))
)
df_predicciones <- rbind(df_predicciones, df_nuevo)
}
w <- data.frame(df_predicciones)
# Creamos el gráfico base
base_plot <- ggplot(data.frame(x = x, y = y), aes(x = x, y = y)) +
geom_point() +
geom_point(x=mean(x), y=mean(y), color='green', size=2.5) +
geom_abline(aes(slope = 0, intercept = y_barra),
linetype = "dashed", color = "blue") +
labs(title = "Estimación de Betas por Minimización del MSE") +
theme_minimal()
# Creamos la animación
animation <- base_plot +
geom_abline(aes(slope = B1, intercept = B0, color = color_point), data=mse_df) +
geom_text(aes(label = paste("MSE: ", round(mse,0))), x= 0.5, y = 0.5, data=mse_df,
color = "red")+
geom_segment(data = df_predicciones,
aes(x=x_from_pred, y=y_from_pred, xend=x_from_pred, yend=y_pred_mod),
col = "violet", lty='dashed') +
transition_states(estado, transition_length = 2, state_length = 1) +
scale_color_manual(values = c("red" = "red", "green" = "green"),
labels = c("Subóptima", "Óptima"),
name = "Rectas regresoras") +
enter_fade() +
exit_fade()
# Guardamos la animación en un archivo GIF
animate(animation, duration = 20, fps = 2, renderer = gifski_renderer())
anim_save("beta_estimation.gif", animation)
}## [1] 0.09238873
## [1] 0.007092336
## [1] 0.006585233
# Confirmamos usando la función lm, a continuación la documentación
# lm(formula, data, subset, weights, na.action,
# method = "qr", model = TRUE, x = FALSE, y = FALSE, qr = TRUE,
# singular.ok = TRUE, contrasts = NULL, offset, ...)
mod1 <- hass_dolar %>% lm(variacion_aguacate ~ variacion_dolar,.)
print(mod1$coefficients)## (Intercept) variacion_dolar
## 0.007092336 0.092388729
## [1] 0.08114945
parametros <- data.frame(metodo = c("manual","lm"),
beta_0 = c(b0,mod1$coefficients[1]),
beta_1 = c(b1,mod1$coefficients[2]),
sigma_2 = c(sigma_2, (resumen$sigma)^2))
parametros %>%
ftable()metodo | beta_0 | beta_1 | sigma_2 |
|---|---|---|---|
manual | 0.007092336 | 0.09238873 | 0.006585233 |
lm | 0.007092336 | 0.09238873 | 0.006585233 |
De acuerdo con la tabla antes expuesta, se comprueba que ambos métodos (manual y librería lm de R studio) permiten llegar a la misma respuesta B0=0.007092336; B1=0.09238876 y sigma_2= 0.006585233
El modelo de regresión lineal es de la forma: Y = b0 + b1X + ε, donde Y es la variable de respuesta (en este caso, variación del precio del aguacate), X es la variable predictora (variación del precio del dolar), b0 es la intersección (o intercepto), b1 es el coeficiente de la variable predictora, y ε es el error aleatorio.
Los resultados del modelo indican que la intersección (intercepto) es aproximadamente 0.007092336 y el coeficiente asociado a la variable predictora “variación del precio del dolar” es aproximadamente 0.09238876. Esto significa que, la relación entre la variación del precio del dolar y la variación del precio del aguacate es positiva. En otras palabras, a medida que el precio del dolar aumenta, el precio del aguacate también tiende a aumentar. Por cada unidad adicional de variación del precio del dolar, se estima que la variación del precio del aguacate aumentará en 0.09238876 unidades.
2. COMPROBACIÓN DEL SUPUESTO DE NORMALIDAD.
# Paso 1: Ordenamos los datos de menor a mayor
n <- length(mod1$residuals)
df_residuals <- data.frame(residuals = mod1$residuals)
df_residuals <- df_residuals %>%
arrange(residuals)
# Paso 2: Estimamos los parámetros para la distribución normal teórica a partir de los datos
mean_residuals <- mean(df_residuals$residuals)
sd_residuals <- sd(df_residuals$residuals)
# Paso 3: Calculamos la función acumulada para cada valor de residual
cdf_teorica <- pnorm(df_residuals$residuals,
mean = mean_residuals,
sd = sd_residuals)
# Paso 4: Calculamos la máxima diferencia absoluta entre la densidad empírica y la teórica
# Método 1: Construir la función empírica usando fórmulas de R (ecdf) y luego tomar diferencias
# absolutas entre valor empírico y teórico (enfoque KS)
cdf_empirica <- ecdf(df_residuals$residuals)
diferencias_absolutas <- abs(cdf_empirica(df_residuals$residuals) - cdf_teorica)
pos_estadistico_m1 <- which.max(diferencias_absolutas)
estadistico_m1 <- diferencias_absolutas[pos_estadistico_m1]
print(paste0("Este es el valor del estadístico: ", estadistico_m1," y esta es la posición en ",
"la que ocurre la máxima diferencia ", pos_estadistico_m1))## [1] "Este es el valor del estadístico: 0.0543964322112345 y esta es la posición en la que ocurre la máxima diferencia 81"
# Método 2: Construir manualmente los cálculos que permiten hallar la máxima diferencia
# entre la empírica y la teórica usando la definición de la empírica como
# función a trozos 📈
e_plus <- seq(1:n)/n
e_minus <- seq(0:(n-1))/n
D_plus <- e_plus - cdf_teorica
D_minus <- cdf_teorica - e_minus
pos_max_d_plus <- which.max(D_plus)
pos_max_d_minus <- which.max(D_minus)
estadistico_m2 <- max(D_plus[pos_max_d_plus], D_minus[pos_max_d_minus])
pos_estadistico_m2 <- function() {
if(max(D_plus)>=max(D_minus)) {
return(pos_max_d_plus)
}else{
return(pos_max_d_minus)
}
}
print(paste0("Este es el valor del estadístico: ", estadistico_m2," y esta es la posición en ",
"la que ocurre la máxima diferencia ", pos_estadistico_m2()))## [1] "Este es el valor del estadístico: 0.0543964322112345 y esta es la posición en la que ocurre la máxima diferencia 81"
# Comprobamos los resultados invocando la función de R que realiza estos cálculos automáticos
Lilliefors_test <- lillie.test(df_residuals$residuals)
print(Lilliefors_test)##
## Lilliefors (Kolmogorov-Smirnov) normality test
##
## data: df_residuals$residuals
## D = 0.054396, p-value = 0.4413
estadistico_l <- Lilliefors_test$statistic
print(paste0("Este es el estadístico de Lilliefors usando paquetería de R: ", estadistico_l))## [1] "Este es el estadístico de Lilliefors usando paquetería de R: 0.0543964322112345"
La prueba de normalidad de Lilliefors (Kolmogorov-Smirnov) se utiliza para evaluar si los residuales del modelo de regresión se distribuyen de manera normal. Esto es importante porque muchos modelos de regresión asumen que los residuales siguen una distribución normal, sin embargo, el resultado de la prueba muestra que el valor de D (estadístico de prueba de Lilliefors) es aproximadamente 0.054396, y el valor p es aproximadamente 0.4413, como el valor p es mayor que un nivel de significancia previamente establecido (como 0.05), se acepta la hipótesis nula de que los residuales siguen una distribución normal. En este caso, el valor p es mayor que 0.05, lo que sugiere que hay evidencia para afirmar que los residuales sí siguen una distribución normal 😀😎.
x_seg <- df_residuals$residuals[pos_estadistico_m1]
y_sup_seg <- cdf_teorica[pos_estadistico_m1]
y_inf_seg <- cdf_empirica(x_seg)
cat("x_seg: ", format(x_seg, digits=5),
" y_inf_seg: ", format(y_inf_seg, digits=5),
" y_sup_seg: ", format(y_sup_seg, digits=5),
sep='')## x_seg: 0.012048 y_inf_seg: 0.61364 y_sup_seg: 0.55924
datos_para_grafico <- data.frame(
residuales = df_residuals$residuals
)
ggplot(datos_para_grafico, aes(x = residuales)) +
# geom_density(color = "blue", fill = "lightblue", alpha = 0.5) +
stat_function(fun = pnorm, args = list(mean = mean_residuals,
sd = sd_residuals),
color = "red", size = 1) +
stat_ecdf(color = "green", size = 1) +
geom_segment(aes(x = x_seg, y = y_inf_seg, xend = x_seg,
yend = y_sup_seg),
color = "purple", size = 1, linetype = "solid") +
geom_point(aes(x = x_seg, y = y_inf_seg),
color = "purple",size = 3) +
geom_point(aes(x = x_seg, y = y_sup_seg),
color = "purple", size = 3) +
annotate("text", x = x_seg , y = (y_inf_seg + y_sup_seg)/2,
label = paste0("Máxima Separación=",
format(estadistico_m1,
digits=5)," ", sep=''),
color = "purple", size = 3, hjust = -0.05, vjust = 0) +
coord_cartesian(xlim = c(min(datos_para_grafico$residuales),
max(datos_para_grafico$residuales))) +
xlab("Residuales del modelo") +
ylab("Probabilidad acumulada") +
labs(title = "Esquema de funcionamiento de la prueba de Lilliefors") +
theme_minimal()#Prueba Shapiro-Wilk
##
## Shapiro-Wilk normality test
##
## data: mod1$residuals
## W = 0.98723, p-value = 0.259
##
## Asymptotic one-sample Kolmogorov-Smirnov test
##
## data: mod1$residuals
## D = 0.41984, p-value < 0.00000000000000022
## alternative hypothesis: two-sided
Pruebas de Normalidad:
La prueba de normalidad Shapiro-Wilk y la prueba de Kolmogorov-Smirnov se utilizan para evaluar si los residuales del modelo de regresión se distribuyen de manera normal. Esto es importante porque muchos modelos de regresión asumen que los residuales siguen una distribución normal.
Resultados de la prueba Shapiro-Wilk:
El valor de W (estadístico de prueba de Shapiro-Wilk) es aproximadamente 0.98723, y el valor p es aproximadamente 0.259 y el valor p es mayor que el nivel de significancia previamente establecido (como 0.05), entonces se acepta la hipótesis nula de que los residuales siguen una distribución normal. En este caso, el valor p es mayor que 0.05, lo que sugiere que hay evidencia para afirmar que los residuales Sí siguen una distribución normal.
Resultados de la prueba de Kolmogorov-Smirnov:
El valor de D (el estadístico de prueba de Kolmogorov-Smirnov) es aproximadamente 0.41984, mientras que el valor p es demasiado bajo, aproximadamente 0.00000000000000022, lo que se acerca prácticamente a cero.
A diferencia de la prueba de Shapiro-Wilk, cuando el valor p es menor que un nivel de significancia previamente establecido, podemos rechazar la hipótesis nula que sostiene que los residuales siguen una distribución normal. En este caso, el valor p es excepcionalmente pequeño, lo que sugiere que los residuales no siguen una distribución normal. Sin embargo, la discrepancia entre la prueba de Shapiro-Wilk y la de Kolmogorov-Smirnov puede deberse a la minuciosidad con la que esta última lleva a cabo la prueba, lo que podría señalar falta de normalidad incluso ante la más mínima desviación de dicha distribución.
3. EL SUPUESTO DE INDEPENDENCIA DEL ERROR
# Gráfico de residuales vs valores ajustados
hass_dolar %>%
ggplot(aes(x= predicciones, y=residuales)) +
geom_point()+
theme_light()+
ylab("Residuales") +
xlab("Valor predicho")# Gráfico de residuales con índice temporal
hass_dolar %>%
ggplot(aes(x= variacion_dolar, y=variacion_aguacate)) +
geom_point()+
geom_line(aes(y = residuales, color = "residuales"), size = 1)+
theme_light()+
xlab("capital")+
ylab("Residuales")# Prueba de Durbin-Watson tomando el mismo orden en el que venían los
# datos en el dataframe puesto que este orden se corresponde con el
# orden temporal.
resultado_dw <- dwtest(mod1)
print(resultado_dw)##
## Durbin-Watson test
##
## data: mod1
## DW = 1.6008, p-value = 0.01003
## alternative hypothesis: true autocorrelation is greater than 0
# Prueba de Ljung-Box
# Como esperamos cierta estacionalidad intra año en los datos podría tener sentido esperarla también para los residuales. Por lo tanto eligiremos lag = 1,2,3,...,12
p_values <- vector()
for (i in 1:12) {
resultado_ljung_box <- Box.test(mod1$residuals, lag = i,
type = "Ljung-Box")
p_values[i] <-resultado_ljung_box$p.value
}
resultados_lb <- data.frame(
lag = seq(1,12),
p_value = p_values
)
print(resultado_ljung_box)##
## Box-Ljung test
##
## data: mod1$residuals
## X-squared = 84.736, df = 12, p-value = 0.0000000000005116
## lag p_value
## 1 1 0.0212212849984015905
## 2 2 0.0066591599036169846
## 3 3 0.0000007699842595743
## 4 4 0.0000005164588542383
## 5 5 0.0000016606826340748
## 6 6 0.0000002945760799733
## 7 7 0.0000008167597805864
## 8 8 0.0000006222160334746
## 9 9 0.0000000023576409713
## 10 10 0.0000000050131288010
## 11 11 0.0000000030342633961
## 12 12 0.0000000000005115908
Durbin-Watson test:
Esta prueba se utiliza para verificar la autocorrelación de los residuos en un modelo de regresión. La estadística de Durbin-Watson (DW) evalúa si hay autocorrelación positiva (valores cercanos a 0) o autocorrelación negativa (valores cercanos a 4) en los residuos. En tu caso, la estadística DW es 1.6008, lo que está cerca de 2. Esto sugiere que no hay una autocorrelación significativa en los residuos del modelo.
Box-Ljung test:
Esta prueba se utiliza para evaluar si hay autocorrelación en los residuos de un modelo de regresión en múltiples rezagos (lags). La estadística X-cuadrado se compara con una distribución chi-cuadrado y su correspondiente valor p para determinar si hay evidencia de autocorrelación en los residuos.
La estadística X-cuadrado es 84.736 con 12 grados de libertad y un valor p de 0.0000000000005116. Un valor p muy bajo, cercano a cero (como en este caso) sugiere que hay evidencia significativa de autocorrelación en los residuos a lo largo de múltiples rezagos.
Conclusión: Según las pruebas aplicadas, y teniendo en cuenta que la prueba de Durbin Watson aprueba y que la prueba de Ljung Box no, pueden indicar que no existe autocorrelación significativa en el modelo con el residuo inmediatamente anterior, algo que se comprueba mediante el siguiente gráfico:
# Residuales estandarizados vs valores predichos
hass_dolar %>%
ggplot(aes(x= predicciones, y=residuales/sd(residuales))) +
geom_point()+
theme_light()+
ylab("Residuales estandarizados") +
xlab("Valor predicho fncer")# Residuales estandarizados vs fncer
hass_dolar%>%
ggplot(aes(x= variacion_dolar, y=residuales/sd(residuales))) +
geom_point()+
theme_light()+
ylab("Residuales estandarizados") +
ylab("capital")# Pruebas estadísticas formales
resultado_breusch_pagan <- bptest(mod1)
print(resultado_breusch_pagan)##
## studentized Breusch-Pagan test
##
## data: mod1
## BP = 0.60265, df = 1, p-value = 0.4376
Si el valor p es alto (en este caso, p = 0.4376), no hay evidencia significativa para rechazar la hipótesis nula de homocedasticidad. En otras palabras, los datos sugieren que la varianza de los residuos es constante y no varía de manera heterocedástica en función de las variables independientes. Conclusión: según el resultado del “studentized Breusch-Pagan test”, no hay evidencia de heterocedasticidad en los residuos del modelo de regresión.
resultado_white <- bptest(mod1,
varformula = ~ (hass_dolar$variacion_dolar + I(hass_dolar$variacion_dolar^2)),
data = hass_dolar)
print(resultado_white)##
## studentized Breusch-Pagan test
##
## data: mod1
## BP = 0.70038, df = 2, p-value = 0.7046
El resultado del test Breusch-Pagan (también conocido como el test de White) se utiliza para evaluar la presencia de heterocedasticidad en un modelo de regresión. La heterocedasticidad se refiere a la variabilidad no constante de los errores en un modelo de regresión, lo que significa que la varianza de los errores no es la misma en todos los niveles de las variables predictoras.
BP = 0.70038 es el estadístico de prueba de Breusch-Pagan. df = 2 son los grados de libertad del estadístico de prueba. p-value = 0.7046 es el valor p asociado al estadístico de prueba.
Hipótesis nula (H0): No hay heterocedasticidad en el modelo (la varianza de los errores es constante). Hipótesis alternativa (H1): Hay evidencia de heterocedasticidad en el modelo (la varianza de los errores no es constante).
Dado que el valor p (0.7046) es mayor que el nivel de significancia = 0.05, tenemos evidencia más que suficiente para aceptar la hipótesis nula.
Conclusión: según los resultados del test Breusch-Pagan, no hay indicios de heterocedasticidad en tu modelo de regresión, ya que el valor p es mayor que el nivel de significancia seleccionado. Esto sugiere que la suposición de homocedasticidad (varianza constante de los errores) es razonable.
Para ilustrar como trabajan estas pruebas por debajo, considere la siguiente implementación manual:
# Implementación manual del test de White
#1. Construir el dataframe que relaciona el cuadrado del error con
#los términos lineales de la predictora y el término cuadrático
#(por ser modelo lineal simple no hay productos cruzados)
# 1. Crear un dataframe con el cuadrado del error y las variables del modelo
df_squared_error <- data.frame(
squared_error = mod1$residuals^2,
capital = hass_dolar$variacion_dolar,
capital_2 = hass_dolar$variacion_dolar^2
)
# 2. Ajustar un modelo lineal sobre el cuadrado del error regresado por el término lineal y cuadrático de la variable capital
modelo_white <- lm(data = df_squared_error,
squared_error ~ capital + capital_2)
# 3. Calcular el estadístico de prueba como n * R^2 (con n = número de datos del dataframe de errores)
R2 <- summary(modelo_white)$r.squared
n <- nrow(df_squared_error)
w_estadistico <- n * R2
# 4. Calcular el valor p utilizando la distribución chi-cuadrado con 2 grados de libertad
p_valor <- 1 - pchisq(w_estadistico, df = 2)
# Imprimir el estadístico de prueba y el valor p
paste("BP =", round(w_estadistico, 4), "y p_valor =", round(p_valor, 5))## [1] "BP = 0.7004 y p_valor = 0.70455"
En la salida impresa puede notar que los resultados son consistentes con lo generado por la prueba de heterocedasticidad arrojada con la función bptest.
# 1. Crear un dataframe con el cuadrado del error y las variables del modelo
df_squared_error <- data.frame(
squared_error = mod1$residuals^2,
capital = hass_dolar$variacion_dolar,
capital_2 = hass_dolar$variacion_dolar^2
)
# 2. Ajustar un modelo lineal sobre el cuadrado del error regresado por el término lineal y cuadrático de la variable capital
modelo_white <- lm(data = df_squared_error,
squared_error ~ capital + capital_2)
# 3. Calcular el valor estimado del error cuadrado
estimated_squared_error <- modelo_white$coefficients[1] +
modelo_white$coefficients[2] * df_squared_error$capital +
modelo_white$coefficients[3] * df_squared_error$capital_2
# 4. Crear un gráfico
library(ggplot2)
p <- ggplot(data = df_squared_error, aes(x = capital, y = squared_error)) +
geom_point() +
geom_line(aes(y = estimated_squared_error, color = "red")) +
labs(
title = "Modelo white sobre cuadrados del error",
subtitle = paste(
"R² =", round(R2, 4),
"W-Estadístico =", round(w_estadistico, 4),
"p_valor =", round(p_valor, 5)
)
) +
scale_color_manual(
values = c("red" = "blue"),
labels = c("Error cuadrado estimado"),
name = "Modelo de White"
)
print(p)
Para ilustrar como trabajan estas pruebas por debajo, considere la
siguiente implementación manual:
# Implementación manual del test de White
#1. Construir el dataframe que relaciona el cuadrado del error con
#los términos lineales de la predictora y el término cuadrático
#(por ser modelo lineal simple no hay productos cruzados)
# 1. Crear un dataframe con el cuadrado del error y las variables del modelo
df_squared_error <- data.frame(
squared_error = mod1$residuals^2,
capital = hass_dolar$variacion_dolar,
capital_2 = hass_dolar$variacion_dolar^2
)
# 2. Ajustar un modelo lineal sobre el cuadrado del error regresado por el término lineal y cuadrático de la variable capital
modelo_white <- lm(data = df_squared_error,
squared_error ~ capital + capital_2)
# 3. Calcular el estadístico de prueba como n * R^2 (con n = número de datos del dataframe de errores)
R2 <- summary(modelo_white)$r.squared
n <- nrow(df_squared_error)
w_estadistico <- n * R2
# 4. Calcular el valor p utilizando la distribución chi-cuadrado con 2 grados de libertad
p_valor <- 1 - pchisq(w_estadistico, df = 2)
# Imprimir el estadístico de prueba y el valor p
paste("BP =", round(w_estadistico, 4), "y p_valor =", round(p_valor, 5))## [1] "BP = 0.7004 y p_valor = 0.70455"
En la salida impresa puede notar que los resultados son consistentes con lo generado por la prueba de heterocedasticidad arrojada con la función bptest.
# 1. Crear un dataframe con el cuadrado del error y las variables del modelo
df_squared_error <- data.frame(
squared_error = mod1$residuals^2,
capital = hass_dolar$variacion_dolar,
capital_2 = hass_dolar$variacion_dolar^2
)
# 2. Ajustar un modelo lineal sobre el cuadrado del error regresado por el término lineal y cuadrático de la variable capital
modelo_white <- lm(data = df_squared_error,
squared_error ~ capital + capital_2)
# 3. Calcular el valor estimado del error cuadrado
estimated_squared_error <- modelo_white$coefficients[1] +
modelo_white$coefficients[2] * df_squared_error$capital +
modelo_white$coefficients[3] * df_squared_error$capital_2
# 4. Crear un gráfico
library(ggplot2)
p <- ggplot(data = df_squared_error, aes(x = capital, y = squared_error)) +
geom_point() +
geom_line(aes(y = estimated_squared_error, color = "red")) +
labs(
title = "Modelo white sobre cuadrados del error",
subtitle = paste(
"R² =", round(R2, 4),
"W-Estadístico =", round(w_estadistico, 4),
"p_valor =", round(p_valor, 5)
)
) +
scale_color_manual(
values = c("red" = "blue"),
labels = c("Error cuadrado estimado"),
name = "Modelo de White"
)
print(p)
CONCLUSIONES GENERALES
Análisis del Modelo de Regresión Lineal Múltiple: El modelo de regresión lineal múltiple revela una relación estadísticamente significativa entre la variable dependiente, es decir, la Variación del Precio del Aguacate, y la variable independiente, que es la Variación del Precio del Dólar. Los coeficientes del modelo señalan que, en promedio, un incremento en la variación del precio del dólar está asociado con una variación en el precio del aguacate. Sin embargo, es importante notar que la magnitud de este efecto es relativamente modesta. Esta observación sugiere que puede haber otras variables no incluidas en el modelo que podrían ofrecer una explicación más completa de la variabilidad en el precio del aguacate. Por tanto, es plausible considerar la influencia de otras variables adicionales para obtener una comprensión más detallada de la variación en el precio del aguacate.
Normalidad de los Residuos: Los resultados de las pruebas de normalidad de los residuos (Lilliefors y Shapiro-Wilk) sugieren que los residuos del modelo siguen una distribución normal, aunque la prueba de Kolmogorov Smirnov indica una violación en el supuesto de normalidad, esto se puede atribuir a la rigurosidad de la prueba.
Autocorrelación de los Residuos: El resultado del test de Durbin-Watson sugiere que los residuos del modelo no muestran autocorrelación positiva significativa, aunque la prueba de Ljung Box parecen indicar autocorrelación en múltiples rezagos, lo que nos llevaría nos puede llevar a estudiar el fenómeno de precios con mayor detalle o replantear el uso de dicha prueba en un modelo de regresión lineal.
Homocedasticidad de los Residuos: El resultado del test de Breusch-Pagan no muestra evidencia significativa de heteroscedasticidad en los residuos. Esto indica que la varianza de los errores parece ser constante en diferentes niveles de las variables independientes.
Modelo de White: El modelo de White indica que no existe Heterocedasticidad en la variación de los residuos.
Análisis de Gráficos: Los gráficos generados muestran la relación entre las variables y cómo se ajusta el modelo. Los puntos dispersos y la línea de regresión en los gráficos ayudan a visualizar cómo se relacionan las variables.