Esta clase sigue una lógica progresiva:
Idea central: en series de tiempo no se debe comenzar preguntando “¿qué modelo uso?”.
Primero se debe preguntar: ¿qué estructura tiene la serie?
Una serie de tiempo es una sucesión de observaciones ordenadas cronológicamente:
\[ Y_1,Y_2,\ldots,Y_t,\ldots,Y_T. \]
La característica que diferencia a una serie temporal de una muestra transversal es que las observaciones pueden estar relacionadas con su propio pasado.
Por ejemplo:
Un pronóstico intenta describir lo que probablemente ocurrirá, dadas la información histórica y las relaciones que pueden ser modeladas.
Una meta representa lo que se desea que ocurra.
La planificación consiste en decidir acciones que permitan responder a los pronósticos y acercar los resultados a las metas.
En ciencias actuariales esta distinción es esencial. Una aseguradora puede tener como meta reducir la siniestralidad, pero un modelo de pronóstico debe estimar la siniestralidad futura con independencia de esa aspiración.
Para que toda la clase sea reproducible, construiremos un portafolio asegurador mensual ficticio.
La serie contiene:
set.seed(2026)
n <- 144 # 12 años de información mensual
t <- 1:n
mes <- rep(1:12, length.out = n)
fecha <- seq(as.Date("2015-01-01"), by = "month", length.out = n)
# Factores estacionales de frecuencia
factor_est_frec <- c(
0.88, 0.90, 0.93, 0.97,
1.02, 1.07, 1.12, 1.15,
1.10, 1.03, 0.96, 0.91
)
# Factores estacionales de severidad
factor_est_sev <- c(
0.96, 0.97, 0.98, 0.99,
1.00, 1.02, 1.03, 1.05,
1.04, 1.02, 0.99, 0.95
)
# Exposición: número de pólizas-mes
exposicion <- round(
100000 +
500 * t +
2000 * sin(2 * pi * t / 12)
)
# Frecuencia mensual por unidad de exposición
tasa_frecuencia <- 0.018 *
exp(0.0012 * t) *
factor_est_frec[mes]
# Número de siniestros
n_siniestros <- rpois(
n,
lambda = exposicion * tasa_frecuencia
)
# Severidad promedio, con inflación
severidad_media <- 3500 *
exp(0.004 * t) *
factor_est_sev[mes] *
exp(rnorm(n, mean = 0, sd = 0.05))
# Indicador de evento extraordinario
catastrofe <- rep(0, n)
catastrofe[c(70, 115)] <- 1
# Siniestros agregados
siniestros_pagados <- n_siniestros * severidad_media
# Impacto de los eventos extraordinarios
siniestros_pagados[catastrofe == 1] <-
siniestros_pagados[catastrofe == 1] * c(1.75, 1.55)
datos <- data.frame(
fecha,
t,
mes,
exposicion,
n_siniestros,
severidad_media,
siniestros_pagados,
catastrofe
)
head(datos)Creamos objetos ts.
serie_n <- ts(
datos$n_siniestros,
start = c(2015, 1),
frequency = 12
)
serie_pago <- ts(
datos$siniestros_pagados,
start = c(2015, 1),
frequency = 12
)
serie_sev <- ts(
datos$severidad_media,
start = c(2015, 1),
frequency = 12
)La frecuencia 12 indica que el patrón estacional, si
existe, puede repetirse cada 12 observaciones.
Un modelo temporal debe ser una consecuencia del comportamiento observado en los datos.
autoplot(serie_n) +
labs(
title = "Número mensual de siniestros",
x = "Año",
y = "Número de siniestros"
)Antes de estimar cualquier modelo, pregúntese:
Existe tendencia cuando se observa un incremento o disminución de largo plazo.
La tendencia no tiene que ser lineal.
Podemos representar conceptualmente:
\[ T_t = f(t), \]
donde \(f(t)\) describe la evolución de largo plazo.
Ejemplo actuarial:
Una cartera en expansión puede producir una tendencia creciente en el número de siniestros aun si la tasa de siniestralidad permanece estable.
Existe estacionalidad cuando un comportamiento se repite con una frecuencia fija y conocida.
Para datos mensuales:
\[ m=12. \]
Para datos trimestrales:
\[ m=4. \]
Ejemplos actuariales:
Visualicemos el patrón mensual.
seasonplot(
serie_n,
year.labels = TRUE,
main = "Comportamiento estacional del número de siniestros",
ylab = "Siniestros",
xlab = "Mes"
)También podemos comparar los meses directamente.
Un ciclo también representa aumentos y disminuciones, pero, a diferencia de la estacionalidad, su duración no tiene una frecuencia fija.
Ejemplos:
Por tanto:
\[ \text{estacionalidad} \neq \text{ciclo}. \]
La estacionalidad tiene periodicidad fija. El ciclo no.
Representa movimientos que no son explicados por los demás componentes:
\[ R_t. \]
Puede incluir:
Una serie puede representarse como la combinación de varios componentes.
\[ Y_t = T_t + S_t + R_t. \]
Se utiliza cuando la amplitud de la estacionalidad es aproximadamente constante.
Ejemplo:
Si un mes particular agrega aproximadamente 150 siniestros adicionales independientemente del nivel general de la cartera, una representación aditiva puede ser razonable.
\[ Y_t = T_t S_t R_t. \]
Se utiliza cuando la variación estacional cambia proporcionalmente con el nivel de la serie.
Ejemplo:
Si diciembre suele generar aproximadamente un 15% más de reclamaciones que un mes promedio, la estructura es naturalmente multiplicativa.
Aplicando logaritmos:
\[ Y_t=T_tS_tR_t \]
implica
\[ \log(Y_t) = \log(T_t)+ \log(S_t)+ \log(R_t). \]
Por eso una estructura multiplicativa puede convertirse en una estructura aditiva en escala logarítmica.
Los promedios móviles permiten reducir fluctuaciones de corto plazo para revelar el componente tendencia-ciclo.
Para \(m=2k+1\):
\[ \widehat{T}_t = \frac{1}{m} \sum_{j=-k}^{k}Y_{t+j}. \]
Por ejemplo, un promedio móvil de tres periodos es:
\[ \widehat{T}_t = \frac{Y_{t-1}+Y_t+Y_{t+1}}{3}. \]
Suponga:
\[ Y_{t-1}=1800,\qquad Y_t=2100,\qquad Y_{t+1}=2400. \]
Entonces:
\[ \widehat T_t = \frac{1800+2100+2400}{3} = 2100. \]
El promedio móvil elimina parte de la variación accidental y mantiene el movimiento de nivel.
ma3 <- ma(serie_n, order = 3)
autoplot(serie_n, series = "Serie") +
autolayer(ma3, series = "MA(3)") +
labs(
title = "Suavizamiento mediante promedio móvil",
x = "Año",
y = "Número de siniestros",
colour = ""
)En datos mensuales \(m=12\). Un promedio móvil de 12 términos queda centrado entre dos meses.
Por ello se utiliza un promedio móvil centrado, denominado \(2\times12\)-MA.
Conceptualmente:
Los pesos equivalentes son:
\[ \frac{1}{24}, \frac{1}{12}, \ldots, \frac{1}{12}, \frac{1}{24}. \]
Los extremos reciben la mitad del peso.
ma12 <- ma(serie_n, order = 12)
autoplot(serie_n, series = "Serie") +
autolayer(ma12, series = "MA(12) centrado") +
labs(
title = "Estimación de tendencia-ciclo con promedio móvil",
x = "Año",
y = "Número de siniestros",
colour = ""
)Si
\[ Y_t=T_t+S_t+R_t, \]
podemos seguir cuatro pasos.
\[ \widehat T_t. \]
\[ Y_t-\widehat T_t = S_t+R_t. \]
Para cada mes \(j\), promediamos los valores sin tendencia correspondientes a ese mes:
\[ \widehat S_j = \frac{1}{n_j} \sum_{t\in j} (Y_t-\widehat T_t). \]
Si
\[ Y_t=T_tS_tR_t, \]
entonces después de estimar tendencia:
\[ \frac{Y_t}{\widehat T_t} = S_tR_t. \]
El índice estacional se obtiene promediando las razones correspondientes a cada estación.
Finalmente:
\[ \widehat R_t = \frac{Y_t}{ \widehat T_t\widehat S_t }. \]
STL significa:
Seasonal and Trend decomposition using Loess.
Tiene ventajas importantes:
En una descomposición aditiva:
\[ A_t=Y_t-S_t. \]
serie_ajustada <- seasadj(ajuste_stl)
autoplot(serie_ajustada) +
labs(
title = "Número de siniestros ajustado estacionalmente",
x = "Año",
y = "Siniestros"
)Interpretación:
Si la serie ajustada aumenta, ese crecimiento no puede atribuirse únicamente a la estacionalidad.
Una descomposición STL permite cuantificar la intensidad de estos patrones.
Si:
\[ Y_t=T_t+S_t+R_t, \]
la fuerza de la tendencia puede definirse como:
\[ F_T = \max \left( 0, 1- \frac{\operatorname{Var}(R_t)} {\operatorname{Var}(T_t+R_t)} \right). \]
La fuerza de la estacionalidad:
\[ F_S = \max \left( 0, 1- \frac{\operatorname{Var}(R_t)} {\operatorname{Var}(S_t+R_t)} \right). \]
Ambos indicadores toman valores aproximadamente entre 0 y 1.
componentes <- ajuste_stl$time.series
Tt <- componentes[, "trend"]
St <- componentes[, "seasonal"]
Rt <- componentes[, "remainder"]
F_tendencia <- max(
0,
1 - var(Rt, na.rm = TRUE) /
var(Tt + Rt, na.rm = TRUE)
)
F_estacional <- max(
0,
1 - var(Rt, na.rm = TRUE) /
var(St + Rt, na.rm = TRUE)
)
data.frame(
componente = c("Tendencia", "Estacionalidad"),
fuerza = c(F_tendencia, F_estacional)
)Antes de utilizar un modelo sofisticado debemos construir métodos simples que funcionen como benchmark.
Si un modelo complejo no supera un método ingenuo fuera de muestra, su utilidad predictiva es cuestionable.
\[ \widehat Y_{T+h|T} = \bar Y = \frac{1}{T} \sum_{t=1}^{T}Y_t. \]
\[ \widehat Y_{T+h|T} = Y_T. \]
Para periodicidad \(m\):
\[ \widehat Y_{T+h|T} = Y_{T+h-m(k+1)}. \]
En términos simples: para pronosticar un enero utilizamos el enero más reciente; para febrero, el febrero más reciente, etc.
No son exactamente lo mismo.
Un residuo suele calcularse dentro de la muestra de entrenamiento:
\[ e_t=Y_t-\widehat Y_{t|t-1}. \]
Un error de pronóstico fuera de muestra es:
\[ e_{T+h} = Y_{T+h} - \widehat Y_{T+h|T}. \]
La evaluación fuera de muestra es especialmente importante porque reproduce mejor la situación real de pronóstico.
\[ MAE= \frac{1}{n} \sum_{t=1}^{n} |e_t|. \]
Interpretación: error absoluto promedio en las mismas unidades de la serie.
\[ RMSE = \sqrt{ \frac{1}{n} \sum_{t=1}^{n} e_t^2 }. \]
Penaliza con mayor fuerza los errores grandes.
\[ MAPE = \frac{100}{n} \sum_{t=1}^{n} \left| \frac{e_t}{Y_t} \right|. \]
Debe utilizarse con cuidado si existen valores iguales o cercanos a cero.
La idea del MASE es escalar el error respecto a un benchmark naïve.
Para una serie no estacional:
\[ MASE = \frac{ \frac{1}{n}\sum|e_t| }{ \frac{1}{T-1} \sum_{t=2}^{T} |Y_t-Y_{t-1}| }. \]
Interpretación aproximada:
Nunca deberíamos comparar modelos únicamente por su ajuste histórico.
Usaremos 2015–2025 como entrenamiento y 2026 como prueba.
train_n <- window(serie_n, end = c(2025, 12))
test_n <- window(serie_n, start = c(2026, 1))
autoplot(serie_n) +
autolayer(train_n, series = "Entrenamiento") +
autolayer(test_n, series = "Prueba") +
labs(
title = "Separación entrenamiento/prueba",
x = "Año",
y = "Siniestros",
colour = ""
)b_media <- meanf(train_n, h = length(test_n))
b_naive <- naive(train_n, h = length(test_n))
b_snaive <- snaive(train_n, h = length(test_n))
b_drift <- rwf(train_n, h = length(test_n), drift = TRUE)
tabla_benchmark <- rbind(
Media = accuracy(b_media, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
Naive = accuracy(b_naive, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
SNaive = accuracy(b_snaive, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
Drift = accuracy(b_drift, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")]
)
knitr::kable(
round(tabla_benchmark, 3),
caption = "Precisión de métodos de referencia"
)| RMSE | MAE | MAPE | MASE | |
|---|---|---|---|---|
| Media | 1035.256 | 985.568 | 26.666 | 6.167 |
| Naive | 578.643 | 484.167 | 12.709 | 3.030 |
| SNaive | 176.766 | 169.083 | 4.683 | 1.058 |
| Drift | 510.414 | 411.024 | 10.717 | 2.572 |
La validación cruzada convencional mezcla observaciones del pasado y del futuro, lo cual no es apropiado.
En series de tiempo se utiliza un origen móvil de pronóstico.
Esquemáticamente:
\[ \{Y_1,\ldots,Y_t\} \rightarrow \widehat Y_{t+1|t}. \]
Luego avanzamos un periodo:
\[ \{Y_1,\ldots,Y_t,Y_{t+1}\} \rightarrow \widehat Y_{t+2|t+1}. \]
f_snaive <- function(y, h) {
snaive(y, h = h)
}
errores_cv <- tsCV(
serie_n,
forecastfunction = f_snaive,
h = 1
)
RMSE_cv <- sqrt(mean(errores_cv^2, na.rm = TRUE))
RMSE_cv## [1] 175.9212
Los métodos exponenciales construyen pronósticos como promedios ponderados de observaciones históricas, asignando mayor peso a los datos recientes.
SES es apropiado cuando no existe tendencia ni estacionalidad clara.
La ecuación de actualización del nivel es:
\[ \ell_t = \alpha Y_t + (1-\alpha)\ell_{t-1}, \qquad 0\leq\alpha\leq1. \]
El pronóstico es:
\[ \widehat Y_{t+h|t} = \ell_t. \]
Partimos de:
\[ \ell_t = \alpha Y_t + (1-\alpha)\ell_{t-1}. \]
Sustituimos:
\[ \ell_{t-1} = \alpha Y_{t-1} + (1-\alpha)\ell_{t-2}. \]
Entonces:
\[ \ell_t = \alpha Y_t + \alpha(1-\alpha)Y_{t-1} + (1-\alpha)^2\ell_{t-2}. \]
Repitiendo:
\[ \ell_t = \alpha Y_t + \alpha(1-\alpha)Y_{t-1} + \alpha(1-\alpha)^2Y_{t-2} +\cdots. \]
Los pesos son:
\[ \alpha, \quad \alpha(1-\alpha), \quad \alpha(1-\alpha)^2, \quad \ldots \]
y decrecen exponencialmente.
Si:
\[ \alpha\approx1, \]
el modelo reacciona fuertemente a la información reciente.
Si:
\[ \alpha\approx0, \]
el nivel cambia lentamente.
## Simple exponential smoothing
##
## Call:
## ses(y = train_n, h = 12)
##
## Smoothing parameters:
## alpha = 0.9999
##
## Initial states:
## l = 1630.1606
##
## sigma: 137.8357
##
## AIC AICc BIC
## 1948.995 1949.183 1957.643
Cuando existe tendencia necesitamos un segundo estado.
Nivel:
\[ \ell_t = \alpha Y_t + (1-\alpha)(\ell_{t-1}+b_{t-1}). \]
Tendencia:
\[ b_t = \beta^*(\ell_t-\ell_{t-1}) + (1-\beta^*)b_{t-1}. \]
Pronóstico:
\[ \widehat Y_{t+h|t} = \ell_t+hb_t. \]
Una tendencia lineal puede resultar demasiado agresiva cuando el horizonte aumenta.
Se introduce:
\[ 0<\phi<1. \]
Pronóstico:
\[ \widehat Y_{t+h|t} = \ell_t+ (\phi+\phi^2+\cdots+\phi^h)b_t. \]
Cuando \(h\) crece, el efecto adicional de la tendencia se reduce.
Para una serie estacional con amplitud aproximadamente constante:
Pronóstico:
\[ \widehat Y_{t+h|t} = \ell_t+hb_t+s_{t+h-m(k+1)}. \]
Nivel:
\[ \ell_t = \alpha(Y_t-s_{t-m}) + (1-\alpha)(\ell_{t-1}+b_{t-1}). \]
Tendencia:
\[ b_t = \beta^*(\ell_t-\ell_{t-1}) + (1-\beta^*)b_{t-1}. \]
Estacionalidad:
\[ s_t = \gamma(Y_t-\ell_{t-1}-b_{t-1}) + (1-\gamma)s_{t-m}. \]
Si la amplitud estacional es proporcional al nivel:
\[ \widehat Y_{t+h|t} = (\ell_t+hb_t) s_{t+h-m(k+1)}. \]
Nivel:
\[ \ell_t = \alpha \frac{Y_t}{s_{t-m}} + (1-\alpha)(\ell_{t-1}+b_{t-1}). \]
Tendencia:
\[ b_t = \beta^*(\ell_t-\ell_{t-1}) + (1-\beta^*)b_{t-1}. \]
Estacionalidad:
\[ s_t = \gamma \frac{Y_t}{\ell_{t-1}+b_{t-1}} + (1-\gamma)s_{t-m}. \]
Aplicaremos el método a los pagos agregados.
train_pago <- window(serie_pago, end = c(2025, 12))
test_pago <- window(serie_pago, start = c(2026, 1))
fit_hw_mult <- hw(
train_pago,
seasonal = "multiplicative",
h = 12
)
autoplot(fit_hw_mult)ETS representa:
\[ \text{Error} + \text{Trend} + \text{Seasonal}. \]
Las letras más habituales son:
N: ninguno;A: aditivo;M: multiplicativo;Ad: tendencia aditiva amortiguada.Ejemplos:
La gran ventaja de ets() es que puede seleccionar
automáticamente una estructura utilizando criterios de información.
## ETS(M,A,M)
##
## Call:
## ets(y = train_n)
##
## Smoothing parameters:
## alpha = 0.035
## beta = 0.0069
## gamma = 0.0001
##
## Initial states:
## l = 1792.0729
## b = 13.4683
## s = 0.9166 0.9536 1.0189 1.0826 1.1343 1.1025
## 1.067 1.0268 0.9698 0.9342 0.9061 0.8877
##
## sigma: 0.0211
##
## AIC AICc BIC
## 1716.441 1721.810 1765.449
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 1.24594 51.03674 41.01155 -0.01765761 1.595032 0.2566162
## ACF1
## Training set -0.02519064
AIC/AICc es útil para comparar modelos dentro de una misma familia.
No debe utilizarse directamente para decidir que un ETS es mejor que un ARIMA cuando las verosimilitudes no son comparables en la misma escala.
Para comparar familias distintas es preferible usar:
Un buen modelo debe dejar residuos aproximadamente impredecibles.
Idealmente:
\[ E(e_t)=0, \]
\[ \operatorname{Var}(e_t)=\sigma^2, \]
y
\[ \operatorname{Corr}(e_t,e_{t-k})\approx0. \]
##
## Ljung-Box test
##
## data: Residuals from ETS(M,A,M)
## Q* = 26.406, df = 24, p-value = 0.3329
##
## Model df: 0. Total lags used: 24
El test de Ljung-Box contrasta, de forma conjunta, autocorrelaciones de varios rezagos.
Una hipótesis simplificada es:
\[ H_0: \rho_1=\rho_2=\cdots=\rho_K=0. \]
Si no rechazamos \(H_0\), no encontramos evidencia suficiente de autocorrelación residual en los rezagos evaluados.
fit_ses_test <- ses(train_n, h = 12)
fit_holt_test <- holt(train_n, h = 12)
fit_damped_test <- holt(train_n, h = 12, damped = TRUE)
fit_hw_add_test <- hw(train_n, h = 12, seasonal = "additive")
fit_ets_test <- forecast(ets(train_n), h = 12)
tabla_exp <- rbind(
SES = accuracy(fit_ses_test, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
Holt = accuracy(fit_holt_test, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
Holt_amortiguado = accuracy(fit_damped_test, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
Holt_Winters = accuracy(fit_hw_add_test, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
ETS = accuracy(fit_ets_test, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")]
)
knitr::kable(
round(tabla_exp, 3),
caption = "Comparación de suavizamiento exponencial"
)| RMSE | MAE | MAPE | MASE | |
|---|---|---|---|---|
| SES | 578.620 | 484.140 | 12.708 | 3.029 |
| Holt | 2261.050 | 2033.239 | 55.051 | 12.722 |
| Holt_amortiguado | 1225.302 | 1127.745 | 30.295 | 7.056 |
| Holt_Winters | 64.850 | 53.164 | 1.494 | 0.333 |
| ETS | 47.549 | 35.046 | 0.989 | 0.219 |
En ETS preguntamos principalmente:
¿Cómo evolucionan el nivel, la tendencia y la estacionalidad?
En ARIMA preguntamos:
¿Qué dependencia existe entre la serie actual, sus valores pasados y sus errores pasados?
Por ello ambos enfoques son complementarios.
Una serie es estacionaria, en sentido débil, si aproximadamente:
\[ E(Y_t)=\mu, \]
\[ \operatorname{Var}(Y_t)=\sigma^2, \]
y
\[ \operatorname{Cov}(Y_t,Y_{t-k}) = \gamma_k, \]
donde la covarianza depende del rezago \(k\), no del tiempo específico \(t\).
Una serie con tendencia determinística o estacionalidad fija generalmente no es estacionaria.
\[ \Delta Y_t = Y_t-Y_{t-1}. \]
La diferenciación elimina niveles y puede reducir tendencias.
diff1 <- diff(serie_n)
autoplot(diff1) +
labs(
title = "Primera diferencia del número de siniestros",
x = "Año",
y = expression(Delta * Y[t])
)\[ \Delta^2Y_t = \Delta Y_t-\Delta Y_{t-1}. \]
Desarrollando:
\[ \Delta^2Y_t = (Y_t-Y_{t-1}) - (Y_{t-1}-Y_{t-2}), \]
por tanto:
\[ \boxed{ \Delta^2Y_t = Y_t-2Y_{t-1}+Y_{t-2} }. \]
Debe evitarse la sobrediferenciación, porque puede introducir estructura artificial y aumentar la varianza.
Para datos mensuales:
\[ \Delta_{12}Y_t = Y_t-Y_{t-12}. \]
diff12 <- diff(serie_n, lag = 12)
autoplot(diff12) +
labs(
title = "Diferencia estacional de orden 12",
x = "Año",
y = expression(Y[t] - Y[t-12])
)\[ (1-B)(1-B^{12})Y_t. \]
Desarrollando:
\[ (1-B)(1-B^{12}) = 1-B-B^{12}+B^{13}. \]
Por tanto:
\[ \boxed{ Y_t - Y_{t-1} - Y_{t-12} + Y_{t-13} }. \]
Definimos:
\[ BY_t=Y_{t-1}. \]
Entonces:
\[ B^2Y_t=Y_{t-2}. \]
La primera diferencia puede escribirse:
\[ \Delta Y_t = (1-B)Y_t. \]
La diferencia de orden \(d\):
\[ \Delta^dY_t = (1-B)^dY_t. \]
La diferencia estacional:
\[ \Delta_mY_t = (1-B^m)Y_t. \]
Esta notación simplifica mucho la representación ARIMA.
Podemos combinar:
ndiffs();nsdiffs().## [1] 1
## [1] 1
En una versión habitual de KPSS:
\[ H_0: \text{la serie es estacionaria}. \]
\[ H_1: \text{la serie no es estacionaria}. \]
##
## KPSS Test for Level Stationarity
##
## data: serie_n
## KPSS Level = 2.7101, Truncation lag parameter = 4, p-value = 0.01
Si el valor \(p\) es pequeño, existe evidencia contra la estacionariedad bajo la especificación utilizada.
No se recomienda decidir únicamente por una prueba. La decisión debe combinar teoría, gráficos y diagnóstico.
La autocorrelación de rezago \(k\) mide asociación lineal entre:
\[ Y_t \quad\text{y}\quad Y_{t-k}. \]
Una expresión muestral es:
\[ r_k = \frac{ \sum_{t=k+1}^{T} (Y_t-\bar Y) (Y_{t-k}-\bar Y) }{ \sum_{t=1}^{T} (Y_t-\bar Y)^2 }. \]
Una ACF que disminuye lentamente suele sugerir no estacionariedad.
Picos en los rezagos 12, 24, 36, … sugieren estructura estacional mensual.
La PACF mide la relación entre:
\[ Y_t \quad\text{y}\quad Y_{t-k} \]
después de controlar el efecto lineal de:
\[ Y_{t-1}, Y_{t-2}, \ldots, Y_{t-k+1}. \]
Un modelo AR(\(p\)) es:
\[ Y_t = c+ \phi_1Y_{t-1} + \phi_2Y_{t-2} +\cdots+ \phi_pY_{t-p} + \varepsilon_t. \]
Para AR(1):
\[ Y_t = c+\phi_1Y_{t-1}+\varepsilon_t. \]
Si \(|\phi_1|<1\), el proceso AR(1) es estacionario.
Tomando esperanza:
\[ E(Y_t) = c+\phi_1E(Y_{t-1}). \]
Bajo estacionariedad:
\[ E(Y_t)=E(Y_{t-1})=\mu. \]
Entonces:
\[ \mu=c+\phi_1\mu, \]
\[ \mu(1-\phi_1)=c, \]
y:
\[ \boxed{ \mu= \frac{c}{1-\phi_1} }. \]
No debe confundirse con el promedio móvil utilizado para suavizar una serie.
Un MA(\(q\)) utiliza errores pasados:
\[ Y_t = c+ \varepsilon_t+ \theta_1\varepsilon_{t-1} + \cdots+ \theta_q\varepsilon_{t-q}. \]
Para MA(1):
\[ Y_t = c+\varepsilon_t+ \theta_1\varepsilon_{t-1}. \]
La idea es que un shock puede afectar no solo el periodo actual, sino algunos periodos siguientes.
Reglas heurísticas para series estacionarias:
| Modelo | ACF | PACF |
|---|---|---|
| AR(p) | decae gradualmente | corte aproximado después de \(p\) |
| MA(q) | corte aproximado después de \(q\) | decae gradualmente |
| ARMA(p,q) | decae | decae |
Estas reglas no son mecánicas. Son una guía para construir modelos candidatos.
ARIMA combina:
Sea:
\[ W_t=\Delta^dY_t. \]
Entonces:
\[ W_t = c+ \phi_1W_{t-1} +\cdots+ \phi_pW_{t-p} + \varepsilon_t + \theta_1\varepsilon_{t-1} +\cdots+ \theta_q\varepsilon_{t-q}. \]
La notación es:
\[ ARIMA(p,d,q). \]
Interpretación:
Definimos:
\[ \phi(B) = 1-\phi_1B-\cdots-\phi_pB^p, \]
y:
\[ \theta(B) = 1+\theta_1B+\cdots+\theta_qB^q. \]
Entonces:
\[ \boxed{ \phi(B)(1-B)^dY_t = c+\theta(B)\varepsilon_t }. \]
Esta es una de las formas más compactas de expresar ARIMA.
Algunos modelos conocidos aparecen como casos especiales:
\[ ARIMA(0,0,0) \]
corresponde a ruido blanco.
\[ ARIMA(1,0,0) \]
es un AR(1).
\[ ARIMA(0,0,1) \]
es un MA(1).
\[ ARIMA(0,1,0) \]
sin constante corresponde a una caminata aleatoria:
\[ Y_t=Y_{t-1}+\varepsilon_t. \]
Un modelo estacional se escribe:
\[ ARIMA(p,d,q)(P,D,Q)_m. \]
Donde:
Para datos mensuales:
\[ m=12. \]
La forma general puede escribirse:
\[ \Phi(B^m)\phi(B) (1-B)^d (1-B^m)^D Y_t = \Theta(B^m)\theta(B) \varepsilon_t. \]
Considere:
\[ ARIMA(1,1,1)(1,1,1)_{12}. \]
Entonces:
\[ (1-\phi_1B) (1-\Phi_1B^{12}) (1-B) (1-B^{12})Y_t = (1+\theta_1B) (1+\Theta_1B^{12}) \varepsilon_t. \]
Esta ecuación contiene simultáneamente:
La secuencia recomendada es:
Cuando la variabilidad crece con el nivel, una transformación puede ser útil.
La transformación Box-Cox es:
\[ w_t= \begin{cases} \log(Y_t), & \lambda=0,\\[6pt] \dfrac{Y_t^\lambda-1}{\lambda}, & \lambda\neq0. \end{cases} \]
## [1] 0.00006610696
Si \(\lambda\) se aproxima a cero, una transformación logarítmica puede resultar adecuada.
Aplicamos diferencia estacional:
Aplicamos además primera diferencia:
serie_arima_est <- diff(
diff(log(train_pago), lag = 12)
)
ggtsdisplay(
serie_arima_est,
main = "Serie con diferencia estacional y regular"
)Ahora la ACF y PACF se interpretan sobre una serie mucho más cercana a estacionaria.
Como ejemplo didáctico, estimaremos:
\[ ARIMA(1,1,1)(0,1,1)_{12}. \]
fit_arima_manual <- Arima(
train_pago,
order = c(1, 1, 1),
seasonal = c(0, 1, 1),
lambda = 0,
biasadj = TRUE
)
summary(fit_arima_manual)## Series: train_pago
## ARIMA(1,1,1)(0,1,1)[12]
## Box Cox transformation: lambda= 0
##
## Coefficients:
## ar1 ma1 sma1
## -0.0555 -0.9561 -0.9966
## s.e. 0.0963 0.0483 0.8827
##
## sigma^2 = 0.006723: log likelihood = 114.41
## AIC=-220.81 AICc=-220.46 BIC=-209.7
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE ACF1
## Training set -124623 1529211 690946.6 -1.474474 4.573652 0.4173999 -0.06166245
No se debe elegir este orden simplemente porque “se ve sofisticado”. En una aplicación real debe justificarse con:
##
## Ljung-Box test
##
## data: Residuals from ARIMA(1,1,1)(0,1,1)[12]
## Q* = 15.444, df = 21, p-value = 0.8001
##
## Model df: 3. Total lags used: 24
Un ARIMA útil debería dejar poco patrón sistemático en los residuos.
Si la ACF residual mantiene picos claros, probablemente queda información temporal sin modelar.
fc_arima_manual <- forecast(
fit_arima_manual,
h = 12
)
autoplot(fc_arima_manual) +
labs(
title = "Pronóstico SARIMA de siniestros pagados",
x = "Año",
y = "Monto"
)Los intervalos de predicción normalmente se amplían con el horizonte porque la incertidumbre acumulada aumenta.
fit_auto_arima <- auto.arima(
train_pago,
seasonal = TRUE,
lambda = 0,
biasadj = TRUE,
stepwise = FALSE,
approximation = FALSE
)
summary(fit_auto_arima)## Series: train_pago
## ARIMA(0,0,0)(2,1,1)[12] with drift
## Box Cox transformation: lambda= 0
##
## Coefficients:
## sar1 sar2 sma1 drift
## -0.0789 -0.0895 -0.8194 0.0091
## s.e. 0.2293 0.1774 0.3221 0.0002
##
## sigma^2 = 0.00725: log likelihood = 119.46
## AIC=-228.91 AICc=-228.38 BIC=-214.97
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 13550.95 1572578 699667.4 -0.1847079 4.559708 0.4226681
## ACF1
## Training set -0.08447364
auto.arima() es una herramienta de selección, no un
sustituto de la interpretación.
Debe revisarse:
##
## Ljung-Box test
##
## data: Residuals from ARIMA(0,0,0)(2,1,1)[12] with drift
## Q* = 12.33, df = 21, p-value = 0.9303
##
## Model df: 3. Total lags used: 24
No existe una regla universal que diga que ARIMA es superior a ETS o viceversa.
ETS modela principalmente estados de:
ARIMA modela principalmente:
La decisión debe ser empírica.
fit_ets_pago <- ets(
train_pago
)
fc_ets_pago <- forecast(
fit_ets_pago,
h = length(test_pago)
)
fc_arima_pago <- forecast(
fit_auto_arima,
h = length(test_pago)
)
comparacion_pago <- rbind(
ETS = accuracy(
fc_ets_pago,
test_pago
)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
ARIMA = accuracy(
fc_arima_pago,
test_pago
)["Test set", c("RMSE", "MAE", "MAPE", "MASE")]
)
knitr::kable(
round(comparacion_pago, 3),
caption = "ETS frente a ARIMA: evaluación fuera de muestra"
)| RMSE | MAE | MAPE | MASE | |
|---|---|---|---|---|
| ETS | 2151623 | 1771757 | 8.011 | 1.070 |
| ARIMA | 2499244 | 1867437 | 8.455 | 1.128 |
f_ets <- function(y, h) {
forecast(ets(y), h = h)
}
f_arima <- function(y, h) {
forecast(auto.arima(y), h = h)
}
e_ets <- tsCV(
serie_n,
forecastfunction = f_ets,
h = 1
)
e_arima <- tsCV(
serie_n,
forecastfunction = f_arima,
h = 1
)
data.frame(
modelo = c("ETS", "ARIMA"),
RMSE_CV = c(
sqrt(mean(e_ets^2, na.rm = TRUE)),
sqrt(mean(e_arima^2, na.rm = TRUE))
)
)Esta comparación es más sólida que seleccionar el modelo únicamente por ajuste dentro de muestra.
Hasta ahora hemos utilizado principalmente la historia de \(Y_t\).
Pero un actuario puede disponer de variables explicativas.
Por ejemplo:
\[ Y_t= \beta_0 + \beta_1X_{1t} + \beta_2X_{2t} + n_t, \]
donde:
\[ n_t\sim ARIMA(p,d,q)(P,D,Q)_m. \]
Esto combina dos ideas:
Ejemplo:
\[ \text{Siniestros}_t = \beta_0 + \beta_1\text{Exposición}_t + \beta_2\text{Catástrofe}_t + n_t. \]
## Series: train_n
## Regression with ARIMA(5,0,1)(1,0,0)[12] errors
##
## Coefficients:
## ar1 ar2 ar3 ar4 ar5 ma1 sar1 intercept
## 0.7402 -0.0288 -0.1186 -0.0335 -0.3772 -0.3678 0.3850 -847.2076
## s.e. 0.1663 0.1323 0.1102 0.1045 0.1151 0.1711 0.0939 46.4301
## Exposicion Catastrofe
## 0.0261 -38.9638
## s.e. 0.0004 38.7471
##
## sigma^2 = 4745: log likelihood = -744.8
## AIC=1511.59 AICc=1513.79 BIC=1543.3
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set -0.8431677 66.2228 52.38769 -0.1046336 2.023397 0.3277986
## ACF1
## Training set 0.01699761
Para pronosticar con regresores es necesario conocer o pronosticar sus valores futuros.
fc_dyn <- forecast(
fit_dyn,
xreg = xreg_test,
h = nrow(xreg_test)
)
autoplot(fc_dyn) +
autolayer(test_n, series = "Observado") +
labs(
title = "Regresión dinámica con errores ARIMA",
x = "Año",
y = "Número de siniestros",
colour = ""
)Suponga que el coeficiente estimado para exposición es positivo.
Esto no significa simplemente que “el tiempo hace crecer los siniestros”.
Significa que, controlando por la estructura temporal incluida en el error ARIMA, el volumen de exposición ayuda a explicar el número esperado de siniestros.
Esta separación es actuarialmente importante:
\[ \text{crecimiento de siniestros} = \text{crecimiento de exposición} + \text{cambio de frecuencia} + \text{estacionalidad} + \text{otros efectos}. \]
Otra extensión es utilizar modelos NNAR.
Un modelo NNAR utiliza rezagos como entradas de una red neuronal.
Conceptualmente:
\[ Y_t = f( Y_{t-1}, Y_{t-2}, \ldots, Y_{t-p} ) + \varepsilon_t, \]
donde \(f(\cdot)\) puede ser no lineal.
set.seed(2026)
fit_nnar <- nnetar(
train_n,
repeats = 20
)
fc_nnar <- forecast(
fit_nnar,
h = 12
)
autoplot(fc_nnar)Se deja eval=FALSE porque la estimación puede tardar más
según el equipo.
Estos modelos pueden capturar relaciones no lineales, pero suelen ser menos transparentes y sus intervalos de predicción requieren más cuidado.
Ahora resolveremos un ejercicio completo.
Queremos pronosticar el número mensual de siniestros de 2026.
Compararemos:
# 1. Benchmark
fc1 <- snaive(
train_n,
h = 12
)
# 2. ETS
fit2 <- ets(train_n)
fc2 <- forecast(
fit2,
h = 12
)
# 3. ARIMA
fit3 <- auto.arima(
train_n,
seasonal = TRUE
)
fc3 <- forecast(
fit3,
h = 12
)
# 4. Regresión dinámica
fit4 <- auto.arima(
train_n,
xreg = xreg_train,
seasonal = TRUE
)
fc4 <- forecast(
fit4,
xreg = xreg_test,
h = 12
)
resultados <- rbind(
SNaive = accuracy(fc1, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
ETS = accuracy(fc2, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
ARIMA = accuracy(fc3, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
Regresion_dinamica = accuracy(fc4, test_n)["Test set", c("RMSE", "MAE", "MAPE", "MASE")]
)
knitr::kable(
round(resultados, 3),
caption = "Comparación final de modelos"
)| RMSE | MAE | MAPE | MASE | |
|---|---|---|---|---|
| SNaive | 176.766 | 169.083 | 4.683 | 1.058 |
| ETS | 47.549 | 35.046 | 0.989 | 0.219 |
| ARIMA | 63.426 | 50.908 | 1.353 | 0.319 |
| Regresion_dinamica | 68.526 | 56.853 | 1.603 | 0.356 |
No debe utilizarse una sola estadística.
Una decisión profesional debería considerar simultáneamente:
Por ejemplo:
Pregunte primero:
Un pronóstico puntual:
\[ \widehat Y_{T+h|T} \]
no expresa toda la incertidumbre.
Un intervalo de predicción aproximado bajo normalidad puede escribirse:
\[ \widehat Y_{T+h|T} \pm z_{\alpha/2} \widehat{\sigma}_h. \]
Para 95%:
\[ \widehat Y_{T+h|T} \pm 1.96\widehat{\sigma}_h. \]
En general:
\[ \widehat{\sigma}_h \]
aumenta con \(h\).
Por tanto:
mientras más lejos pronosticamos, mayor suele ser la incertidumbre.
Esto es especialmente importante en aplicaciones actuariales porque la incertidumbre del pronóstico puede ser tan importante como el valor esperado.
Desde una perspectiva estadística:
\[ Y_{T+h} \]
es una variable aleatoria futura.
El pronóstico puntual suele representar una medida central de:
\[ p( Y_{T+h} \mid Y_1,\ldots,Y_T ). \]
Pero la distribución predictiva contiene mucha más información:
En seguros puede ser más importante conocer:
\[ P( Y_{T+h}>c ) \]
que únicamente el valor medio esperado.
Suponga que un pronóstico anual de pagos tiene:
\[ \widehat Y=Q\,120\,000\,000 \]
y error estándar predictivo:
\[ \sigma=Q\,12\,000\,000. \]
Queremos:
\[ P( Y>Q\,140\,000\,000 ). \]
Estandarizamos:
\[ Z= \frac{ 140-120 }{12} = 1.667. \]
Por tanto:
\[ P(Y>140) = 1-\Phi(1.667). \]
## [1] 0.04779035
Este tipo de cálculo conecta directamente el pronóstico temporal con decisiones de capital, liquidez y gestión de riesgo.
Si:
\[ e_t \]
está correlacionado temporalmente, las inferencias de una regresión convencional pueden resultar deficientes.
La estacionalidad tiene periodo fijo; el ciclo no.
La sobrediferenciación puede dañar el modelo.
En forecasting, el objetivo principal es pronosticar adecuadamente fuera de muestra.
AIC/AICc no reemplaza la validación fuera de muestra.
Un modelo complejo debe demostrar que agrega valor.
Un shock catastrófico puede distorsionar tendencia, estacionalidad y parámetros.
Un modelo dinámico necesita valores futuros de las variables explicativas.
Considere una serie mensual de frecuencia de siniestros con patrón anual.
Preguntas:
Suponga:
\[ Y_t=2500, \]
\[ \ell_{t-1}=2400, \]
\[ \alpha=0.30. \]
Calcule el nuevo nivel SES:
\[ \ell_t = \alpha Y_t+ (1-\alpha)\ell_{t-1}. \]
Yt <- 2500
nivel_anterior <- 2400
alpha <- 0.30
nivel_nuevo <-
alpha * Yt +
(1 - alpha) * nivel_anterior
nivel_nuevo## [1] 2430
El nivel actualizado es una combinación ponderada entre la observación actual y el nivel histórico. Con \(\alpha=0.30\), el modelo conserva 70% de la información contenida en el nivel previo y asigna 30% a la observación más reciente.
Sean:
\[ Y_t=120, \quad Y_{t-1}=112, \quad Y_{t-12}=100, \quad Y_{t-13}=96. \]
Calcule:
\[ (1-B)(1-B^{12})Y_t. \]
Desarrollando:
\[ Y_t-Y_{t-1}-Y_{t-12}+Y_{t-13}. \]
## [1] 4
Primero se elimina el efecto anual y luego el cambio de corto plazo. El resultado representa el cambio mensual en la diferencia respecto del mismo mes del año anterior.
Suponga que después de estacionarizar una serie:
¿Qué modelo inicial consideraría?
Un candidato natural es:
\[ AR(2), \]
o, si la serie requirió \(d\) diferencias:
\[ ARIMA(2,d,0). \]
Debe confirmarse mediante estimación, AICc, diagnóstico residual y evaluación fuera de muestra.
Una compañía observa un crecimiento de 8% anual en número de siniestros.
¿Es correcto concluir que la frecuencia de siniestros está aumentando 8%?
No necesariamente. El número de siniestros depende también de la exposición.
Una descomposición conceptual es:
\[ E[N_t] = \text{Exposición}_t \times \text{Frecuencia}_t. \]
El crecimiento de siniestros puede deberse principalmente a crecimiento del portafolio, no a deterioro del riesgo.
Utilice una serie actuarial propia o sustituya serie_n
por una serie real.
Complete el siguiente flujo.
# 1. Convertir a serie temporal
mi_serie <- ts(
mis_datos,
start = c(2018, 1),
frequency = 12
)
# 2. Graficar
autoplot(mi_serie)
# 3. Explorar estacionalidad
seasonplot(mi_serie)
monthplot(mi_serie)
# 4. ACF y PACF
ggtsdisplay(mi_serie)
# 5. Descomponer
ajuste_stl <- stl(
mi_serie,
s.window = "periodic",
robust = TRUE
)
autoplot(ajuste_stl)
# 6. Determinar diferenciación
ndiffs(mi_serie)
nsdiffs(mi_serie)
# 7. Separar entrenamiento/prueba
train <- window(
mi_serie,
end = c(2024, 12)
)
test <- window(
mi_serie,
start = c(2025, 1)
)
# 8. Benchmark
fc_snaive <- snaive(
train,
h = length(test)
)
# 9. ETS
fit_ets <- ets(train)
fc_ets <- forecast(
fit_ets,
h = length(test)
)
# 10. ARIMA
fit_arima <- auto.arima(
train,
seasonal = TRUE
)
fc_arima <- forecast(
fit_arima,
h = length(test)
)
# 11. Diagnóstico
checkresiduals(fit_ets)
checkresiduals(fit_arima)
# 12. Comparar fuera de muestra
rbind(
SNaive = accuracy(
fc_snaive,
test
)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
ETS = accuracy(
fc_ets,
test
)["Test set", c("RMSE", "MAE", "MAPE", "MASE")],
ARIMA = accuracy(
fc_arima,
test
)["Test set", c("RMSE", "MAE", "MAPE", "MASE")]
)
# 13. Seleccionar el modelo
# No utilizar una sola métrica.
# Revisar:
# - precisión
# - residuos
# - estabilidad
# - lógica actuarial
# 14. Reestimar con toda la muestra y pronosticar
modelo_final <- auto.arima(
mi_serie,
seasonal = TRUE
)
pronostico_final <- forecast(
modelo_final,
h = 12
)
autoplot(pronostico_final)La secuencia completa puede resumirse así:
\[ \boxed{ \text{Datos} \rightarrow \text{Gráficos} \rightarrow \text{Patrones} \rightarrow \text{Descomposición} \rightarrow \text{Modelo} \rightarrow \text{Diagnóstico} \rightarrow \text{Validación} \rightarrow \text{Pronóstico} } \]
Y la decisión entre familias de modelos puede verse como:
\[ \boxed{ \begin{array}{c} \text{Nivel/tendencia/estacionalidad} \\ \Downarrow \\ ETS \end{array} } \qquad \boxed{ \begin{array}{c} \text{Autocorrelación/diferenciación} \\ \Downarrow \\ ARIMA \end{array} } \]
Si existen predictores externos:
\[ \boxed{ \text{Regresión} + \text{errores ARIMA} } \]
Si existen relaciones no lineales complejas:
\[ \boxed{ NNAR \text{ u otros modelos avanzados} } \]
Antes de entregar un pronóstico, responda:
El objetivo de una buena metodología de pronóstico no es encontrar el modelo más complejo.
Es encontrar una representación suficientemente adecuada de la estructura de los datos para producir pronósticos:
En ciencias actuariales esto implica separar cuidadosamente:
\[ \text{exposición}, \quad \text{frecuencia}, \quad \text{severidad}, \quad \text{tendencia}, \quad \text{estacionalidad}, \quad \text{shocks}. \]
Un modelo temporal debe ser entendido como una representación del proceso generador de datos, no como una función automática que se aplica sin diagnóstico.