En este documento se realiza el pronóstico de la inflación para Bolivia para el año 2025, aplicando la metodología de Box-Jenkins (1976), para identificar un modelo autorregresivo integrado de media móvil ARIMA(p,d,q).
La Metodología de Box-Jenkins, consiste en:
Identificación: Consiste en identificar los valores (p,d,q), mediante los gráficos de la Función de autocorrelación (FAC) para identificar los procesos MA(q) y Función de autocorrelación parcial (FACP) para identificar los procesos AR(p).
Estimación: Consiste en estimar los parámetros o coeficientes del modelo.
Verificación o diagnóstico:Consiste en verificar que los residuos sean ruidos blanco.
Pronóstico: Si se cumplen los tres pasos previos, se puede utilizar el módulo para fines de pronóstico.
#librarías
#install.packages(c("pacman","seasonal",tidyverse","dplyr","readxl","janitor","ggplot2","lubridate","tseries","tsibble", "aTSA","feasts"))
pacman::p_load(
tidyverse,
dplyr,#Transformación de variables
readxl,#Importar datos de excel
janitor,#Para tablas
ggplot2,#Gráficos
lubridate,#Trabajar con Fechas
tseries,#Pruebas del modelo
tsibble,#Para trabajar con datos de series de tiempo
forecast,#Para pronostico
aTSA,#ajustar modelos de series de tiempo
feasts,#Complemento para autoplot,
seasonal
)
La información empleada es del Instituto Nacional de Estadística (INE). Para el propósito se emplea la inflación a doce meses por mes.
Como la información obtenida no viene en formato de tabla normalizada, se procederá a hacer las transformaciones pertinentes para lograr obtener una tabla normalizada. Ademas solo se empleara información a partir del año 2015.
#Importar la base de datos
Infl <- read_excel("~/Portafolio_R/Inflación/Infl_Base.xlsx", sheet="CUADRO Nº 1.4 VAR 12 MESES", skip = 4)
#Filtro de observaciones y anulación de la dinamización
Infl <- Infl %>%
filter(row_number()%in%c(seq(2,13))) %>%
pivot_longer(cols = !MES, names_to = "Años",values_to = "Inflación")
#Filtrar para años mayores a 2015
Infl <- Infl %>%
mutate(Años=as.integer(Años)) %>%
filter(Años>=2015)
#Transformación de Fecha
Infl <- Infl %>%
mutate(fecha=paste0("01/",MES,"/",Años),
Fecha=as.Date(fecha, format="%d/%B/%Y")) %>%
select(-fecha)
head(Infl,7)
## # A tibble: 7 × 4
## MES Años Inflación Fecha
## <chr> <int> <dbl> <date>
## 1 Enero 2015 5.94 2015-01-01
## 2 Enero 2016 2.39 2016-01-01
## 3 Enero 2017 3.68 2017-01-01
## 4 Enero 2018 2.93 2018-01-01
## 5 Enero 2019 1.43 2019-01-01
## 6 Enero 2020 1.21 2020-01-01
## 7 Enero 2021 1.17 2021-01-01
En esta sección se gráfica la serie con el objetivo de detectar patrones o identificar cualquier observación inusual.
ggplot(Infl, aes(x=Fecha, y=Inflación))+
geom_line(color="#539CBA", size=1)+
theme_bw()+
labs(title="Inflación Mensual de Bolivia, 2015-2024", subtitle = "(%)", x=NULL, y="Tasa")+
scale_x_date(date_breaks = "year", date_labels = "%Y")+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
plot.caption.position = "plot",
plot.title.position = "plot",
axis.text = element_text(size=14, color="gray12")
)
Infl %>%
mutate(Año_1=as.character(year(Fecha))) %>%
ggplot(aes(x=Año_1, y=Inflación, group=Año_1))+
geom_boxplot(color="#539CBA", alpha=.7,size=1, show.legend = F)+
geom_jitter(stat="identity",color="midnightblue", alpha=.7,size=1.5)+
theme_bw()+
labs(title="Evolución de la Distribución de la Inflación Anual, 2015-2024", subtitle = "(%)", x=NULL, y="Tasa")+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
plot.caption.position = "plot",
plot.title.position = "plot",
axis.text= element_text( color="gray12",size=14),
)
Infl %>%
mutate(Años_1=as.character(year(Fecha))) %>%
group_by(Años_1) %>%
summarise(Inf_Pro=mean(Inflación)) %>%
ggplot(aes(x=Años_1, y=Inf_Pro))+
geom_bar(stat = "identity", fill="#539CBA", alpha=.7, width = .7)+
theme_classic()+
geom_text(aes(label=paste(round(Inf_Pro,2),"%")), vjust=-.5, hjust="center",size=14, size.unit = "pt")+
labs(title="Inflación Promedio Anual, 2015-2024", subtitle = "(%)",x=NULL)+
scale_y_continuous(limits = c(0, 6), expand = c(0, 0))+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
plot.caption.position = "plot",
plot.title.position = "plot",
axis.line.y = element_blank(),
axis.ticks = element_blank(),
axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.text.x = element_text(color="black", size=14)
)
Infl_ts <- Infl %>%
select(-c(Años,MES)) %>%
mutate(Fecha=yearmonth(Fecha)) %>% #Variable tiempo
as_tsibble(
index = Fecha#Index
)
head(Infl_ts,7)
## # A tsibble: 7 x 2 [1M]
## Inflación Fecha
## <dbl> <mth>
## 1 5.94 2015 ene.
## 2 5.49 2015 feb.
## 3 4.75 2015 mar.
## 4 4.14 2015 abr.
## 5 4.08 2015 may.
## 6 3.19 2015 jun.
## 7 3.06 2015 jul.
Infl_ts %>%
gg_season(Inflación, labels = "both", size=1)+
theme_bw()+
labs(title="Inflación Mensual de Bolivia, 2015-2024", subtitle = "(%)")+
coord_cartesian(expand = TRUE)+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
plot.caption.position = "plot",
plot.title.position = "plot",
axis.text= element_text( color="gray12",size=14)
)
Infl_ts %>%
gg_subseries(Inflación)+#La media por Mes
theme_bw()+
labs(title="Inflación Mensual de Bolivia, 2015-2024", subtitle = "(%)")+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
axis.text.x = element_text(angle = 90),
plot.caption.position = "plot",
plot.title.position = "plot"
)
De acuerdo al análisis, no existe un efecto estacional marcado en la serie. Pero por rutina se procederá a desestacionalizar para eliminar algunos picos.
Para la desestacionalización se empleará la técnica creada por la Oficina del Censo de USA X-13ARIMA-SEATS.
Infl_ts<- Infl_ts %>%
model(x11 = X_13ARIMA_SEATS(Inflación ~ x11())) %>%
components() %>%select(-.model)
Infl_ts %>%
ggplot(aes(x = as_date(Fecha))) +
geom_line(aes(y = Inflación, colour = "Inflación"), size=0.7) +
geom_line(aes(y = season_adjust,colour = "season_adjust"), size=0.7) +
geom_line(aes(y = trend, colour = "trend"), size=0.7) +
scale_x_date(date_breaks = "year", date_labels = "%Y")+
scale_colour_manual(
values = c("#539CBA", "midnightblue", "#D55E00"),
breaks = c("Inflación", "season_adjust", "trend"))+
theme_bw()+
labs(title="Descoposicón de la Inflación Mensual de Bolivia, 2015-2024", subtitle = "(%)", x=NULL, y="%")+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
legend.position='top',
legend.justification='left',
legend.direction='horizontal',
legend.title = element_blank(),
plot.caption.position = "plot",
plot.title.position = "plot",
legend.location = "plot",
axis.text= element_text( color="gray12",size=14)
)
Infl_ts %>%
PACF(season_adjust, lag_max = 24) %>%
autoplot()+
theme_bw()+
labs(title = "Función de Autucorrelación Parcial")+
theme(
plot.title = element_text(face="bold", color = "#539CBA",size=18),
legend.position='top',
legend.justification='left',
legend.direction='horizontal',
legend.title = element_blank(),
plot.caption.position = "plot",
plot.title.position = "plot"
)
Infl_ts %>%
ACF(season_adjust, lag_max = 24) %>%
autoplot()+
theme_bw()+
labs(title = "Función de Autucorrelación")+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
legend.position='top',
legend.justification='left',
legend.direction='horizontal',
legend.title = element_blank(),
plot.caption.position = "plot",
plot.title.position = "plot",
)
Ho: La serie tiene raíz unitaria.
#ADF
Infl_ts %>%
features(season_adjust, adf.test)
## Augmented Dickey-Fuller Test
## alternative: stationary
##
## Type 1: no drift no trend
## lag ADF p.value
## [1,] 0 0.5370 0.797
## [2,] 1 0.0154 0.647
## [3,] 2 0.2017 0.701
## [4,] 3 0.6460 0.828
## [5,] 4 0.7311 0.852
## Type 2: with drift no trend
## lag ADF p.value
## [1,] 0 0.0430 0.958
## [2,] 1 -0.8820 0.738
## [3,] 2 -0.7574 0.782
## [4,] 3 -0.0259 0.953
## [5,] 4 0.1145 0.964
## Type 3: with drift and trend
## lag ADF p.value
## [1,] 0 0.530 0.990
## [2,] 1 -0.310 0.989
## [3,] 2 -0.123 0.990
## [4,] 3 0.863 0.990
## [5,] 4 1.206 0.990
## ----
## Note: in fact, p.value = 0.01 means p.value <= 0.01
## # A tibble: 1 × 3
## type1 type2 type3
## <list> <list> <list>
## 1 <dbl [5 × 3]> <dbl [5 × 3]> <dbl [5 × 3]>
Ho: La serie no es estacionaria o tiene raíz unitaria.
#P-P
Infl_ts %>%
features(season_adjust, pp.test)
## Phillips-Perron Unit Root Test
## alternative: stationary
##
## Type 1: no drift no trend
## lag Z_rho p.value
## 4 0.446 0.788
## -----
## Type 2: with drift no trend
## lag Z_rho p.value
## 4 -2.56 0.702
## -----
## Type 3: with drift and trend
## lag Z_rho p.value
## 4 1.71 0.99
## ---------------
## Note: p-value = 0.01 means p.value <= 0.01
## # A tibble: 3 × 3
## lag Z_rho p.value
## * <dbl> <dbl> <dbl>
## 1 4 0.446 0.788
## 2 4 -2.56 0.702
## 3 4 1.71 0.99
#Diferenciar
Infl_ts %>%
ggplot(aes(x=Fecha, y=difference(season_adjust)))+
geom_line()+
theme_bw()+
labs(title="Diff de la Inflación Mensual de Bolivia, 2015-2024", subtitle = "(%)")+
geom_hline(yintercept = 0, size=.5)+
scale_x_yearmonth(date_breaks = "1 year", date_labels = "%Y")+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
plot.caption.position = "plot",
plot.title.position = "plot"
)
De acuerdo con las pruebas formales e informales, se evidencia que la serie es integrada de orden (1).
De la función de autocorrelación parcial se obtiene el orden del modelo AR(p) y de la función de autocorrelación, el orden del modelo MA(q).
Infl_ts %>%
ACF(difference(season_adjust), lag_max = 24) %>%
autoplot()+
theme_bw()+
labs(title="Función de autocorrelación")+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
plot.caption.position = "plot",
plot.title.position = "plot"
)
Infl_ts %>%
PACF(difference(season_adjust), lag_max = 24) %>%
autoplot()+
theme_bw()+
labs(title="Función de autocorrelación Parcial")+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
plot.caption.position = "plot",
plot.title.position = "plot"
)
De acuerdo a las gráficas, sugiere un modelo ARIMA(3,1,1), aunque da la percepción de ser un modelo estacional dado que el pico en el rezago 12 es significativo, es decir, ARIMA(3,1,1)S(1,0,1)[12].
Ahora se estimarán los modelos candidatos posibles.
#Modelos
Model <- Infl_ts %>%
model(
arima011 = fable::ARIMA(season_adjust~ pdq(0,1,1)),
arima310 = fable::ARIMA(season_adjust ~ pdq(3,1,0)),
arima311 = fable::ARIMA(season_adjust~ pdq(3,1,1)),
#Estacionales
arima_311S = fable::ARIMA(season_adjust~ pdq(3,1,1)+PDQ(1,0,1)),
arima_011S = fable::ARIMA(season_adjust~ pdq(0,1,1)+PDQ(0,0,1)),
arima_310S = fable::ARIMA(season_adjust~ pdq(3,1,0)+PDQ(1,0,0)),
)
Para la elección del mejor modelo se hará uso de los criterios de información: Elegir el modelo cuyos estadísticos o criterios de información se minimicen y, para el caso de la razón de verosimilitud (LL), elegir el modelo cuyo estadístico o criterio de razón de verosimilitud se maximice (mayor valor).
#Encontrar el mejor modelo
glance(Model) %>% arrange(AICc) %>% select(.model:BIC)
## # A tibble: 5 × 6
## .model sigma2 log_lik AIC AICc BIC
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 arima310 0.248 -86.1 182. 183. 196.
## 2 arima011 0.257 -88.9 184. 184. 192.
## 3 arima_011S 0.257 -88.9 184. 184. 192.
## 4 arima_310S 0.259 -87.9 186. 186. 200.
## 5 arima311 0.260 -87.6 187. 188. 204.
De acuerdo con los resultados, el mejor modelo es el ARIMA(3,1,0), dado que más de un criterio los respalda.
Model %>%
select(arima310) %>%
gg_tsresiduals()+
labs(title="Residuos")
Ho: La serie es Ruido blanco.
augment(Model) %>%
filter(.model=="arima310") %>%
features(.innov, ljung_box, lag = 36, dof = 6)
## # A tibble: 1 × 3
## .model lb_stat lb_pvalue
## <chr> <dbl> <dbl>
## 1 arima310 37.2 0.170
Aunque no se rechaza la H0, existe presencia de valores atípicos.
resid <- augment(Model)
#Gráficos de los residuos
resid %>%
filter(.model=="arima310") %>%
ggplot(aes(x=Fecha,y=.innov))+
geom_line()+
geom_hline(yintercept = 0)+
labs(title = "Residuos del Modelo")+
theme_bw()+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
plot.caption.position = "plot",
plot.title.position = "plot"
)
La técnica empleada para detectar outlier será el diagrama de caja.
#Detectando oulier en resid
data <- augment(Model)%>%
filter(.model=="arima310")
Oulier <- boxplot(data$.innov)$out
Oulier
## [1] -2.138903 1.580320
#Ubicar los outliers
data %>%
filter(.innov%in%Oulier)
## # A tsibble: 2 x 6 [1M]
## # Key: .model [1]
## .model Fecha season_adjust .fitted .resid .innov
## <chr> <mth> <dbl> <dbl> <dbl> <dbl>
## 1 arima310 2019 dic. 1.36 3.50 -2.14 -2.14
## 2 arima310 2024 ago. 5.35 3.77 1.58 1.58
Los valores atípicos u outliers se encuentran en la fecha dic/2019 y Agos/2024
#Agregamos dummies a la data ts
Infl_ts <- Infl_ts %>%
mutate(dumm_dic19=ifelse(as.character(Fecha)=="2019 dic.",1,0),
dumm_ago24=ifelse(as.character(Fecha)=="2024 ago.", 1,0))
ggplot(Infl_ts,aes(x=Fecha))+
geom_line(aes(y=dumm_dic19), color="red")+
geom_line(aes(y=dumm_ago24))+
theme_bw()
#Modelo Corregido
Model_cor <- Infl_ts %>%
model(
arima011 = fable::ARIMA(season_adjust~ pdq(0,1,1)+dumm_dic19+dumm_ago24),
arima310 = fable::ARIMA(season_adjust ~ pdq(3,1,0)+dumm_dic19+dumm_ago24),
arima311 = fable::ARIMA(season_adjust~ pdq(3,1,1)+dumm_dic19+dumm_ago24),
auto = fable::ARIMA(season_adjust)
)
#Encontrar el mejor modelo
glance(Model_cor) %>% arrange(AICc) %>% select(.model:BIC)
## # A tibble: 4 × 6
## .model sigma2 log_lik AIC AICc BIC
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 arima311 0.218 -78.0 174. 176. 199.
## 2 arima310 0.239 -82.5 179. 180. 198.
## 3 auto 0.243 -86.1 180. 181. 191.
## 4 arima011 0.252 -86.3 183. 183. 197.
El mejor modelo es el ARIMA(3,1,1)
Model_cor %>%
select(arima311) %>%
gg_tsresiduals()
augment(Model_cor) %>%
filter(.model=="arima311") %>%
features(.innov, ljung_box, lag = 36, dof = 6)
## # A tibble: 1 × 3
## .model lb_stat lb_pvalue
## <chr> <dbl> <dbl>
## 1 arima311 26.4 0.655
Model_Sim <- glance(Model) %>% arrange(AICc) %>% select(.model:BIC) %>%
filter(.model=="arima310")
Model_Core <- glance(Model_cor) %>% arrange(AICc) %>% select(.model:BIC) %>%
filter(.model=="arima311")
#El mejor modelo es el Arima corregido
#Pruebas ex
bind_rows(Model_Sim, Model_Core)
## # A tibble: 2 × 6
## .model sigma2 log_lik AIC AICc BIC
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 arima310 0.248 -86.1 182. 183. 196.
## 2 arima311 0.218 -78.0 174. 176. 199.
De acuerdo al AIC y log_lik, el modelo final es el ARIMA(3,1,1).
#Creación de una data para el Pronostico
Data <- new_data(Infl_ts, 12) |>
mutate(
dumm_ago24 = 0,#Las variables exogenas
dumm_dic19= 0,
)
#Pronostico (Data)
forecast(Model_cor,Data) |>
filter(.model=="arima311") %>%
hilo()
## # A tsibble: 12 x 8 [1M]
## # Key: .model [1]
## .model Fecha
## <chr> <mth>
## 1 arima311 2025 ene.
## 2 arima311 2025 feb.
## 3 arima311 2025 mar.
## 4 arima311 2025 abr.
## 5 arima311 2025 may.
## 6 arima311 2025 jun.
## 7 arima311 2025 jul.
## 8 arima311 2025 ago.
## 9 arima311 2025 sep.
## 10 arima311 2025 oct.
## 11 arima311 2025 nov.
## 12 arima311 2025 dic.
## # ℹ 6 more variables: season_adjust <dist>, .mean <dbl>, dumm_ago24 <dbl>,
## # dumm_dic19 <dbl>, `80%` <hilo>, `95%` <hilo>
#Gráfico
forecast(Model_cor,Data) %>%
filter(.model=="arima311") %>%
autoplot(Infl_ts, color=alpha("#539CBA",.5))+
theme_bw()+
scale_x_yearmonth(date_breaks = "1 year", date_labels = "%Y")+
labs(title="Pronóstico de la Inflación de Bolivia, 2015-2025", subtitle = "(%)",
x=NULL, y="%")+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
legend.position='top',
legend.justification='left',
legend.direction='horizontal',
plot.caption.position = "plot",
plot.title.position = "plot",
legend.location = "plot",
axis.text= element_text( color="gray12",size=14)
)
#Pronostico de la Inflación anual promedio
Data2 <- bind_rows(Infl_ts,
forecast(Model_cor,Data) %>%
filter(.model=="arima311") %>%
hilo())
Data2 %>%
mutate(Años=as.character(year(Fecha))) %>%
group_by(Años) %>%
as_tibble() %>%
select(-Fecha) %>%
summarise(Infl=mean(Inflación, na.rm = TRUE), Pron=mean(.mean, na.rm=TRUE)) %>%
mutate(is_2025=ifelse(Años==2025, TRUE, FALSE)) %>%
ggplot(aes(x=Años,y=Infl, fill=is_2025))+
geom_bar(stat = "identity", show.legend = F)+
geom_bar(aes(y=Pron), stat = "identity", show.legend = F)+
scale_fill_manual(breaks = c(FALSE, TRUE), values = c("#BAD1D6","#539CBA"))+
coord_cartesian(expand = FALSE,
ylim = c(0,8))+
theme_classic()+
theme(
plot.title = element_text(face="bold", color = "#539CBA", size=18),
axis.line.y = element_blank(),
axis.ticks = element_blank(),
axis.text.x = element_text(size=14, color="gray12"),
axis.text.y = element_blank(),
plot.caption.position = "plot",
plot.title.position = "plot"
)+
geom_text(aes(y=Pron,label=ifelse(is_2025,paste(round(Pron,2),"%"),NA)), vjust=-.5, hjust="center", color="#539CBA",size=12, size.unit = "pt")+
geom_text(aes(label=paste(round(Infl,2),"%")), vjust=-.5, hjust="center", size=12, size.unit = "pt")+
labs(title="Inflación Promedio Anual, 2015-2025", subtitle = "(%)",x=NULL,y=NULL)
De acuerdo a los datos y la aplicación de la metodología de Box-Jenkins (1976):
El modelo ARIMA empleado es el ARIMA(3,1,1) corregido por la influencia de valores atípicos.
La inflación promedio pronosticada para el año 2025 alcanzará el 7.48%, 2.4 pp por encima del promedio registrado en 2024.
Además, de acuerdo con los datos, la inflación en el último semestre del año 2024 presentó una dispersión marcada, terminando el año con una inflación de 9.97%