Series de Tiempo: Conceptos, Análisis, Manipulación y Visualización
En la Clase 1 introdujimos las series de tiempo de manera conceptual como una colección de observaciones secuenciales en el tiempo.
En esta clase revisaremos las series de tiempo desde una perspectiva teórica, estableciendo los antecendentes estadísticos necesarios para analizarlas y caracterizarlas. Luego de esta clase podrás:
Por supuesto, todo lo anterior a través de la utilización de distintos paquetes de R
.
Uno de los objetivos principales del análisis de series de tiempo es desarrollar modelos matemáticos que provean de descripciones plausibles para los datos muestreados. Para establecer una configuración estadística que nos permita describir las series de tiempo, asumiremos que una serie de tiempo puede ser definida como una colección de variables aleatorias indexadas de acuerdo al orden en que fueron obtenidas a través del tiempo.
Podemos entonces entender una serie de tiempo como la secuencia de variables aleatorias \(y_1, y_2,y_3,\dots\), donde la variable aleatoria \(y_1\) denota el valor que toma la serie en la primera observación temporal, la variable \(y_2\) denota el valor que toma el segundo punto en el tiempo y así sucesivamente. Podemos referirnos a dicha secuencia de variables aleatorias \(\{y_t\} = \{y_1,\dots,y_T\}\), indexada por \(t\)1 como un proceso estocástico2. Luego, los valores observados de una serie de tiempo son una realización del proceso estocástico, siendo una de muchas posibles secuencias que un proceso aleatorio puede generar (Shumway and Stoffer (2017)).
Otra manera de ver a las series de tiempo es como una muestra finita que se obtiene de una secuencia doblemente infinita subyacente: \(\{\dots, y_{-1},y_0,y_1,y_2,\dots,y_T,y_{T+1},y_{T+2},\dots\}\).
En este contexto, un modelo de series de tiempo para \(\{y_t\}\) es una especificación de las distribuciones conjuntas de la sencuencia de variables aleatorias para la cual \(\{y_t\}\) es una realización. Dado lo anterior, un modelo de series de tiempo nos permitirá usar una realización para realizar inferencias acerca de la distribución conjunta subyacente desde donde se produjo la realización obtenida.
En la Clase 1 describimos al análisis exploratorio como uno de los objetivos principales del análisis de series de tiempo, haciendo énfasis en la necesidad de identificar dichos fenómenos para poder caracterizar las series estudiadas. A continuación definimos de manera más formal a qué nos referiremos cuando hablemos de tendencia, estacionalidad y ciclos, entre otros términos (Hyndman and Athanasopoulos (2021)).
Hablamos de que existe tendencia en una serie de tiempo cuando hay un patrón creciente o decreciente a largo plazo3 en los datos. Cuando la tendencia cambia de una tendencia creciente a decreciente hablamos de un cambio en la dirección de la serie. Al analizar la tendencia, es necesario considerar la cantidad de observaciones disponible y realizar una evaluación subjetiva de a qué nos referimos con largo plazo. Existen métodos para estimar o remover la tendencia y así observar de mejor manera las otras fuentes de variación de una serie. Finalmente, si la serie no posee tendencia podemos considerarla estacionaria4 en la media.
Un patrón estacional ocurre cuando una serie de tiempo es afectada por factores estacionales tales como el período del año o el día de la semana. Es decir, hablamos de una serie con estacionalidad cuando la serie muestra variaciones sistemáticas a través de un período de tiempo determinado. La estacionalidad en general es fija y de período conocido. Este tipo de variación es fácil de entender y puede ser facilmente estimado si el efecto estacional es de interés directo. De manera alternativa, también es posible remover la variación estacional de los datos, para obtener datos desestacionalizados, si dicha variación no es de interés.
Un ciclo ocurre cuando los datos exhiben alzas y bajas que no poseen frecuencia determinada. Estas fluctuaciones continuas en la tendencia se asocian usualmente a condiciones económicas determinadas por el tipo de industria o negocio estudiado. La duración usual de estas fluctuaciones es de al menos 2 años.
Si las fluctuaciones presentes en una serie de tiempo no poseen una frecuencia determinada son consideradas cíclicas. Si la frecuencia es invariante y asociada a algún aspecto del calendario, entonces el patrón es estacional.
Luego de que las variaciones asociadas a patrones de tendencia o estacionalidad han sido removidos de un set de datos, obtenemos una serie de residuales5 que pueden parecer (o no) aleatorios. Los elementos irregulares de una serie de tiempo le dan sus características no-sistemáticas. Al hacer pronósticos, la idea es calibrar cada uno de los componentes de una serie de tiempo en una manera precisas excepto por el componente irregular.
Desde una perspectiva tradicional, una anomalía u outlier es una observación que se desvía con respecto a las otras observaciones lo suficiente como para generar sospechas acerca de su proceso de generación. Es decir, un outlier es una observaciones que no sigue un comportamiento esperado. Si la observación es indeseada (por ejemplo un error de medición producto de un sensor descalibrado o evento de ocurrencia única en el calendario), usualmente podemos limpiarla o imputarla. Sin embargo, si el evento es de interés, quizá sea necesario analizar el outlier de manera aislada (por ejemplo en detección de fraude).
# devtools::install_github("amrrs/coindeskr")
library(coindeskr)
# Obtenemos precio historico del Bitcoin en USd´
btc_usd_tbl <-
get_historic_price('USD', '2019-01-01', '2021-10-25') %>%
timetk::tk_tbl() %>%
rename(Date = index)
# devtools::install_github("twitter/AnomalyDetection")
# Deteccion de anomalias
library(AnomalyDetection)
btc_usd_anoms <-
AnomalyDetectionTs(
btc_usd_tbl,
max_anoms = 0.05,
direction = 'both',
plot = FALSE
)
# Graficamos
ggplot_btc_usd_anoms <- btc_usd_anoms$anoms %>%
as_tibble() %>%
mutate(Date = as.Date(timestamp), .keep = "unused") %>%
left_join(btc_usd_tbl, .) %>%
ggplot(aes(x = Date, y = Price)) +
geom_line() +
geom_point(aes(y = anoms), color = 'red') +
theme_bw()
plotly::ggplotly(ggplot_btc_usd_anoms)
Un cambio estructural (a veces llamados cambios de régimen) es un cambio repentino e inesperado en el comportamiento de una serie de tiempo. En términos estadísticos, un cambio estructural ocurre cuando la distribución de probabilidad subyacente de una serie de tiempo cambia. El proceso de detección de puntos de cambios6 busca identificar cuando ocurren estos cambios, usualmente utilizando algoritmos que comparan propiedades estadísticas de la distribución nueva con respecto a la original.
library(changepoint)
set.seed(42)
# Simulamos a partir de una distribucion normal
ts_sim <-
c(rnorm(100, mean = 0, sd = 1),
rnorm(100, mean = 1, sd = 1),
rnorm(100, mean = 0, sd = 1),
rnorm(100, mean = 0.2, sd = 1))
# Calculamos posicionamiento optimo y cantidad (potencial) de
# puntos de cambio en los datos usando PELT
ts_pelt <- cpt.mean(ts_sim, method='PELT')
# [!] Notar que ts_pelt es un objeto clase S4
# Establecemos la media para cada intervalo hallado
ts_levels <-
rep(ts_pelt@param.est$mean,
times = c(ts_pelt@cpts[1], diff(ts_pelt@cpts)))
# Generamos tibble que reune resultados
ts_cpoint_tbl <-
dplyr::tibble(index = 1:400,
ts = ts_sim,
ts_level = ts_levels)
# Grafico
ggplot_ts_cpoint <- ts_cpoint_tbl %>%
ggplot(aes(x = index, y = ts_sim)) +
geom_line() +
geom_line(y = ts_levels, color = 'red') +
theme_bw()
plotly::ggplotly(ggplot_ts_cpoint)
Hemos evidenciado que las series de tiempo pueden exhibir un sinnúmero de patrones, tales como tendencia, estacionalidad y ciclos, por mencionar algunos. Cuando descomponemos series de tiempo, lo hacemos mediante la combinación de la tendencia y ciclicidad en un sólo componente tendencia-ciclidad7, un componente estacional (pudiendo existir más de una componente estacional o ninguna) y un componente residual, que contiene la información restante de la serie de tiempo.
La extracción de componentes a partir de una serie de tiempo permite no sólo mejorar el entendimiento de una serie de tiempo, sino también ser utilizado para mejorar la elaboración de pronósticos
En el ejemplo anterior8 pudimos ver la aplicación de la descomposición en tendencia y estacionalidad por LOESS (locally estimated scatterplot smoothing)9 (STL), que permite descomponer de manera aditiva10 los componentes. La regresión local es de utilidad ya que nos permite aplicar un suavizador no-paramétrico realizando ajustes por mínimos cuadrados en vecindades de una serie numérica11.
Así como la correlación mide el grado de relación lineal entre dos variables, la autocorrelación12 mide la relación lineal entre valores rezagados de una serie de tiempo.
Existen varios coeficientes de autocorrelación, cada uno correspondiente a cada panel obtenido en un lag plot, donde se muestra \(y_t\) con respecto a \(y_{t-k}\) para diferentes valores de \(k\) como sigue:
Aquí los colores indican el mes de la vairable en el eje vertical, mientras que los rezagos están graficados en el eje horizontal.
Si \(r_k\) mide la relación entre \(y_t\) y \(y_{t-k}\), entonces la autocorrelación del rezago \(k\),\(r_k\), puede escribirse como:
\[r_{k} = \frac{\sum\limits_{t=k+1}^T (y_{t}-\bar{y})(y_{t-k}-\bar{y})} {\sum\limits_{t=1}^T (y_{t}-\bar{y})^2}\]
donde \(T\) es el largo de la serie de tiempo. Los coeficientes de autocorrelación consolidan la función de autocorrelación (ACF) de las serie de tiempo.
Revisemos las ACF de las series de tiempo vistas la clase pasada:
# Yearly
Nile %>%
tsibble::as_tsibble() %>%
timetk::plot_acf_diagnostics(
.date_var = index,
.value = value,
.lags = 20,
.interactive = TRUE
)
# Quarterly
AirPassengers %>%
tsibble::as_tsibble() %>%
mutate(index = as.Date(index)) %>%
timetk::plot_acf_diagnostics(
.date_var = index,
.value = value,
.lags = 12,
.interactive = TRUE
)
# Half-hourly
forecast::taylor %>%
tsibble::as_tsibble() %>%
timetk::plot_acf_diagnostics(
.date_var = index,
.value = value,
.lags = 48*14,
.interactive = TRUE
)
# Quarterly
timetk::m4_quarterly %>% filter(id == "Q10") %>%
timetk::plot_acf_diagnostics(
.date_var = index,
.value = value,
.lags = 12,
.interactive = TRUE
)
# Monthly
datasets::sunspots %>%
timetk::tk_tbl() %>%
timetk::plot_acf_diagnostics(
.date_var = index,
.value = value,
.lags = 12,
.interactive = TRUE
)
# Monthly
forecast::wineind %>%
timetk::tk_tbl() %>%
timetk::plot_acf_diagnostics(
.date_var = index,
.value = value,
.lags = 24,
.interactive = TRUE
)
# Yearly average global temperature deviations
astsa::gtemp_land %>%
timetk::tk_tbl() %>%
timetk::plot_acf_diagnostics(
.date_var = index,
.value = value,
.lags = 10,
.interactive = TRUE
)
Usualmente graficamos la ACF para ver cómo las correlaciones varían con respecto al lag \(k\)-ésimo. En la literatura el gráfico de la ACF es conocido como correlograma.
Al analizar una ACF nos interesará enfocarnos en los siguientes fenómenos13:
R
, el correlograma puede incluir intervalos de confianza dibujados en forma de cono, usualmente asociados al 90% (\(\pm 1.96/\sqrt{T}\)) o 95%, sugiriendo que valores de correlación fuera de este intervalo se asocian con alta probabilidad a correlación y no a casualidad estadística.Las autocorrelaciones entre rezagos están formadas por los efectos de correlación directos e indirectos. La autocorrelación parcial (PACF) para el rezago \(k\) es la correlación que resulta luego de remover el efecto de cualquier correlación asociada a los términos de rezagos intermedios (Cowpertwait and Metcalfe (2009)), permitiendo aislar los efectos directos de cada rezago \(k\) en los valores actuales de la serie analizada.
El grado de suavidad de las series de tiempo revisadas en la clase pasada es una de las características fundamentales que nos permite diferenciarlas entre ellas. Como vimos anteriormente, podríamos suponer que los puntos adyacentes en el tiempo están correlacionados, por lo que el valor de la serie en el período \(t\), \(y_t\), dependería de algún modo de sus valores pasados \(y_{t-1}, y_{t-2},\dots\). Podemos incorporar dicha suposición junto a colecciones de variables aleatorias para modelar series de tiempo.
A continuación revisaremos la serie más simple que podemos generar: una colección de variables aleatorias no correlacionadas utilizando la distribución normal.
El ruido blanco14 es una de las series de tiempo más simples que podemos generar y es ampliamente utilizada como modelo de ruido en aplicaciones ingenieriles. Corresponde a observaciones aleatorias, independientes e identicamente distribuidas, lo que los estadísticos llaman variables aleatorias iid (independent and identically distributed).
Un ruido blanco particularmente útil es el ruido blanco Gaussiano, donde \(w_t\) son variables aleatorias independientes con media 0 y varianza \(\sigma_{w}^{2}\), lo que podemos escribir como \(w_t \sim \text{iid}\ N(0,\sigma_{w}^{2})\).
Anteriormente utilizamos rnorm()
para generar posibles realizaciones de una serie de tiempo. Dicha serie no poseía tendencia, estacionalidad ni ciclicidades aparentes, por lo que vale la pena preguntarse ¿cómo será su ACF?💡
ts_tbl %>%
filter(series == "ts_1") %>%
timetk::plot_acf_diagnostics(
.date_var = index,
.value = value,
.lags = 100,
.interactive = TRUE
)
Podemos ver que la serie no posee ningún tipo de correlación, sólo ruido. No existe información para poder construir un modelo de pronóstico. Para evaluar nuestra hipótesis, podemos utilizar el test de Ljung-Box (Box.test()
)15, que considera la autocorrelación de los primeros \(h\) valores juntos. La significancia del test indica que los datos probablemente no son ruido blanco.
El ruido blanco es el ejemplo más simple de un proceso estacionario. Una serie de tiempo se dice estrictamente estacionaria si sus propiedades no se ven afectadas un cambio en el origen del tiempo. Es decir, la distribución de probabilidad conjunta de las observaciones \(y_t, y_{t+1}, \dots, y_{t+n}\) es exactamente la misma que la distribución de probabilidad conjunta de \(y_t+k, y_{t+k+1}, \dots, y_{t+k+n}\).
La estacionariedad implica un tipo de equilibrio o estabilidad estadística en los datos. Por ello, la serie de tiempo posee una media constante definida de manera usual
\[\mu_y = E(y) = \int_{-\infty}^{\infty}yf(y)dy\] y varianza constante definida como
\[\sigma_{y}^{2} = \text{Var}(y) = \int_{-\infty}^{\infty}(y - \mu_{y}^{})^{2}f(y)dy\]
La media y varianza muestral pueden ser utilizadas para estimar dichos parámetros. Si las observaciones en una serie de tiempo son \(y_1, y_2, \dots, y_{T}\), entonces la media muestral es
\[\bar{y} = \hat{\mu}_{y} = \frac{1}{T}\sum_{t=1}^{T}y_t\] y la varianza muestral16 es
\[s^{2} = \hat{\sigma}_{y}^{2} = \frac{1}{T}\sum_{t=1}^{T}(y_t - \bar{y})^{2}\]
Para que la ACF tenga sentido, la serie debe ser considerada una serie debilmente estacionaria. Esto implica que la función de autocorrelación para cualquier rezago particular es la misma sin importar el lugar en el que estamos en el tiempo. La serie será debilmente estacionaria si
En términos generales, se habla de que una serie es estacionaria si no existen cambios sistemáticos en la media (no posee tendencia), en su varianza y si las variaciones estrictamente periódicas pueden ser removidas. Es usual que las series estudiadas violen la estacionariedad como propiedad. Sin embargo, esta denominación se utiliza a menudo para expresar que la serie posee características que sugieren el ajuste de un modelo estacionario. Gran parte de la teoría probabilística asociada a series de tiempo se asocia al análisis de series de tiempo estacionarias, por lo que dicho análisis a implicará transformar una serie no-estacionaria en una estacionaria mediante la remoción de tendencia y variación estacional de los datos, para luego modelar la variación en los residuales mediante un proceso estocástico estacionario.
La visualización de una serie de tiempo puede sugerir la aplicación de una transformación, por ejemplo, aplicando logaritmo o raíz cuadrada. Las principales razones por las cuales transformar una serie de tiempo son (Chatfield and Xing (2019)):
Si hay tendencia en la serie y la varianza parece incrementarse con la media puede ser recomendable transformar los datos. En particular, si la desviación estándar es directamente proporcional a la media, una transformación logarítmica peude ser adecuada. Por otro lado, si la varianza cambia a través del tiempo sin una tendencia presente en a serie, la transformación no será de utilidad y quizá valga la pena evaluar modelos que admitan cambios en la varianza17.
Si existe tendencia en la serie y el tamaño del efecto estacional parece incrementarse con la media, puede ser recomendable transformar los datos para lograr que el efecto estacional sea constante de año a año. En este último caso, se dice que el efecto estacional es aditivo. En el caso de que el efecto estacional sea directamente proporcional a la media, entonces el efecto estacional es considerado multiplicativo y un transformación logarítmica es apropiada para hacerlo aditivo19.
La construcción de modelos y pronósticos usualmente se basa en el supuesto de que los datos distribuyen normal. En la práctica es usual que ese no sea el caso, por ejemplo, puede haber evidencia de sesgos asociados a picos en la misma dirección en la serie de tiempo (hacia arriba o hacia abajo). Este efecto puede ser difícil de eliminar con una transformación y quizá sea necesario modelar los datos con una distribución del error distinta.
Las transformaciones logarítmicas y de raíces cuadradas mencionadas anteriormente son casos especiales de una clase general de transformaciones llamadas las Transformaciones de Box-Cox. Dada un serie de tiempo observada \({y_t}\) y un parámetro de transformación \(\lambda\), la serie transformadad está dada por
\[\begin{equation} y_t = \begin{cases} \log(y_t) & \text{si $\lambda=0$} \\ (y_t^{\lambda} - 1)/\lambda & \text{si $\lambda \neq 0$} \end{cases} \end{equation}\]
R version 4.1.2 (2021-11-01)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Pop!_OS 21.10
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.13.so
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods
[7] base
other attached packages:
[1] changepoint_2.2.2 zoo_1.8-9 AnomalyDetection_1.0
[4] coindeskr_0.1.0 xaringanExtra_0.5.5 plotly_4.10.0
[7] forcats_0.5.1 stringr_1.4.0 dplyr_1.0.7
[10] purrr_0.3.4 readr_2.1.1 tidyr_1.1.4
[13] tibble_3.1.6 ggplot2_3.3.5 tidyverse_1.3.1
loaded via a namespace (and not attached):
[1] readxl_1.3.1 backports_1.4.0 workflows_0.2.4
[4] plyr_1.8.6 lazyeval_0.2.2 splines_4.1.2
[7] crosstalk_1.2.0 listenv_0.8.0 digest_0.6.29
[10] foreach_1.5.1 htmltools_0.5.2 yardstick_0.0.9
[13] parsnip_0.1.7 fansi_0.5.0 astsa_1.14
[16] magrittr_2.0.1 memoise_2.0.1 tune_0.1.6
[19] tzdb_0.2.0 recipes_0.1.17 globals_0.14.0
[22] modelr_0.1.8 gower_0.2.2 xts_0.12.1
[25] hardhat_0.1.6 anytime_0.3.9 forecast_8.15
[28] rsample_0.1.1 tseries_0.10-49 dials_0.0.10
[31] colorspace_2.0-2 rvest_1.0.2 haven_2.4.3
[34] xfun_0.28 crayon_1.4.2 jsonlite_1.7.2
[37] survival_3.2-13 iterators_1.0.13 glue_1.5.1
[40] gtable_0.3.0 ipred_0.9-12 distributional_0.2.2
[43] future.apply_1.8.1 quantmod_0.4.18 timetk_2.6.2
[46] scales_1.1.1 DBI_1.1.1 Rcpp_1.0.7
[49] viridisLite_0.4.0 GPfit_1.0-8 lava_1.6.10
[52] prodlim_2019.11.13 htmlwidgets_1.5.4 httr_1.4.2
[55] ellipsis_0.3.2 pkgconfig_2.0.3 farver_2.1.0
[58] nnet_7.3-17 sass_0.4.0.9000 dbplyr_2.1.1
[61] utf8_1.2.2 tidyselect_1.1.1 labeling_0.4.2
[64] rlang_0.4.12 DiceDesign_1.9 munsell_0.5.0
[67] cellranger_1.1.0 tools_4.1.2 cachem_1.0.6
[70] cli_3.1.0 generics_0.1.1 broom_0.7.10
[73] evaluate_0.14 fastmap_1.1.0 yaml_2.2.1
[76] knitr_1.36 fs_1.5.1 nlme_3.1-155
[79] future_1.23.0 xml2_1.3.3 compiler_4.1.2
[82] feasts_0.2.2 rstudioapi_0.13 curl_4.3.2
[85] reprex_2.0.1 lhs_1.1.3 bslib_0.3.1
[88] stringi_1.7.6 lattice_0.20-45 Matrix_1.4-0
[91] urca_1.3-0 vctrs_0.3.8 pillar_1.6.4
[94] lifecycle_1.0.1 furrr_0.2.3 lmtest_0.9-39
[97] jquerylib_0.1.4 data.table_1.14.2 R6_2.5.1
[100] parallelly_1.29.0 codetools_0.2-18 distill_1.3
[103] MASS_7.3-55 assertthat_0.2.1 withr_2.4.3
[106] fracdiff_1.5-1 parallel_4.1.2 hms_1.1.1
[109] tsibble_1.1.1 quadprog_1.5-8 grid_4.1.2
[112] rpart_4.1-15 timeDate_3043.102 class_7.3-19
[115] rmarkdown_2.11.3 downlit_0.4.0 TTR_0.24.2
[118] pROC_1.18.0 fabletools_0.3.2 lubridate_1.8.0
El índice \(t\) usualmente es discreto y toma valores en los enteros↩︎
Cross Validated: Is a time series the same as a stochastic process?↩︎
¿Qué es largo plazo de todas maneras? Para el clima una variación cíclica puede ocurrir en un período de 50 años. Si sólo tuvieramos 20 años de datos, esta oscilación podría parecer una tendencia…↩︎
Retomaremos este concepto de manera más formal un poco más adelante↩︎
Retomaremos este concepto de manera más formal un poco más adelante↩︎
Véase paquete changepoint
↩︎
En general sólo “tendencia.”↩︎
(https://business-science.github.io/timetk/reference/plot_stl_diagnostics.html)↩︎
📌 Es bueno conocer el trabajo de B.D. Ripley.↩︎
por defecto, si no se aplican transformaciones tipo Box-Cox↩︎
📌 Es posible optimizar sus hiper-parámetros (i.e span
)↩︎
a.k.a. Correlación Serial↩︎
Más adelante ahondaremos en los modelos que se podrían utilizar a partir del análisis de una ACF↩︎
En inglés White Noise (WN), en analogía con la luz blanca y la idea de que todas las oscilaciones periódicas posibles están presentes en ella con igual fuerza.↩︎
No hay mucha diferencia entre usar \(T\) y \(T-1\) cuando se tienen grandes candidades de observaciones↩︎
Modelos Heteroscedásticos y aproximaciones a clústers de volatilidad↩︎
Retomaremos esta idea en la clase 4.↩︎
Sin embargo, la transformación solo estabilizará la varianza si el término del error también es pensado como multiplicativo, lo que a veces se pasa por alto.↩︎