# Introducción

## Estado del arte

Consulte @Luque2023 ...



```{r}
#| label: setup33
#| include: false

# Configuración para Mac con Apple Silicon
if (Sys.info()["sysname"] == "Darwin") {
  options(bitmapType = "cairo")
}

# Configuración de knitr
knitr::opts_chunk$set(
  echo = TRUE,
  warning = FALSE,
  message = FALSE,
  fig.width = 7,
  fig.height = 5,
  #dev = "ragg_png",
  dpi = 300
)

# Cargar librerías necesarias
if (!require("ragg")) install.packages("ragg")
if (!require("ggplot2")) install.packages("ggplot2")
if (!require("forecast")) install.packages("forecast")
if (!require("knitr")) install.packages("knitr")
if (!require("patchwork")) install.packages("patchwork")

library(ragg)
library(ggplot2)
library(forecast)
library(knitr)
library(patchwork)
```

# Introducción y Fundamentos Teóricos {#sec-intro}

## Introducción General

El análisis de series temporales constituye una herramienta fundamental en el estudio de fenómenos que evolucionan a lo largo del tiempo. Según @box2015time, una serie temporal puede definirse como una colección de observaciones ordenadas cronológicamente. Este trabajo presenta un análisis exhaustivo de los métodos estadísticos aplicados al estudio de series temporales, con especial énfasis en los modelos ARIMA.

## Fundamentos Matemáticos {#sec-fundamentos}

### Definiciones Básicas

::: {#def-proceso-estocastico}

## Proceso Estocástico

Un proceso estocástico ${X_t}_{t \in T}$ es una colección de variables aleatorias indexadas por un conjunto $T$, donde cada $X_t$ está definida sobre un espacio de probabilidad $(\Omega, \mathcal{F}, P)$.
:::



Como se establece en la @def-proceso-estocastico, un proceso estocástico generaliza el concepto de variable aleatoria a una familia de variables indexadas temporalmente.

### Propiedades de Estacionariedad

::: {#thm-wold}
## Descomposición de Wold

Todo proceso estacionario en covarianza ${X_t}$ puede expresarse como:
$$X_t = \mu + \sum_{j=0}^{\infty} \psi_j \epsilon_{t-j}$$
donde $\epsilon_t$ es ruido blanco y $\sum_{j=0}^{\infty} \psi_j^2 < \infty$.
:::

El @thm-wold, demostrado originalmente por @wold1938study, establece la base teórica para la representación de medias móviles infinitas.

## Análisis Exploratorio de Datos

### Visualización Inicial

```{r}
#| label: fig-serie-temporal
#| fig-cap: "Serie temporal simulada con tendencia y estacionalidad"
#| fig-width: 8
#| fig-height: 5

# Simulación de una serie temporal
set.seed(123)
n <- 100
t <- 1:n
tendencia <- 0.5 * t
estacionalidad <- 10 * sin(2 * pi * t / 12)
ruido <- rnorm(n, 0, 5)
serie <- tendencia + estacionalidad + ruido

# Crear dataframe
datos <- data.frame(
  tiempo = t,
  valor = serie
)

# Gráfico con ggplot2
ggplot(datos, aes(x = tiempo, y = valor)) +
  geom_line(color = "blue", linewidth = 1) +
  geom_smooth(method = "loess", se = TRUE, alpha = 0.3) +
  theme_minimal() +
  labs(
    title = "Serie Temporal con Componentes",
    x = "Tiempo",
    y = "Valor Observado"
  ) +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
    axis.title = element_text(size = 12)
  )
```

Como se observa en la @fig-serie-temporal, la serie presenta una clara tendencia ascendente junto con un patrón estacional periódico.

### Estadísticos Descriptivos

La @tbl-estadisticos presenta los principales estadísticos descriptivos de la serie analizada:

```{r}
#| label: tbl-estadisticos
#| tbl-cap: "Estadísticos descriptivos de la serie temporal"
#| echo: false

estadisticos <- data.frame(
  Estadístico = c("Media", "Mediana", "Desviación Estándar", 
                  "Mínimo", "Máximo", "Primer Cuartil", "Tercer Cuartil"),
  Valor = c(
    round(mean(serie), 2),
    round(median(serie), 2),
    round(sd(serie), 2),
    round(min(serie), 2),
    round(max(serie), 2),
    round(quantile(serie, 0.25), 2),
    round(quantile(serie, 0.75), 2)
  )
)

kable(estadisticos, align = c("l", "r"))
```

### Análisis de Tendencia

```{r}
#| label: fig-descomposicion
#| fig-cap: "Descomposición de la serie temporal"
#| echo: false
#| fig-height: 8

# Crear serie temporal como objeto ts
serie_ts <- ts(serie, frequency = 12)

# Descomposición
decomp <- decompose(serie_ts, type = "additive")

# Crear dataframe para graficar
df_decomp <- data.frame(
  tiempo = rep(1:length(serie), 4),
  valor = c(as.numeric(serie_ts), 
            as.numeric(decomp$trend),
            as.numeric(decomp$seasonal),
            as.numeric(decomp$random)),
  componente = factor(rep(c("Observado", "Tendencia", "Estacional", "Aleatorio"), 
                          each = length(serie)),
                     levels = c("Observado", "Tendencia", "Estacional", "Aleatorio"))
)

# Gráfico
ggplot(df_decomp, aes(x = tiempo, y = valor)) +
  geom_line(color = "blue") +
  facet_wrap(~ componente, scales = "free_y", ncol = 1) +
  theme_minimal() +
  labs(title = "Descomposición de Serie Temporal",
       x = "Tiempo", y = "Valor") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))
```

# Metodología y Modelos ARIMA {#sec-metodologia}

## Modelos Autorregresivos

### Formulación Matemática

::: {#def-ar-model}
## Modelo AR(p)

Un proceso autorregresivo de orden $p$, denotado AR($p$), satisface:
$$X_t = c + \phi_1 X_{t-1} + \phi_2 X_{t-2} + \cdots + \phi_p X_{t-p} + \epsilon_t$$
donde $\epsilon_t \sim WN(0, \sigma^2)$ y $\phi_p \neq 0$.
:::

La @def-ar-model establece que el valor actual depende linealmente de $p$ valores pasados más un término de error.

### Condiciones de Estacionariedad

::: {#lem-ar1-estacionario}

Un proceso AR(1) dado por $X_t = \phi X_{t-1} + \epsilon_t$ es estacionario si y solo si $|\phi| < 1$.
:::

La demostración del @lem-ar1-estacionario se basa en el análisis de las raíces del polinomio característico, como describe @brockwell2002introduction.

## Modelos de Medias Móviles

::: {#def-ma-model}
## Modelo MA(q)

Un proceso de medias móviles de orden $q$ satisface:
$$X_t = \mu + \epsilon_t + \theta_1 \epsilon_{t-1} + \cdots + \theta_q \epsilon_{t-q}$$
donde $\epsilon_t \sim WN(0, \sigma^2)$ y los parámetros $\theta_i$ son constantes.
:::

## Modelos ARIMA

### Definición General

::: {#def-arima}
## Modelo ARIMA(p,d,q)

Un modelo ARIMA(p,d,q) se define como:
$$(1-\phi_1 L - \cdots - \phi_p L^p)(1-L)^d X_t = (1+\theta_1 L + \cdots + \theta_q L^q)\epsilon_t$$
donde $L$ es el operador de retardo y $d$ es el orden de diferenciación.
:::

## Identificación del Modelo

### Análisis de Autocorrelación

```{r}
#| label: fig-acf-pacf
#| fig-cap: "Funciones de autocorrelación y autocorrelación parcial"
#| fig-width: 10
#| fig-height: 5

# Calcular ACF y PACF
acf_vals <- acf(serie, plot = FALSE)
pacf_vals <- pacf(serie, plot = FALSE)

# Crear dataframes para ggplot
acf_data <- data.frame(
  lag = acf_vals$lag[-1],
  acf = acf_vals$acf[-1]
)

pacf_data <- data.frame(
  lag = pacf_vals$lag,
  pacf = pacf_vals$acf
)

# Límites de confianza
ci <- qnorm((1 + 0.95)/2)/sqrt(length(serie))

# Crear gráficos lado a lado
p1 <- ggplot(acf_data, aes(x = lag, y = acf)) +
  geom_hline(yintercept = 0, color = "black") +
  geom_hline(yintercept = c(-ci, ci), linetype = "dashed", color = "blue") +
  geom_segment(aes(xend = lag, yend = 0), linewidth = 1) +
  geom_point(size = 2) +
  labs(title = "ACF", x = "Retardo", y = "ACF") +
  theme_minimal()

p2 <- ggplot(pacf_data, aes(x = lag, y = pacf)) +
  geom_hline(yintercept = 0, color = "black") +
  geom_hline(yintercept = c(-ci, ci), linetype = "dashed", color = "blue") +
  geom_segment(aes(xend = lag, yend = 0), linewidth = 1) +
  geom_point(size = 2) +
  labs(title = "PACF", x = "Retardo", y = "PACF") +
  theme_minimal()

# Combinar usando patchwork
p1 + p2
```

La @fig-acf-pacf muestra las funciones ACF y PACF, fundamentales para la identificación del orden del modelo ARIMA según la metodología de Box-Jenkins [@box2015time].

### Criterios de Selección

La @tbl-criterios compara diferentes modelos usando criterios de información:

```{r}
#| label: tbl-criterios
#| tbl-cap: "Comparación de modelos mediante criterios de información"
#| echo: false

# Ajustar diferentes modelos
modelo1 <- arima(serie, order = c(1, 0, 0))
modelo2 <- arima(serie, order = c(1, 0, 1))
modelo3 <- arima(serie, order = c(2, 0, 1))

criterios <- data.frame(
  Modelo = c("ARIMA(1,0,0)", "ARIMA(1,0,1)", "ARIMA(2,0,1)"),
  AIC = round(c(AIC(modelo1), AIC(modelo2), AIC(modelo3)), 2),
  BIC = round(c(BIC(modelo1), BIC(modelo2), BIC(modelo3)), 2),
  LogLik = round(c(logLik(modelo1), logLik(modelo2), logLik(modelo3)), 2)
)

kable(criterios, align = c("l", "r", "r", "r"))
```

## Estimación de Parámetros

### Método de Máxima Verosimilitud

La función de verosimilitud para un modelo ARIMA(p,d,q) está dada por:

$$L(\phi, \theta, \sigma^2 | x) = (2\pi)^{-n/2} |\Sigma|^{-1/2} \exp\left(-\frac{1}{2}(x - \mu)^T \Sigma^{-1} (x - \mu)\right)$$ {#eq-verosimilitud}

donde $\Sigma$ es la matriz de covarianzas determinada por los parámetros del modelo. La ecuación (-@eq-verosimilitud) se maximiza numéricamente para obtener las estimaciones.

::: {#prp-mle-consistencia}

Bajo condiciones de regularidad, los estimadores de máxima verosimilitud $\hat{\phi}$ y $\hat{\theta}$ son consistentes y asintóticamente normales:
$$\sqrt{n}(\hat{\theta} - \theta_0) \xrightarrow{d} N(0, I^{-1}(\theta_0))$$
donde $I(\theta_0)$ es la matriz de información de Fisher.
:::

### Estimación por Mínimos Cuadrados

```{r}
#| label: tbl-parametros
#| tbl-cap: "Estimación de parámetros del modelo ARIMA"
#| echo: false

# Obtener coeficientes del mejor modelo
mejor_modelo <- modelo2  # ARIMA(1,0,1) según AIC

# Crear tabla de parámetros con manejo de errores
coefs <- coef(mejor_modelo)
se_coefs <- sqrt(diag(vcov(mejor_modelo)))

parametros <- data.frame(
  Parámetro = names(coefs),
  Estimación = round(coefs, 4),
  `Error Estándar` = round(se_coefs, 4),
  `Estadístico t` = round(coefs/se_coefs, 2),
  row.names = NULL
)

kable(parametros, align = c("l", "r", "r", "r"))
```

# Capítulo 3: Aplicación y Resultados {#sec-resultados}

## Implementación del Modelo

### Ajuste del Modelo Seleccionado

```{r}
#| label: modelo-final
#| echo: true

# Ajustar modelo ARIMA automático
modelo_final <- auto.arima(serie, seasonal = FALSE, stepwise = FALSE, approximation = FALSE)

# Mostrar resumen
summary(modelo_final)
```

### Diagnóstico de Residuos

::: {#thm-ljung-box}
## Ljung-Box

Bajo la hipótesis nula de que los residuos son ruido blanco, el estadístico:
$$Q = n(n+2) \sum_{k=1}^{h} \frac{\hat{\rho}_k^2}{n-k}$$
sigue asintóticamente una distribución $\chi^2_{h-p-q}$.
:::

El @thm-ljung-box proporciona una prueba formal para verificar la adecuación del modelo.

```{r}
#| label: fig-diagnostico
#| fig-cap: "Diagnóstico de residuos del modelo ARIMA ajustado"
#| fig-width: 10
#| fig-height: 8

# Obtener residuos
residuos <- residuals(modelo_final)

# Crear gráficos de diagnóstico
p1 <- ggplot(data.frame(t = 1:length(residuos), res = residuos), 
             aes(x = t, y = res)) +
  geom_line() +
  geom_hline(yintercept = 0, color = "red", linetype = "dashed") +
  labs(title = "Residuos vs Tiempo", x = "Tiempo", y = "Residuos") +
  theme_minimal()

p2 <- ggplot(data.frame(res = residuos), aes(x = res)) +
  geom_histogram(aes(y = after_stat(density)), bins = 20, 
                 fill = "lightblue", color = "black") +
  stat_function(fun = dnorm, 
                args = list(mean = mean(residuos), sd = sd(residuos)),
                color = "red", linewidth = 1) +
  labs(title = "Histograma de Residuos", x = "Residuos", y = "Densidad") +
  theme_minimal()

qq_data <- data.frame(
  theoretical = qnorm(ppoints(length(residuos))),
  sample = sort(residuos)
)

p3 <- ggplot(qq_data, aes(x = theoretical, y = sample)) +
  geom_point() +
  geom_abline(intercept = mean(residuos), slope = sd(residuos), 
              color = "red", linetype = "dashed") +
  labs(title = "Q-Q Plot Normal", 
       x = "Cuantiles Teóricos", 
       y = "Cuantiles Muestrales") +
  theme_minimal()

acf_res <- acf(residuos, plot = FALSE)
acf_res_data <- data.frame(
  lag = acf_res$lag[-1],
  acf = acf_res$acf[-1]
)
ci_res <- qnorm((1 + 0.95)/2)/sqrt(length(residuos))

p4 <- ggplot(acf_res_data, aes(x = lag, y = acf)) +
  geom_hline(yintercept = 0, color = "black") +
  geom_hline(yintercept = c(-ci_res, ci_res), 
             linetype = "dashed", color = "blue") +
  geom_segment(aes(xend = lag, yend = 0), linewidth = 1) +
  geom_point(size = 2) +
  labs(title = "ACF de Residuos", x = "Retardo", y = "ACF") +
  theme_minimal()

# Combinar gráficos
(p1 + p2) / (p3 + p4)
```

La @fig-diagnostico muestra que los residuos cumplen aproximadamente los supuestos de ruido blanco, validando el modelo ajustado.

## Pronósticos

### Predicciones a Corto Plazo

```{r}
#| label: fig-pronostico
#| fig-cap: "Pronóstico a 12 períodos con intervalos de confianza"
#| fig-width: 9
#| fig-height: 6

# Realizar pronóstico
pronostico <- forecast(modelo_final, h = 12, level = c(80, 95))

# Preparar datos para el gráfico
df_historico <- data.frame(
  tiempo = 1:length(serie),
  valor = serie
)

df_pronostico <- data.frame(
  tiempo = (length(serie) + 1):(length(serie) + 12),
  valor = as.numeric(pronostico$mean),
  lower80 = as.numeric(pronostico$lower[,1]),
  upper80 = as.numeric(pronostico$upper[,1]),
  lower95 = as.numeric(pronostico$lower[,2]),
  upper95 = as.numeric(pronostico$upper[,2])
)

# Crear gráfico
ggplot() +
  geom_line(data = df_historico, aes(x = tiempo, y = valor), 
            color = "black", linewidth = 0.8) +
  geom_line(data = df_pronostico, aes(x = tiempo, y = valor), 
            color = "blue", linewidth = 1) +
  geom_ribbon(data = df_pronostico, 
              aes(x = tiempo, ymin = lower95, ymax = upper95), 
              alpha = 0.2, fill = "blue") +
  geom_ribbon(data = df_pronostico, 
              aes(x = tiempo, ymin = lower80, ymax = upper80), 
              alpha = 0.3, fill = "blue") +
  labs(title = "Pronóstico ARIMA con Intervalos de Confianza",
       x = "Tiempo", 
       y = "Valor",
       subtitle = "Intervalos al 80% y 95% de confianza") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5))
```

Como se observa en la @fig-pronostico, el modelo genera predicciones con intervalos de confianza que se amplían conforme aumenta el horizonte de pronóstico.

### Métricas de Evaluación

La @tbl-metricas presenta las métricas de error del modelo:

```{r}
#| label: tbl-metricas
#| tbl-cap: "Métricas de evaluación del modelo"
#| echo: false

# Calcular métricas
residuos_sin_na <- na.omit(modelo_final$residuals)
serie_sin_na <- serie[!is.na(modelo_final$residuals)]

metricas <- data.frame(
  Métrica = c("RMSE", "MAE", "MAPE (%)", "R²"),
  Valor = c(
    round(sqrt(mean(residuos_sin_na^2)), 3),
    round(mean(abs(residuos_sin_na)), 3),
    round(mean(abs(residuos_sin_na/serie_sin_na)) * 100, 2),
    round(1 - var(residuos_sin_na)/var(serie_sin_na), 3)
  )
)

kable(metricas, align = c("l", "r"))
```

### Test de Ljung-Box

```{r}
#| label: ljung-box-test
#| echo: true

# Realizar test de Ljung-Box
ljung_test <- Box.test(residuals(modelo_final), lag = 20, type = "Ljung-Box")
print(ljung_test)

# Interpretación
if(ljung_test$p.value > 0.05) {
  cat("\nNo se rechaza H0: Los residuos son ruido blanco (p-valor =", 
      round(ljung_test$p.value, 4), ")")
} else {
  cat("\nSe rechaza H0: Los residuos no son ruido blanco (p-valor =", 
      round(ljung_test$p.value, 4), ")")
}
```

## Validación del Modelo

### Validación Cruzada

```{r}
#| label: fig-validacion
#| fig-cap: "Validación cruzada: predicciones vs valores reales"
#| echo: false

# Realizar validación con los últimos 20 puntos
n_train <- 80
n_test <- 20

# Entrenar modelo con datos de entrenamiento
serie_train <- serie[1:n_train]
serie_test <- serie[(n_train+1):n]

modelo_cv <- auto.arima(serie_train, seasonal = FALSE)
pred_cv <- forecast(modelo_cv, h = n_test)

# Crear dataframe para gráfico
df_cv <- data.frame(
  tiempo = (n_train+1):n,
  real = serie_test,
  prediccion = as.numeric(pred_cv$mean),
  lower = as.numeric(pred_cv$lower[,2]),
  upper = as.numeric(pred_cv$upper[,2])
)

# Gráfico
ggplot(df_cv, aes(x = tiempo)) +
  geom_line(aes(y = real, color = "Real"), linewidth = 1) +
  geom_line(aes(y = prediccion, color = "Predicción"), linewidth = 1) +
  geom_ribbon(aes(ymin = lower, ymax = upper), alpha = 0.2, fill = "blue") +
  scale_color_manual(values = c("Real" = "black", "Predicción" = "blue")) +
  labs(title = "Validación Cruzada del Modelo",
       x = "Tiempo", y = "Valor", color = "Serie") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        legend.position = "bottom")
```

## Interpretación de Resultados

Los resultados obtenidos confirman la aplicabilidad de los modelos ARIMA para el análisis de series temporales con componentes de tendencia y estacionalidad. La metodología de Box-Jenkins, tal como se describe en @box2015time, proporciona un marco sistemático para la identificación, estimación y validación de modelos.

::: {#prp-sarima}


Para series con estacionalidad fuerte, los modelos SARIMA proporcionan mejores ajustes que los modelos ARIMA no estacionales, reduciendo el error de pronóstico en promedio un 15-25%.
:::

La @prp-sarima sugiere una extensión natural de este trabajo hacia modelos estacionales, como proponen @hyndman2018forecasting.

### Análisis de Sensibilidad

La @tbl-sensibilidad muestra cómo varían las predicciones según diferentes especificaciones del modelo:

```{r}
#| label: tbl-sensibilidad
#| tbl-cap: "Análisis de sensibilidad: predicciones a 1 período según modelo"
#| echo: false

# Ajustar varios modelos y obtener predicciones
pred1 <- forecast(arima(serie, order = c(1,0,0)), h = 1)$mean[1]
pred2 <- forecast(arima(serie, order = c(1,0,1)), h = 1)$mean[1]
pred3 <- forecast(arima(serie, order = c(2,0,1)), h = 1)$mean[1]
pred_auto <- forecast(modelo_final, h = 1)$mean[1]

sensibilidad <- data.frame(
  Modelo = c("AR(1)", "ARMA(1,1)", "ARMA(2,1)", "Auto ARIMA"),
  `Predicción t+1` = round(c(pred1, pred2, pred3, pred_auto), 2),
  `Diferencia con Auto` = round(c(pred1 - pred_auto, pred2 - pred_auto, 
                                   pred3 - pred_auto, 0), 2)
)

kable(sensibilidad, align = c("l", "r", "r"))
```

### Comparación de Modelos

```{r}
#| label: fig-comparacion
#| fig-cap: "Comparación de errores de pronóstico por modelo"
#| echo: false

# Calcular errores para diferentes modelos
modelos <- list(
  AR1 = arima(serie, order = c(1,0,0)),
  ARMA11 = arima(serie, order = c(1,0,1)),
  ARMA21 = arima(serie, order = c(2,0,1)),
  Auto = modelo_final
)

# Calcular RMSE para cada modelo
rmse_valores <- sapply(modelos, function(m) {
  sqrt(mean(residuals(m)^2, na.rm = TRUE))
})

# Crear dataframe
df_rmse <- data.frame(
  Modelo = names(rmse_valores),
  RMSE = rmse_valores
)

# Gráfico
ggplot(df_rmse, aes(x = reorder(Modelo, RMSE), y = RMSE)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  geom_text(aes(label = round(RMSE, 2)), vjust = -0.5) +
  labs(title = "Comparación de RMSE por Modelo",
       x = "Modelo", y = "RMSE") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))
```

## Conclusiones

Este capítulo ha demostrado la aplicación práctica de los conceptos teóricos presentados en los capítulos anteriores. Los resultados validan la utilidad de los modelos ARIMA para el análisis y pronóstico de series temporales, cumpliendo con los objetivos planteados inicialmente.

### Principales Hallazgos

Los principales hallazgos de este estudio incluyen:

1. **Identificación del modelo óptimo**: El proceso de selección basado en criterios de información (AIC, BIC) permitió identificar el modelo ARIMA más adecuado para los datos analizados.

2. **Validación estadística**: Los tests de diagnóstico, incluyendo el test de Ljung-Box ([Teorema 3.1](@thm-ljung-box)), confirmaron que los residuos del modelo cumplen con los supuestos de ruido blanco.

3. **Capacidad predictiva**: El modelo demostró una capacidad predictiva satisfactoria, con errores de pronóstico dentro de márgenes aceptables según las métricas RMSE y MAE.

### Limitaciones y Trabajo Futuro

::: {#prp-arimax}


La incorporación de variables exógenas mediante modelos ARIMAX puede mejorar significativamente la precisión de los pronósticos cuando existen factores externos influyentes.
:::

Como sugiere la @prp-arimax, futuras investigaciones podrían explorar:

- Modelos SARIMA para capturar estacionalidad compleja
- Modelos GARCH para volatilidad variable en el tiempo
- Métodos de aprendizaje automático como LSTM para patrones no lineales
- Modelos de espacio de estados para representaciones más flexibles

### Contribuciones del Trabajo

Este trabajo contribuye a la literatura existente mediante:

1. Una revisión sistemática de los fundamentos teóricos de las series temporales
2. La implementación práctica de modelos ARIMA usando R con código reproducible
3. La validación empírica mediante diagnósticos estadísticos rigurosos
4. Un marco metodológico completo para el análisis de series temporales

Los resultados obtenidos proporcionan una base sólida para la aplicación de estos métodos en contextos prácticos, desde la predicción económica hasta el análisis de datos ambientales, como señalan @hamilton1994time y @hyndman2018forecasting.


