Un modelo ARIMA (Autoregressive Integrated Moving Average) es una herramienta estadística utilizada para analizar y predecir series de tiempo. En términos simples, es como una “bola de cristal matemática” que usa datos pasados para estimar valores futuros, especialmente útil en finanzas para preveer precios, ventas o ingresos.
Desglose del nombre ARIMA 1. Autoregressive o autorregresivo (AR) → Usa valores pasados para predecir el futuro 2. Integrated (I) → Ajusta tendencias en los datos para hacerlos estacionarios (sin patrones cambiantes en el tiempo). 3.MAving Average o media móvil (MA)- → Suaviza fluctuaciones aleatorias a partir de errores pasados.
ARIMA combina estos tres elementos para crear una predicción más precisa.
La metodología Box-Jenkins es un enfoque sistemático para construir modelos ARIMA con el objetivo de analizar y pronosticar series de tiempo. Fue desarrollada por George Box y Gwilym Jenkins y se basa en cuatro etapas clave:
1️⃣ Identificación 2️⃣ Estimación 3️⃣ Validación 4️⃣ Pronóstico
Se usa especialmente cuando se quiere encontrar el modelo ARIMA más adecuado para una serie de tiempo.
Antes de ajustar un modelo ARIMA, es necesario entender la estructura de la serie de tiempo.
1. Verificar estacionariedad (media y varianza constantes en el tiempo) 2. Diferenciar los datos por si hay una tendencia para hacer los datos estacionarios 3. Examinar los gráficos ACF y PACF para determinar los parámetro p y q
#Cargar librerías necesarias
library(readxl) # Para leer archivos Excel
library(tseries) # Para pruebas de estacionariedad
library(forecast) # Para modelado ARIMA y pronósticos
library(ggplot2) # Para visualización de datos
library(plotly) # Para gráficos interactivos
library(timetk)
data_col <- read_excel("IPC.xlsx", col_types = c("date",
"numeric"))
# Convertir el IP a una serie de tiempo Mensual
ipc_ts <- ts(data_col$IPC, start = c(2000, 1), frequency = 12)
ipc_ts
## Jan Feb Mar Apr May Jun Jul Aug Sep Oct
## 2000 41.54 42.30 42.87 43.18 43.37 43.42 43.48 43.58 43.84 44.04
## 2001 44.80 45.44 46.14 46.72 47.12 47.18 47.11 47.22 47.55 47.67
## 2002 48.32 49.14 49.48 49.74 50.02 50.28 50.31 50.44 50.82 51.13
## 2003 52.41 53.08 53.40 53.94 54.26 54.46 54.41 54.49 54.73 54.93
## 2004 55.81 56.29 56.92 57.14 57.32 57.53 57.48 57.65 58.09 58.13
## 2005 58.70 59.17 59.57 59.85 60.23 60.39 60.45 60.48 60.86 61.07
## 2006 61.33 61.63 61.99 62.11 62.28 62.59 62.90 63.27 63.54 63.46
## 2007 64.10 64.68 65.32 65.89 66.10 66.31 66.18 66.14 66.29 66.36
## 2008 67.37 68.49 68.96 69.32 69.92 70.67 71.11 71.40 71.24 71.58
## 2009 72.36 72.65 72.91 73.08 73.13 72.95 72.94 73.03 72.81 72.66
## 2010 73.23 73.64 73.90 74.30 74.33 74.47 74.40 74.32 73.80 73.65
## 2011 75.14 75.40 75.45 75.57 75.69 75.94 76.09 75.99 76.87 76.79
## 2012 77.45 77.58 77.48 77.47 78.13 77.98 77.69 77.90 78.50 78.43
## 2013 78.89 78.78 78.65 79.03 79.64 79.53 79.16 79.47 80.11 79.55
## 2014 80.20 80.51 80.90 81.34 81.64 81.68 81.78 82.16 82.47 82.58
## 2015 83.76 84.50 85.01 85.11 84.82 85.16 85.07 85.80 86.49 87.28
## 2016 89.83 90.72 91.65 92.41 92.69 93.25 93.56 92.98 93.22 92.92
## 2017 94.18 95.03 95.44 95.83 96.23 96.34 96.45 96.56 96.81 96.87
## 2018 97.49 98.12 98.66 99.00 99.23 99.42 99.23 99.45 99.81 99.74
## 2019 100.67 101.08 101.75 102.29 102.56 103.03 103.39 103.49 103.94 104.15
## 2020 105.14 105.72 106.33 106.39 106.11 106.00 105.92 105.99 106.21 106.26
## 2021 107.14 107.72 108.13 108.75 111.76 110.39 110.67 111.13 111.42 111.42
## 2022 114.69 116.68 117.95 119.55 120.54 121.20 122.22 123.59 124.50 125.42
## 2023 130.36 132.23 133.64 134.60 135.35 135.59 136.27 137.56 138.08 138.73
## 2024 140.71 142.02 142.75 143.64 144.03 144.04 144.36 144.63 144.97 144.83
## 2025 146.75
## Nov Dec
## 2000 44.19 44.32
## 2001 47.89 47.98
## 2002 51.54 51.69
## 2003 55.01 55.39
## 2004 58.35 58.35
## 2005 61.12 61.03
## 2006 63.53 63.67
## 2007 66.42 66.85
## 2008 71.76 71.93
## 2009 72.75 72.79
## 2010 74.02 74.58
## 2011 76.53 76.96
## 2012 78.14 78.42
## 2013 79.25 79.78
## 2014 82.61 82.81
## 2015 87.60 88.48
## 2016 92.82 93.02
## 2017 96.87 97.01
## 2018 99.68 100.00
## 2019 104.22 104.53
## 2020 106.22 106.47
## 2021 111.86 112.67
## 2022 126.32 128.07
## 2023 138.92 139.23
## 2024 145.05 145.56
## 2025
# Calcular estadísticas descriptivas básicas
descriptive_stats <- data.frame(
Min = min(ipc_ts),
Max = max(ipc_ts),
Media = mean(ipc_ts),
Mediana = median(ipc_ts),
DesviacionEstandar = sd(ipc_ts),
CoefVar = sd(ipc_ts) / mean(ipc_ts)
)
print(descriptive_stats)
## Min Max Media Mediana DesviacionEstandar CoefVar
## 1 41.54 146.75 82.90698 77.98 26.58843 0.320702
# Gráfico interactivo de la serie original
grafico_serie <- ggplot(data_col, aes(x = seq.Date(from = as.Date("2000-01-01"), by = "month", length.out = nrow(data_col)), y = ipc_ts)) +
geom_line(color = "grey", size = 1.5) +
geom_point(color = "grey") +
ggtitle("Figura 1.IPC Cali") +
xlab("Tiempo") +
ylab("índice") +
theme_minimal()
ggplotly(grafico_serie)
# Extraer señales:
# Descomposición de la serie temporal
stl_decomp <- stl(ipc_ts, s.window = "periodic")
plot(stl_decomp)
Graficamos serie original VS ajustada por estacionalidad
# Extraer los componentes de la descomposición
ipc_sa <- ipc_ts - stl_decomp$time.series[, "seasonal"]
library(ggplot2)
library(plotly)
library(scales)
# Comparación de la serie original con la ajustada por estacionalidad
grafico_ajustada <- ggplot() +
geom_line(aes(x = seq_along(ipc_ts), y = ipc_ts), color = "grey", size = 1, linetype = "solid") +
geom_line(aes(x = seq_along(ipc_sa), y = ipc_sa), color = "black", size = 1, linetype = "dashed") +
ggtitle("Figura 2.Serie Original vs Serie Ajustada por Estacionalidad") +
xlab("Tiempo") +
ylab("Índice") +
theme_minimal()
ggplotly(grafico_ajustada)
# Crear vector de fechas correctamente alineado con la serie
fechas <- seq.Date(from = as.Date("2000-01-01"), by = "month", length.out = length(ipc_ts))
# Gráfico mejorado con fechas en el eje X
grafico_ajustada <- ggplot() +
geom_line(aes(x = fechas, y = ipc_ts), color = "grey", size = 1, linetype = "solid", name = "Serie Original") +
geom_line(aes(x = fechas, y = ipc_sa), color = "black", size = 1, linetype = "dashed", name = "Serie Ajustada") +
ggtitle("Figura 2. Serie Original vs Serie Ajustada por Estacionalidad") +
xlab("Tiempo") +
ylab("Índice") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) # Rotar etiquetas para mejor visualización
# Convertir a gráfico interactivo
ggplotly(grafico_ajustada)
# Comparación de la serie original con la tendencia
tendencia <- stl_decomp$time.series[, "trend"]
grafico_tendencia <- ggplot() +
geom_line(aes(x = seq_along(ipc_ts), y = ipc_ts), color = "grey", size = 1) +
geom_line(aes(x = seq_along(tendencia), y = tendencia), color = "purple", size = 1, linetype = "dashed") +
ggtitle("Figura 3. Serie Original vs Tendencia") +
xlab("Tiempo") +
ylab("índice") +
theme_minimal()
ggplotly(grafico_tendencia)
# Extraer la tendencia de la descomposición STL
tendencia <- stl_decomp$time.series[, "trend"]
# Gráfico interactivo de la serie original vs tendencia
grafico_tendencia <- ggplot() +
geom_line(aes(x = fechas, y = ipc_ts), color = "grey", size = 1, linetype = "solid", name = "Serie Original") +
geom_line(aes(x = fechas, y = tendencia), color = "purple", size = 1, linetype = "dashed", name = "Tendencia") +
ggtitle("Figura 3. Serie Original vs Tendencia") +
xlab("Tiempo") +
ylab("Índice") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) # Rotar etiquetas del eje X para mejor visualización
# Convertir a gráfico interactivo
ggplotly(grafico_tendencia)
#Cálculo de la tasa de crecimiento anual correctamente alineada
tasa_crecimiento <- (ipc_ts[(13:length(ipc_ts))] / ipc_ts[1:(length(ipc_ts) - 12)] - 1) * 100
tasa_tendencia <- (tendencia[(13:length(tendencia))] / tendencia[1:(length(tendencia) - 12)] - 1) * 100
# Crear vector de fechas corregido
fechas_corregidas <- seq(from = as.Date("2001-01-01"), by = "month", length.out = length(tasa_crecimiento))
# Verificar longitudes
print(length(fechas_corregidas))
## [1] 289
print(length(tasa_crecimiento))
## [1] 289
print(length(tasa_tendencia))
## [1] 289
# Gráfico de la tasa de crecimiento anual
grafico_crecimiento <- ggplot() +
geom_line(aes(x = fechas_corregidas, y = tasa_crecimiento), color = "grey", size = 1) +
geom_line(aes(x = fechas_corregidas, y = tasa_tendencia), color = "green", size = 1, linetype = "dashed") +
ggtitle("Figura 4. Tasa de Crecimiento Anual: Serie Original vs Tendencia") +
xlab("Tiempo") +
ylab("% de Crecimiento Anual") +
theme_minimal()
# Convertir a gráfico interactivo
ggplotly(grafico_crecimiento)
Ahora dividimos el conjunto de datos de prueba y entrenamiento
✅ Para entrenar el modelo con datos históricos sin usar información futura. ✅ Para evaluar la precisión del modelo comparando sus predicciones con los datos reales de prueba.
💡 Esta división es clave en modelos predictivos para evitar sobreajuste y evaluar el rendimiento en datos no vistos.
# Dividir la serie en conjunto de entrenamiento y prueba
train_size <- length(ipc_ts) - 13
train_ts <- window(ipc_ts, end = c(2023, 12)) # Entrenamiento hasta dic23
test_ts <- window(ipc_ts, start = c(2024, 1)) # Prueba desde ene24
En el análisis de series de tiempo, una serie es estacionaria si su comportamiento es constante a lo largo del tiempo, es decir:
✅ Su promedio no cambia con el tiempo. ✅ Su variabilidad (qué tanto fluctúa) se mantiene estable. ✅ Su relación con valores pasados es siempre la misma.
¿Cómo saber si una serie es estacionaria?
1️⃣ Observando un gráfico Si el gráfico de la serie muestra una tendencia creciente o decreciente, o si las variaciones se hacen más grandes con el tiempo, la serie probablemente no es estacionaria.
2️⃣ Usando la prueba de Dickey-Fuller Aumentada (ADF) Es una prueba estadística que nos dice si la serie tiene una tendencia fuerte. Si el p-valor de la prueba es mayor a 0.05, significa que la serie no es estacionaria.
¿Cómo hacer una serie estacionaria?
Si encontramos que la serie no es estacionaria, podemos transformarla para que lo sea:
✅ Diferenciación: Restamos cada valor con su valor anterior. Esto elimina tendencias crecientes o decrecientes. ✅ Tomar el logaritmo: Si la variabilidad crece con el tiempo, aplicar un logaritmo estabiliza la varianza. ✅ Eliminar tendencias o ajustar estacionalidad: Si hay patrones repetitivos, podemos restarlos o modelarlos por separado.
# Prueba de estacionariedad con Augmented Dickey-Fuller (ADF)
adf_test <- adf.test(train_ts)
print(adf_test)
##
## Augmented Dickey-Fuller Test
##
## data: train_ts
## Dickey-Fuller = 1.7781, Lag order = 6, p-value = 0.99
## alternative hypothesis: stationary
# Si la serie no es estacionaria (p-valor > 0.05), aplicar diferenciación
if (adf_test$p.value > 0.05 && length(train_ts) > 1) {
train_diff <- diff(train_ts, differences = 1)
}
# Graficar la serie original en un gráfico separado
p2 <- ggplot(data.frame(Tiempo = time(train_ts), Ipc = as.numeric(train_ts)), aes(x = Tiempo, y = Ipc)) +
geom_line(color = "blue") +
ggtitle("Serie Original") +
xlab("Tiempo") + ylab("IPC")
ggplotly(p2) # Convertir en gráfico interactivo
# Graficar la serie diferenciada en un gráfico separado
p3 <- ggplot(data.frame(Tiempo = time(train_ts)[-1], IPC_Diff = as.numeric(train_diff)), aes(x = Tiempo, y = IPC_Diff)) +
geom_line(color = "red") +
ggtitle("Serie Estacionaria (Diferenciada)") +
xlab("Tiempo") + ylab("IPC Diferenciado")
ggplotly(p3) # Convertir en gráfico interactivo
A continuación se muestra que el valor que puede tomar d=1:
El valor p ya es menor a 0.05.
# Segunda prueba de estacionariedad sobre la serie diferenciada
adf_test_diff <- adf.test(train_diff)
print(adf_test_diff)
##
## Augmented Dickey-Fuller Test
##
## data: train_diff
## Dickey-Fuller = -4.9966, Lag order = 6, p-value = 0.01
## alternative hypothesis: stationary
Identificación manual de p y q
¿Qué hacen estos gráficos?
ACF (Autocorrelation Function)
Muestra la correlación de la serie con sus rezagos.
Ayuda a determinar el parámetro q en un modelo ARIMA(p, d, q).
PACF (Partial Autocorrelation Function)
Muestra la correlación parcial entre la serie y un rezago específico, eliminando el efecto de rezagos intermedios.
Ayuda a determinar el parámetro p en un modelo ARIMA(p, d, q).
# Graficar ACF y PACF
acf_plot <- ggAcf(train_diff, lag.max = 20) + ggtitle("Autocorrelation Function (ACF)")
pacf_plot <- ggPacf(train_diff, lag.max = 20) + ggtitle("Partial Autocorrelation Function (PACF)")
ggplotly(acf_plot)
ggplotly(pacf_plot)
Modelo amnual Arima (1,1,1)
*El coeficiente AR(1) alto (0.9576) sugiere que la serie tiene una inercia fuerte, lo que implica que valores pasados afectan mucho el presente.
*El coeficiente MA(1) negativo (-0.6682) indica que los errores previos afectan en dirección opuesta al valor presente.
*El AIC y BIC pueden compararse con otros modelos (por ejemplo, ARIMA(0,1,1) o ARIMA(1,1,0)) para ver si esta especificación es la mejor.
🔹 Recomendación:
*Probar otras especificaciones como ARIMA(0,1,1) o ARIMA(1,1,2) y comparar AIC/BIC. Evaluar la serie en términos de estacionariedad para verificar si la diferenciación fue suficiente.
*Usar gráficos de autocorrelación para analizar si el orden AR y MA están bien ajustados.
# Cálculo manual de modelo ARIMA
manual_arima_model <- Arima(train_ts, order = c(1,1,1))
summary(manual_arima_model)
## Series: train_ts
## ARIMA(1,1,1)
##
## Coefficients:
## ar1 ma1
## 0.9576 -0.6682
## s.e. 0.0326 0.1192
##
## sigma^2 = 0.1577: log likelihood = -141.67
## AIC=289.34 AICc=289.42 BIC=300.32
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 0.04456859 0.3950103 0.2637702 0.05480029 0.3301973 0.06556071
## ACF1
## Training set 0.1000932
1. Gráfico de Residuales (arriba) Muestra cómo se comportan los errores del modelo a lo largo del tiempo. Lo ideal: Que los residuales estén distribuidos aleatoriamente alrededor de 0, sin patrones visibles.
Problema: Hay algunos picos grandes, especialmente alrededor de 2020, lo que sugiere que el modelo no capturó bien algunos eventos extremos.
2. Autocorrelación de Residuales (ACF - abajo izquierda) Indica si los errores están correlacionados entre sí. Lo ideal: Que todas las barras estén dentro de las líneas azules (sin autocorrelación). Problema: Hay varios rezagos que sobresalen de las líneas azules, lo que indica que aún hay patrones en los datos que el modelo no ha capturado bien.
3. Histograma de Residuales (abajo derecha) Muestra si los errores siguen una distribución normal. Lo ideal: Que la forma sea similar a la curva roja (campana de Gauss). Problema: Aunque en general sigue una distribución normal, hay valores extremos en los extremos (colas), lo que sugiere que hay algunos eventos atípicos que el modelo no predijo bien.
Conclusión General 🔹 El modelo ARIMA(1,1,1) no captura completamente la estructura de los datos. 🔹 Se observa autocorrelación en los errores y presencia de eventos extremos (especialmente en 2020). 🔹 Podría mejorarse probando un modelo más complejo, incluyendo estacionalidad o cambiando el orden del ARIMA.
checkresiduals(manual_arima_model)
##
## Ljung-Box test
##
## data: Residuals from ARIMA(1,1,1)
## Q* = 118.5, df = 22, p-value = 3.22e-15
##
## Model df: 2. Total lags used: 24
#Pronóstico con el modelo ARIMA manual
manual_forecast <- forecast(manual_arima_model, h = length(test_ts))
# Crear dataframe para gráfico interactivo del pronóstico manual
manual_forecast_data <- data.frame(Tiempo = time(manual_forecast$mean),
Pronostico = as.numeric(manual_forecast$mean),
Observado = as.numeric(test_ts))
# Graficar pronóstico manual junto con los valores observados reales
p_manual <- ggplot(manual_forecast_data, aes(x = Tiempo)) +
geom_line(aes(y = Pronostico, color = "Pronóstico Manual")) +
geom_line(aes(y = Observado, color = "Observado")) +
ggtitle("Pronóstico Manual vs Observado") +
xlab("Tiempo") + ylab("Ingresos")
ggplotly(p_manual)
# Calcular métricas de evaluación del modelo manual
mae_manual <- mean(abs(manual_forecast$mean - test_ts), na.rm = TRUE)
rmse_manual <- sqrt(mean((manual_forecast$mean - test_ts)^2, na.rm = TRUE))
# Mostrar métricas de evaluación del modelo manual
cat("MAE Manual: ", mae_manual, "\n")
## MAE Manual: 2.361106
cat("RMSE Manual: ", rmse_manual, "\n")
## RMSE Manual: 2.414935
Las metricas de evealuación anteriores sugieren:
1. MAE (Mean Absolute Error) = 2.3611 Representa el error promedio en valores absolutos entre las predicciones y los valores reales. Indica, en promedio, cuánto se desvía la predicción del modelo con respecto a los datos reales. Interpretación: En promedio, el modelo comete un error de 2.36 unidades en cada predicción.
2. RMSE (Root Mean Squared Error) = 2.4149
Mide el error cuadrático medio y penaliza más los errores grandes.
Se interpreta de manera similar al MAE, pero al elevar los errores al cuadrado, da más peso a desviaciones grandes.
Interpretación: El error típico del modelo en cada predicción es 2.41 unidades.
# Identificación automática del mejor modelo ARIMA
auto_arima_model <- auto.arima(train_ts) # Busca automáticamente los mejores parámetros del modelo ARIMA
print(auto_arima_model)
## Series: train_ts
## ARIMA(1,2,2)(0,0,2)[12]
##
## Coefficients:
## ar1 ma1 ma2 sma1 sma2
## 0.7560 -1.4721 0.4841 0.2621 0.1430
## s.e. 0.1453 0.1803 0.1689 0.0624 0.0612
##
## sigma^2 = 0.1387: log likelihood = -122.12
## AIC=256.24 AICc=256.54 BIC=278.18
Estructura del modelo ARIMA(1,2,2)(0,0,2)[12] se interpreta así:
(1,2,2):
1 → Componente AR (Auto-Regresivo) de orden 1 (usa el valor previo de la serie para predecir). 2 → Diferenciación de orden 2 (necesita restar valores pasados dos veces para hacer la serie estacionaria). 2 → Componente MA (Media Móvil) de orden 2 (usa errores pasados para mejorar la predicción).
(0,0,2)[12]:
0,0,2 → Indica que hay un componente de media móvil estacional (SMA) de orden 2. [12] → La serie tiene una estacionalidad de 12 períodos (probablemente mensual si la serie es anual). 2. Coeficientes del modelo AR(1) = 0.7560 → Indica que el valor actual depende un 75.6% del valor anterior. MA(1) = -1.4721 y MA(2) = 0.4841 → Capturan el efecto de errores pasados en la predicción. SMA(1) = 0.2621 y SMA(2) = 0.1430 → Capturan patrones de error estacionales. Errores estándar (s.e.) → Son pequeños, lo que sugiere que los coeficientes son estadísticamente significativos.
3. Métricas de Evaluación Log-likelihood = -122.12 → Cuanto más alto, mejor el ajuste del modelo. AIC = 256.24 y BIC = 278.18 → Métricas de selección de modelos (valores más bajos indican mejor ajuste con menos parámetros).
Conclusión 🔹 El modelo ARIMA(1,2,2)(0,0,2)[12] sugiere una serie de tiempo con fuerte dependencia en los valores pasados y un patrón estacional de 12 períodos. 🔹 La presencia de diferenciación de orden 2 sugiere una tendencia fuerte que tuvo que ser eliminada para hacer la serie estacionaria. 🔹 El modelo parece razonablemente ajustado, pero su desempeño debe compararse con otros modelos usando métricas como RMSE o MAE.
Forma de correr modelo manualmente
# Cargar la librería forecast si no está cargada
library(forecast)
# Ajuste del modelo ARIMA con los parámetros encontrados
darima <- Arima(train_ts,
order = c(1,2,2), # (p,d,q) no estacional
seasonal = list(order = c(0,0,2), period = 12)) # (P,D,Q) estacional con periodo 12
# Resumen del modelo ajustado
summary(darima)
## Series: train_ts
## ARIMA(1,2,2)(0,0,2)[12]
##
## Coefficients:
## ar1 ma1 ma2 sma1 sma2
## 0.7560 -1.4721 0.4841 0.2621 0.1430
## s.e. 0.1453 0.1803 0.1689 0.0624 0.0612
##
## sigma^2 = 0.1387: log likelihood = -122.12
## AIC=256.24 AICc=256.54 BIC=278.18
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 0.01238208 0.3679304 0.2389882 0.00249301 0.295471 0.05940109
## ACF1
## Training set 0.006141105
Conclusión Puntos positivos: El modelo captura bien la tendencia y estacionalidad de los datos. Posibles mejoras: La ACF de los residuos muestra algunos valores fuera de las bandas, lo que indica que el modelo podría no haber eliminado toda la autocorrelación. El gran pico en 2020 puede afectar la precisión del modelo. Tal vez una intervención o modelado de valores atípicos ayude. Se podría probar un modelo ARIMA con otras combinaciones de parámetros o incluir variables exógenas si hay información adicional.
# Diagnóstico del modelo (los residuos deben ser ruido blanco)
checkresiduals(darima) # Verificar si los residuos son aleatorios y no presentan patrones
##
## Ljung-Box test
##
## data: Residuals from ARIMA(1,2,2)(0,0,2)[12]
## Q* = 26.6, df = 19, p-value = 0.1143
##
## Model df: 5. Total lags used: 24
# Generar pronóstico para el conjunto de prueba
forecast_arima <- forecast(darima, h = length(test_ts)) # Predecir los valores futuros
# Crear dataframe para gráfico interactivo del pronóstico
forecast_data <- data.frame(Tiempo = time(forecast_arima$mean),
Pronostico = as.numeric(forecast_arima$mean),
Observado = as.numeric(test_ts))
# Graficar pronóstico junto con los valores observados reales
p4 <- ggplot(forecast_data, aes(x = Tiempo)) +
geom_line(aes(y = Pronostico, color = "Pronóstico")) +
geom_line(aes(y = Observado, color = "Observado")) +
ggtitle("Pronóstico vs Observado") +
xlab("Tiempo") + ylab("IPC Cali")
ggplotly(p4) # Convertir el gráfico en interactivo