ANÁLISIS DE REGRESIÓN VARIACIÓN AGUACATE EN FUNCIÓN DEL DOLAR

VARIACIÓN PORCENTUAL

UTP

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()
dolar %>% 
  head(10)%>% 
  ftable()

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

hass%>% 
  head(10)%>% 
  ftable() 

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") 
hass_dolar <- dolar %>% 
  right_join(hass, by = c("mes","anio")) 

hass_dolar %>% 
  head(10) %>% 
  ftable()

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.

hass_dolar %>% 
  ggplot(aes(x=variacion_dolar, y= variacion_aguacate )) +
  geom_point()+ theme_light()

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")
}

Img 1: Relación entre B1 y MSE
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)
}

Img 1: Estimación de betas por minimización de MSE
# b1 = cov(x,y) / (sigma_x)²
b1 <- cov(x,y) / var(x)
print(b1)
## [1] 0.09238873
# bo = y_barra - b1*x_barra
b0 <- mean(y) - b1*mean(x)
print(b0)
## [1] 0.007092336
residuales <- y - (b0 + b1*x)
n <- length(x)

sigma_2 <- sum(residuales^2)/(n-2)
print(sigma_2)
## [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
resumen <-  summary(mod1)
resumen$sigma
## [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

# Probando normalidad del error. 

shapiro.test(mod1$residuals)
## 
##  Shapiro-Wilk normality test
## 
## data:  mod1$residuals
## W = 0.98723, p-value = 0.259
ks.test(mod1$residuals, "pnorm")
## 
##  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.

# Primero inspeccionamos las variables individuales para observar su
# comportamiento temporal en un mismo gráfico

# library(lubridate)
x <- hass_dolar$variacion_dolar
y <- hass_dolar$variacion_aguacate


hass_dolar$predicciones <- mod1$fitted.values
hass_dolar$residuales <-mod1$residuals

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
print(resultados_lb)
##    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.