library(htmltools)
#Sección - 8
Hasta ahora, nos hemos centrado principalmente en los dos primeros componentes del flujo de trabajo del análisis de series temporales: preprocesamiento de datos y análisis descriptivo. A partir de este capítulo, cambiaremos de enfoque y nos centraremos en el tercer y último componente del análisis: el pronóstico. Antes de adentrarnos en los diferentes modelos de pronóstico en los próximos capítulos, presentaremos los principales elementos del flujo de trabajo de pronóstico. Esto incluye enfoques para entrenar un modelo de pronóstico, evaluación del rendimiento y métodos de referencia. Esto le proporcionará un conjunto de herramientas para diseñar y construir un modelo de pronóstico según el objetivo del análisis. Este capítulo cubre los siguientes temas:
• Enfoques de entrenamiento y pruebas para un modelo de pronóstico • Métodos de evaluación del rendimiento y matrices de medición de errores • Métodos de referencia • Cuantificación de la incertidumbre del pronóstico con intervalos de confianza y simulación
El pronóstico tradicional de series temporales sigue el mismo flujo de trabajo que la mayoría de los campos de análisis predictivo, como regresión o clasificación, e incluye típicamente los siguientes pasos: 1. Preparación de datos: Aquí, preparamos los datos para el proceso de entrenamiento y pruebas del modelo. Este paso incluye dividir la serie en particiones de entrenamiento (dentro de la muestra) y pruebas (fuera de la muestra), crear nuevas características (cuando corresponda) y aplicar una transformación si es necesario (por ejemplo, transformación logarítmica, escalado, etc.). 2. Entrenar el modelo: Aquí, usamos la partición de entrenamiento para entrenar un modelo estadístico. El objetivo principal de este paso es utilizar el conjunto de entrenamiento para entrenar, ajustar y estimar los coeficientes del modelo que minimizan los criterios de error seleccionados (más adelante en este capítulo discutiremos métricas de error comunes en detalle). Los valores ajustados y la estimación del modelo de las observaciones de la partición de entrenamiento se utilizarán más adelante para evaluar el rendimiento general del modelo. 3. Probar el modelo: Aquí, utilizamos el modelo entrenado para pronosticar las observaciones correspondientes de la partición de pruebas. La idea principal aquí es evaluar el rendimiento del modelo con un nuevo conjunto de datos (que el modelo no vio durante el proceso de entrenamiento). 4. Evaluación del modelo: Por último, pero no menos importante, después de que el modelo haya sido entrenado y probado, es hora de evaluar el rendimiento general del modelo en ambas particiones de entrenamiento y pruebas. Basándonos en el proceso de evaluación del modelo, si el modelo cumple con cierto umbral o criterio, entonces o retenemos el modelo usando la serie completa para generar el pronóstico final o seleccionamos un nuevo parámetro de entrenamiento/modelo diferente y repetimos el proceso de entrenamiento.
Por otro lado, este proceso tiene características únicas que lo distinguen de otros campos predictivos: ⚫ Las particiones de entrenamiento y pruebas deben estar ordenadas cronológicamente, en lugar de muestreo aleatorio. ⚫ Típicamente, una vez que hemos entrenado y probado el modelo utilizando las particiones de entrenamiento y pruebas, volveremos a entrenar el modelo con todos los datos (o al menos la observación más reciente en orden cronológico). A primera vista, esto podría ser impactante y aterrador para personas con experiencia en modelado de aprendizaje automático tradicional o aprendizaje supervisado, ya que típicamente conduce al sobreajuste y otros problemas. Discutiremos la razón detrás de esto y cómo evitar el sobreajuste más adelante.
Uno de los elementos centrales del flujo de trabajo de pronóstico es el proceso de entrenamiento del modelo. La calidad del entrenamiento del modelo tendrá un impacto directo en la salida del pronóstico. Los principales objetivos de este proceso son los siguientes: • Formalizar la relación de la serie con otros factores, como patrones estacionales y de tendencia, correlación con retrasos pasados y variables externas de manera predictiva. • Ajustar los parámetros del modelo (cuando corresponda). • Que el modelo sea escalable en nuevos datos, o en otras palabras, evitar el sobreajuste. Como mencionamos anteriormente, antes del proceso de entrenamiento, la serie se divide en particiones de entrenamiento y pruebas, donde el modelo se entrena en la partición de entrenamiento y se prueba en la partición de pruebas. Estas particiones deben estar en orden cronológico, independientemente del enfoque de entrenamiento que se haya utilizado. La razón principal de esto es que la mayoría de los modelos de series temporales establecen una relación matemática entre la serie en términos de sus retrasos pasados y términos de error.
Uno de los enfoques de entrenamiento más comunes es el uso de particiones únicas de entrenamiento y pruebas (o una sola fuera de la muestra). Este enfoque simplista se basa en dividir la serie en particiones de entrenamiento y pruebas (o dentro y fuera de la muestra, respectivamente), entrenar el modelo en la partición de entrenamiento y probar su rendimiento en el conjunto de pruebas:
Como se puede ver en el diagrama anterior, las particiones de entrenamiento y pruebas están ordenadas y organizadas cronológicamente. Este enfoque tiene un solo parámetro: la longitud de la fuera de la muestra (o la longitud de la partición de pruebas). Típicamente, la longitud de la partición de pruebas se deriva de las siguientes reglas empíricas: • La longitud de la partición de pruebas debe ser hasta el 30% de la longitud total de la serie para tener suficientes datos de observación para el proceso de entrenamiento. • La longitud del horizonte de pronóstico (siempre y cuando no viole el término anterior). Esto está principalmente relacionado con el hecho de que el nivel de incertidumbre aumenta a medida que aumenta el horizonte de pronóstico. Por lo tanto, alinear el conjunto de pruebas con el horizonte de pronóstico podría proporcionar una estimación más cercana del error esperado del pronóstico. Por ejemplo, si tenemos una serie mensual con 72 observaciones (o 6 años) y el objetivo es pronosticar el próximo año (o 12 meses), tendría sentido usar las primeras 60 observaciones para el entrenamiento y probar el rendimiento usando las últimas 12 observaciones. La creación de particiones en R se puede hacer manualmente con la función window del paquete stats. Por ejemplo, dividamos la serie USgas en particiones, dejando las últimas 12 observaciones de la serie como la partición de pruebas y el resto como entrenamiento:
#Sección - 8 Código
library(TSstudio)
data(USgas)
ts_info(USgas)
## The USgas series is a ts object with 1 variable and 238 observations
## Frequency: 12
## Start time: 2000 1
## End time: 2019 10
# Using the series time index to set the start and end point of each partiton
train <- window(USgas,
start = time(USgas)[1],
end = time(USgas)[length(USgas) - 12])
test <- window(USgas,
start = time(USgas)[length(USgas) - 12 + 1],
end = time(USgas)[length(USgas)])
ts_info(train)
## The train series is a ts object with 1 variable and 226 observations
## Frequency: 12
## Start time: 2000 1
## End time: 2018 10
ts_info(test)
## The test series is a ts object with 1 variable and 12 observations
## Frequency: 12
## Start time: 2018 11
## End time: 2019 10
La simplicidad de este método es su principal ventaja, ya que es bastante rápido entrenar y probar un modelo mientras se utiliza (relativamente) poca potencia informática. Por otro lado, no es posible llegar a una conclusión sobre la estabilidad y escalabilidad del rendimiento del modelo basado en una sola unidad de prueba. Es posible que un modelo, solo por casualidad, tenga un rendimiento relativamente bueno en el conjunto de pruebas pero se desempeñe mal en el pronóstico real ya que no es estable con el tiempo. Una forma de mitigar ese riesgo es con el enfoque de backtesting, que se basa en entrenar un modelo con múltiples particiones de entrenamiento y pruebas.
El enfoque de backtesting para entrenar un modelo de pronóstico es una versión avanzada del enfoque de única fuera de la muestra que vimos anteriormente. Se basa en el uso de una ventana deslizante para dividir la serie en múltiples pares de particiones de entrenamiento y pruebas. Un proceso básico de entrenamiento de backtesting incluye los siguientes pasos: 1. Preparación de datos: Crear múltiples pares de particiones de entrenamiento y pruebas. 2. Entrenar un modelo: Esto se hace en cada una de las particiones de entrenamiento. 3. Probar el modelo: Evaluar su rendimiento en las particiones de pruebas correspondientes. 4. Evaluar el modelo: Evaluar la precisión, escalabilidad y estabilidad del modelo en función de la puntuación de las pruebas. Basándose en la evaluación, se realizaría una de las siguientes acciones: • Generar el pronóstico final para verificar si la puntuación del modelo cumple con un umbral o criterio específico • Aplicar ajustes y optimización adicionales para el modelo y repetir los pasos de entrenamiento y evaluaciones.
El uso de metodología de puntuación nos permite evaluar la estabilidad del modelo examinando la tasa de error del modelo en los diferentes conjuntos de pruebas. Consideraríamos un modelo como estable siempre que la distribución de errores del modelo en los conjuntos de pruebas sea bastante estrecha. En este caso, la tasa de error del pronóstico real debería estar dentro del mismo rango que los conjuntos de pruebas (suponiendo que no haya eventos anormales que impacten la tasa de error del pronóstico).
Este método es, conceptualmente, similar al enfoque de validación cruzada para entrenar modelos de aprendizaje automático. La principal distinción entre los dos enfoques está relacionada con cómo se establecen sus particiones. Mientras que las particiones de backtesting están ordenadas cronológicamente, las del enfoque de validación cruzada se basan en muestreo aleatorio.
Los principales parámetros de configuración de un modelo de entrenamiento de backtesting son los siguientes: ⚫ La longitud de las particiones de entrenamiento: Se establece mediante la configuración de ventana. Hay dos tipos comunes de ventanas deslizantes: • Ventana en expansión: Usando las primeras N observaciones como partición de entrenamiento inicial, agregue las siguientes n observaciones para crear la siguiente partición de entrenamiento. La longitud de la partición de entrenamiento aumenta a medida que la ventana se desplaza sobre la serie. ⚫ Ventana deslizante: Configure las primeras N observaciones como la partición de entrenamiento inicial y desplace la ventana en n observaciones para crear la siguiente partición de entrenamiento. La longitud de las particiones de entrenamiento permanece igual mientras la ventana se desplaza sobre la serie: • La longitud de las particiones de pruebas: un valor constante, típicamente alineado con la longitud del horizonte de pronóstico (bajo la limitación del número mínimo de observaciones requeridas para entrenar el modelo) ⚫ El espacio entre cada partición de entrenamiento: define el ritmo de la ventana deslizante • El número de particiones de entrenamiento y pruebas
El siguiente diagrama muestra la estructura del backtesting con una ventana en expansión:
Descripción de la imagen
Como se puede ver en el diagrama anterior, todas las particiones de entrenamiento del método de ventana en expansión comienzan en el mismo punto de índice, To, donde cada partición de entrenamiento es una combinación de la partición de entrenamiento anterior con las siguientes n observaciones (donde n es una constante que representa la tasa de expansión de la ventana). Tendría sentido usar este método cuando la serie tenga un patrón estacional fuerte y una tendencia estable. En este caso, las primeras observaciones de la serie podrían, potencialmente, tener información predictiva que pueda ser utilizada por el modelo. La desventaja de este método es entrenar el modelo con particiones de longitud diferente, ya que típicamente un modelo tiende a aprender más y, por lo tanto, a rendir mejor cuando hay más datos disponibles. Por lo tanto, podemos observar que el rendimiento de los modelos entrenados en las particiones más recientes es mejor que el de los que se entrenan con las primeras particiones.
Tendría más sentido usar la ventana deslizante siempre que la serie de entrada tenga cambios estructurales o alta volatilidad, o cuando la mayor parte del poder predictivo esté vinculado a la historia más reciente (o alta correlación con los rezagos más recientes de la serie). Por ejemplo, como vimos en el capítulo anterior, los precios mensuales del petróleo crudo tienen una fuerte relación con los rezagos más recientes de la serie, y la historia lejana no contiene información predictiva poderosa sobre la dirección futura de la serie. Si bien el enfoque de backtesting nos proporciona información intuitiva sobre la estabilidad y escalabilidad del modelo, tiene un mayor costo computacional en comparación con el enfoque de partición única de entrenamiento y prueba. Por lo tanto, este compromiso entre los dos enfoques debe tenerse en cuenta al seleccionar el enfoque de entrenamiento.
El objetivo principal del paso de evaluación es evaluar la capacidad del modelo entrenado para pronosticar (o basado en otros criterios) con precisión las observaciones futuras de la serie. Este proceso incluye lo siguiente: ⚫ Análisis residual: Esto se centra en la calidad del modelo, con valores ajustados en la partición de entrenamiento. ⚫ Puntuación del pronóstico: Esto se basa en la capacidad del modelo para pronosticar los valores reales del conjunto de pruebas.
El análisis residual prueba qué tan bien capturó e identificó el modelo los patrones de la serie. Además, proporciona información sobre las distribuciones de los residuos, que se requieren para construir intervalos de confianza para el pronóstico. La definición matemática de un residuo es la diferencia entre la observación real y el valor ajustado correspondiente del modelo, que se observó en el proceso de entrenamiento, o como la siguiente ecuación: Et = Yt - Ŷt Aquí, Et, Y y Ŷt representan el residuo, el valor real y los valores ajustados, respectivamente, en el tiempo t. Este proceso incluye el uso de herramientas de visualización de datos y pruebas estadísticas para evaluar lo siguiente: • Probar la bondad del ajuste frente a los valores reales: Haces esto trazando los valores de los residuos a lo largo del tiempo en orden cronológico. El gráfico indica qué tan bien el modelo pudo capturar la oscilación de la serie en la partición de entrenamiento. Los residuos con oscilación aleatoria alrededor de cero y con variación constante (ruido blanco) indican que el modelo puede capturar la mayoría de la variación de la serie. Por otro lado, si la oscilación residual no tiene las características de ruido blanco, es una indicación de que el modelo no pudo capturar los patrones de la serie. Aquí hay algunas interpretaciones potenciales de la posible salida: • Todos o la mayoría de los residuos están por encima de las líneas cero: Esto es una indicación de que el modelo tiende a subestimar los valores reales. • Todos o la mayoría de los residuos están por debajo de las líneas cero: Esto es una indicación de que el modelo tiende a sobreestimar los valores reales.
# The sample.out argument set the size of the testing partition
# (and therefore the training partition)
USgas_partitions <- ts_split(USgas, sample.out = 12)
train <- USgas_partitions$train
test <- USgas_partitions$test
ts_info(train)
## The train series is a ts object with 1 variable and 226 observations
## Frequency: 12
## Start time: 2000 1
## End time: 2018 10
ts_info(test)
## The test series is a ts object with 1 variable and 12 observations
## Frequency: 12
## Start time: 2018 11
## End time: 2019 10
• Picos aleatorios: Esto es una indicación de posibles valores atípicos en la partición de entrenamiento. • Autocorrelación residual: Esto indica qué tan bien el modelo pudo capturar los patrones de la serie. Los rezagos no correlacionados indican que no hubo patrones que el modelo no capturó. De manera similar, la existencia de rezagos correlacionados indica patrones que el modelo no capturó. • Distribución de los residuos: Esto es necesario para concluir sobre la confiabilidad del intervalo de confianza del pronóstico. Si los residuos no siguen una distribución normal, no podemos usarlos para crear intervalos de confianza ya que se basa en la suposición de que los residuos siguen una distribución normal. • Para demostrar el proceso de análisis residual, entrenaremos un modelo ARIMA en la partición de entrenamiento que creamos anteriormente para la serie USgas. No te preocupes si no estás familiarizado con el modelo ARIMA: lo discutiremos en detalle en el Capítulo 11, Pronóstico con el Modelo ARIMA. Entrenaremos el modelo con la función auto.arima del paquete forecast:
library(forecast)
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
md <- auto.arima(train)
Usaremos la función checkresiduals del paquete forecast para examinar los residuos del modelo entrenado en la partición de entrenamiento. Esta función devuelve los siguientes cuatro resultados: ⚫ Gráfico de la serie temporal de los residuos ⚫ Gráfico de ACF de los residuos ⚫ Gráfico de distribución de los residuos ⚫ El resultado de la prueba de Ljung-Box
La prueba de Ljung-Box es un método estadístico para probar si la autocorrelación de una serie (que, en este caso, son los residuos) es diferente de cero y utiliza las siguientes hipótesis: ⚫ Ho: El nivel de correlación entre la serie y su rezago es igual a cero, y por lo tanto las observaciones de la serie son independientes. ⚫ H1: El nivel de correlación entre la serie y su rezago es diferente de cero, y por lo tanto las observaciones de la serie no son independientes.
Vamos a usar la función checkresiduals para evaluar el rendimiento del modelo entrenado en la partición de entrenamiento:
checkresiduals(md)
##
## Ljung-Box test
##
## data: Residuals from ARIMA(2,1,1)(2,1,1)[12]
## Q* = 24.95, df = 18, p-value = 0.1263
##
## Model df: 6. Total lags used: 24
Obtenemos los siguientes residuos del modelo ARIMA:
Comenzando con la salida de la prueba de Ljung-Box, notarás que, basándonos en los resultados del valor p, podemos rechazar la hipótesis nula con un nivel de significancia del 0.01. Por lo tanto, hay una indicación de que la correlación entre la serie residual y sus rezagos es diferente de cero. El gráfico de ACF proporciona un apoyo adicional para eso también. Esto indica que el modelo no capturó completamente todos los patrones de la serie, y es posible que desees modificar los parámetros de ajuste del modelo. El gráfico de serie temporal de los residuos oscila alrededor del eje x, con la excepción de algunos residuos, que cruzan el valor de ± 250. Esto podría indicar que ocurren algunos valores atípicos durante estos períodos, y deberías revisar esos puntos de datos en la serie (más adelante en este capítulo, veremos un método más intuitivo para comparar los valores ajustados con los valores reales utilizando la función test_forecast del paquete TSstudio). Por último, pero no menos importante, está el gráfico de distribución de los residuos, que parece ser una representación bastante buena de una distribución normal.
En un mundo ideal, deberías terminar este proceso con ruido blanco y residuos independientes. Sin embargo, en la realidad, en algunos casos, será más difícil lograr este objetivo debido a la estructura de la serie (valores atípicos, cambios estructurales, etc.), y es posible que tengas que seleccionar un modelo que te acerque más a este objetivo.
Una vez que finalices la sintonización del modelo, es hora de probar la capacidad del modelo para predecir observaciones que el modelo no vio antes (a diferencia de los valores ajustados que el modelo vio durante todo el proceso de entrenamiento). El método más común para evaluar el éxito del pronóstico para predecir los valores reales es utilizar métricas de precisión o error. El método más común para evaluar el éxito del pronóstico es predecir los valores reales con el uso de una métrica de error para cuantificar la precisión general del pronóstico. La selección de una métrica de error específica depende de los objetivos de precisión del pronóstico. Un ejemplo de métricas de error comunes son las siguientes:
• Error Cuadrático Medio (MSE): Esto cuantifica la distancia cuadrada promedio entre los valores reales y los pronosticados:
Descripción de la imagen
El efecto cuadrático del error evita que los valores positivos y negativos se cancelen entre sí y penaliza la puntuación del error a medida que aumenta la tasa de error. • Error Cuadrático Medio (MSE): Esto cuantifica la distancia cuadrada promedio entre los valores reales y los pronosticados:
• Raíz del Error Cuadrático Medio (RMSE): Esta es la raíz de la distancia cuadrada promedio entre los valores reales y los pronosticados:Descripción de la imagen
Descripción de la imagen
Descripción de la imagen
Es más fácil comparar, establecer un punto de referencia o comunicarse con personas no técnicas debido a la representación en porcentaje.
Por ejemplo, usemos el modelo que entrenamos anteriormente para pronosticar las 12 observaciones que dejamos para la prueba y evaluar su rendimiento. Utilizaremos la función forecast del paquete forecast para pronosticar los siguientes 12 meses (con respecto al punto final de la partición de entrenamiento):
fc <- forecast(md, h = 12)
Ahora que hemos asignado el pronóstico al objeto fc, usaremos la función accuracy del paquete forecast para evaluar el rendimiento del modelo con respecto a los valores reales en la partición de prueba: # ——– Code 6 ——–
accuracy(fc, test)
## ME RMSE MAE MPE MAPE MASE
## Training set 5.844136 97.81626 73.42657 0.1170672 3.522348 0.6376860
## Test set 37.847885 103.22848 81.46603 1.3107987 3.261643 0.7075062
## ACF1 Theil's U
## Training set -0.004183172 NA
## Test set -0.046708926 0.3404092
La función accuracy, que utilizaremos intensivamente en los próximos capítulos, devuelve varias métricas de error tanto para los valores ajustados (fila de Conjunto de Entrenamiento) como para el pronóstico real (fila de Conjunto de Prueba). Notarás que los resultados de MAPE del modelo son del 3.52% y del 7.84% en las particiones de entrenamiento y prueba, respectivamente. No debería sorprender una tasa de error más alta en la partición de prueba en comparación con la de entrenamiento, ya que típicamente el modelo vio los datos de la partición de entrenamiento durante todo el proceso de entrenamiento. Una tasa de error bastante baja en el conjunto de entrenamiento, junto con una alta tasa de error en el conjunto de prueba, es una clara indicación de sobreajuste en el modelo.
Un enfoque alternativo para evaluar el ajuste del modelo tanto en el conjunto de entrenamiento como en el de prueba es con la función test_forecast del paquete TSstudio. Esta función visualiza la serie real, los valores ajustados en la partición de entrenamiento y los valores pronosticados en el conjunto de prueba. Al pasar el cursor sobre los valores ajustados o pronosticados, aparece un cuadro de texto emergente con los resultados de RMSE y MAPE tanto en las particiones de entrenamiento como de prueba (que, lamentablemente, no se pueden transferir al libro en sí):
test_forecast(actual = USgas,
forecast.obj = fc,
test = test)
Es más fácil y rápido identificar información sobre la bondad del ajuste tanto de los valores ajustados como de los pronosticados al trazar esos valores contra los valores reales de la serie. Por ejemplo, inmediatamente notarás que el pico residual durante 2006 es causado por valores atípicos (o un consumo más bajo que el patrón normal de la serie). Además, el pronóstico real no capturó el pico anual de 2018. Estas observaciones no se pueden obtener con métricas de error. Benchmark del pronóstico
Según las métricas de error, el modelo entrenado obtuvo un MAPE del 7.84% o un RMSE de 208.01. ¿Cómo podemos evaluar si estos resultados son demasiado altos o bajos? El método más común es comparar el rendimiento del modelo con algún pronóstico de referencia o algún método heredado que deseamos reemplazar. Un enfoque de referencia popular sería utilizar un enfoque de pronóstico simplista como línea base. Por ejemplo, pronostiquemos la serie con un enfoque ingenuo y lo usemos como referencia para el pronóstico anterior que creamos con el modelo ARIMA.
Un enfoque ingenuo simple típicamente asume que el valor observado más recientemente es el verdadero representante del futuro. Por lo tanto, continuará con el último valor hasta el infinito (o hasta el horizonte del pronóstico). Podemos crear un pronóstico ingenuo con la función naive del paquete forecast y usar el conjunto de entrenamiento como entrada del modelo:
Podemos revisar el rendimiento del modelo en las particiones de entrenamiento y prueba usando la siguiente función test_forecast:
library(forecast)
naive_model <- naive(train, h = 12)
test_forecast(actual = USgas,
forecast.obj = naive_model,
test = test)
accuracy(naive_model, test)
## ME RMSE MAE MPE MAPE MASE
## Training set -1.028444 285.6607 228.5084 -0.9218463 10.97123 1.984522
## Test set 301.891667 499.6914 379.1417 9.6798015 13.28187 3.292723
## ACF1 Theil's U
## Training set 0.3761105 NA
## Test set 0.7002486 1.499679
En el caso del modelo ingenuo, no hay proceso de entrenamiento, y los valores ajustados se establecen como los valores reales (como puedes ver en el gráfico anterior). Dado que USgas tiene un patrón estacional fuerte, tendría sentido utilizar un modelo ingenuo estacional que tenga en cuenta la variación estacional. El modelo snaive del paquete forecast utiliza el último punto estacional como pronóstico de todas las observaciones estacionales correspondientes. Por ejemplo, si estamos utilizando una serie mensual, el valor del último enero en la serie se utilizará como el pronóstico puntual para todos los futuros meses de enero:
snaive_model <- snaive(train, h = 12)
test_forecast(actual = USgas,
forecast.obj = snaive_model,
test = test)
accuracy(snaive_model, test)
## ME RMSE MAE MPE MAPE MASE ACF1
## Training set 33.99953 148.7049 115.1453 1.379869 5.494048 1.000000 0.4859501
## Test set 96.45000 164.6967 135.8833 3.612060 5.220458 1.180103 -0.2120929
## Theil's U
## Training set NA
## Test set 0.4289964
Parece que el modelo ingenuo estacional tiene un mejor ajuste para el tipo de serie que estamos pronosticando, es decir, USgas, debido a su fuerte patrón estacional (en comparación con el modelo ingenuo). Por lo tanto, lo utilizaremos como punto de referencia para el modelo ARIMA. Al comparar tanto el MAPE como el RMSE de los dos modelos en la partición de prueba, queda claro que el modelo ARIMA proporciona una mejora (en términos de precisión) con respecto al modelo de referencia.
Finalizando el pronóstico Ahora que el modelo ha sido entrenado, probado, ajustado (si es necesario) y evaluado con éxito, podemos pasar al último paso y finalizar el pronóstico. Este paso se basa en recalibrar los pesos o coeficientes del modelo con la serie completa. Hay dos enfoques para utilizar la configuración de parámetros del modelo: ⚫ Si el modelo fue ajustado manualmente, debes utilizar los parámetros de ajuste exactos que se utilizaron en el modelo entrenado. ⚫ Si el modelo fue ajustado automáticamente por un algoritmo (como la función auto.arima que utilizamos anteriormente), puedes hacer cualquiera de las siguientes opciones: • Extraer la configuración de parámetros que se utilizó con la partición de entrenamiento. • Permitir que el algoritmo vuelva a ajustar los parámetros del modelo utilizando la serie completa, bajo la suposición de que el algoritmo tiene la capacidad de ajustar correctamente los parámetros del modelo al entrenar el modelo con nuevos datos.
Se recomienda el uso de algoritmos para automatizar el proceso de ajuste del modelo cuando se prueba la capacidad del modelo para ajustar el modelo con backtesting. Esto te permite revisar si el algoritmo tiene la capacidad de ajustar correctamente los parámetros del modelo, según los resultados del backtesting. Por razones de simplicidad, seguiremos utilizando el modelo auto.arima para entrenar el modelo final.
md_final <- auto.arima(USgas)
fc_final <- forecast(md_final, h = 12)
plot_forecast(fc_final,
title = "The US Natural Gas Consumption Forecast",
Xtitle = "Year",
Ytitle = "Billion Cubic Feet")
El objetivo principal del proceso de pronóstico, como vimos anteriormente, es minimizar el nivel de incertidumbre en torno a los valores futuros de la serie. Aunque no podemos eliminar completamente esta incertidumbre, podemos cuantificarla y proporcionar un rango alrededor de la estimación puntual del pronóstico (que no es más que el valor esperado del modelo de cada punto en el futuro). Esto se puede hacer utilizando el intervalo de confianza (o un intervalo creíble, cuando se utiliza el modelo bayesiano) o mediante simulación.
El intervalo de confianza es un método de aproximación estadística que se utiliza para expresar el rango de valores posibles que contienen el valor verdadero con cierto grado de confianza (o probabilidad). Hay dos parámetros que determinan el rango del intervalo de confianza:
Por defecto, la función forecast genera un intervalo de predicción con un nivel de confianza del 80% y 95%, pero puedes modificarlo utilizando el argumento level. Por ejemplo, usemos la función md_final del modelo entrenado y la función forecast para los próximos 60 meses utilizando el intervalo de predicción con niveles de confianza del 80% y 90%:
fc_final2 <- forecast(md_final,
h = 60,
level = c(80, 90))
plot_forecast(fc_final2,
title = "The US Natural Gas Consumption Forecast",
Xtitle = "Year",
Ytitle = "Billion Cubic Feet")
Un enfoque alternativo es utilizar la distribución del modelo para simular posibles trayectorias para el pronóstico. Este método solo se puede usar cuando la distribución del modelo está disponible. La función forecast_sim del paquete TSstudio proporciona una función incorporada para simular posibles trayectorias de pronóstico. Esta estimación se puede utilizar para calcular la estimación puntual del pronóstico (por ejemplo, utilizando la media o la mediana de todas las trayectorias), o para calcular probabilidades de obtener diferentes valores. Alimentaremos el mismo modelo a la función y ejecutaremos 100 iteraciones:
fc_final3 <- forecast_sim(model = md_final,
h = 60,
n = 500)
La salida de la función anterior contiene todas las simulaciones calculadas y las trayectorias simuladas. Extraigamos el gráfico de simulación (y usemos el paquete plotly para agregar títulos al gráfico):
library(plotly)
## Loading required package: ggplot2
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
fc_final3$plot %>%
layout(title = "US Natural Gas Consumption - Forecasting Simulation",
yaxis = list(title = "Billion Cubic Feet"),
xaxis = list(title = "Year"))
#——– Código 15 ——– Enfoque de carrera de caballos Por último, pero no menos importante, terminaremos este capítulo con un enfoque de pronóstico robusto que combina lo que hemos aprendido hasta ahora en este capítulo. El enfoque de carrera de caballos se basa en entrenar, probar y evaluar múltiples modelos de pronóstico y seleccionar el modelo que tenga el mejor desempeño en las particiones de prueba. En el siguiente ejemplo, aplicaremos una carrera de caballos entre siete modelos diferentes (revisaremos los modelos en los próximos capítulos; por ahora, no te preocupes si no estás familiarizado con ellos) usando seis períodos de backtesting. La función ts_backtesting del paquete TSstudio realiza el proceso completo de entrenamiento, prueba, evaluación y luego pronóstico, utilizando el modelo que tuvo el mejor desempeño en las particiones de prueba del backtesting. De forma predeterminada, el modelo probará los siguientes modelos:
auto.arima: Modelo ARIMA automatizado bsts: Modelo bayesiano de series temporales estructurales ets: Modelo de espacio de estados de suavización exponencial híbrido: Un conjunto de múltiples modelos nnetar: Modelo de series temporales de red neuronal tbats: Modelo de espacio de estados de suavización exponencial, junto con transformación Box-Cox, tendencia, errores ARMA y componentes estacionales Holt Winters: Filtrado Holt-Winters Antes de ejecutar la función, establezcamos el valor de la semilla con la función set.seed para que podamos reproducir los resultados.
set.seed(1234)
{r}# USgas_forecast <- ts_backtesting(ts.obj = USgas, periods = 6, models = "abehntw", error = "MAPE", window_size = 12, h = 60, plot = FALSE)
El modelo proporciona una tabla de clasificación (como podemos ver en la salida anterior) que está ordenada según el criterio de error establecido. En este caso, el modelo bsts tuvo la tasa de error más baja, por lo que la función recomendó que lo usemos (aunque todos los modelos y sus pronósticos están disponibles para extraer de la salida de la función). Podemos trazar la tasa de error y el pronóstico sugerido utilizando el modelo almacenado en el objeto de salida:
USgas_forecast$summary_plot
Estos gráficos de error proporcionan una visión general informativa del rendimiento de cada modelo. Consideramos que un modelo es bueno si la distribución del error es estrecha y baja con respecto al resto de los modelos probados (como el modelo bsts).
#Sección - 9 # Código del Capítulo 9
#Predicción con regresión lineal El modelo de regresión lineal es uno de los métodos más comunes para identificar y cuantificar la relación entre una variable dependiente y una variable independiente única (regresión lineal univariada) o múltiple (regresión lineal multivariada). Este modelo tiene una amplia gama de aplicaciones, desde la inferencia causal hasta el análisis predictivo y, en particular, la predicción de series temporales. El objetivo de este capítulo son los métodos y enfoques para pronosticar datos de series de tiempo con regresión lineal. Esto incluye métodos para descomponer y pronosticar los componentes de la serie (por ejemplo, la tendencia y los patrones estacionales), manejar eventos especiales (como valores atípicos y días festivos) y utilizar variables externas como regresores. Este capitulo cubre los siguientes topicos: • Enfoques de pronóstico con modelos de regresión lineal. • Extracción y estimación de los componentes de la serie. • Manejo de rupturas estructurales, valores atípicos y eventos especiales. • Serie de pronósticos con multiestacionalidad. Requerimiento técnico Los siguientes paquetes se utilizarán en este capítulo: •TSstudio: Versión 0.1.4 y superior •plotly: Versión 4.8 y superior •dplyr: Versión 0.8.1 y superior •lubridate: Versión 1.7.4 y superior •previsión: Versión 8.5 y superior
La regresión lineal El uso principal del modelo de regresión lineal es cuantificar la relación entre la variable dependiente Y (también conocida como variable de respuesta) y las variables independientes X (también conocidas como variables predictoras, impulsoras o regresadoras) de manera lineal. . En otras palabras, el modelo expresa la variable dependiente como una combinación lineal de las variables independientes. Una relación lineal entre las variables dependientes e independientes se puede generalizar mediante las siguientes ecuaciones: • En el caso de una sola variable independiente, la ecuación es la siguiente: Y; Bo+B1X1,i+€¡ • Para n variables independientes, la ecuación tiene el siguiente aspecto: Y;= Bo+B1X1,i+B2 X2,i+…+3n Xn‚í + €i Las variables del modelo para estas ecuaciones son las siguientes: ⚫ i representa el índice de observaciones, i = 1,…, N Y representa la i observación de la variable dependiente X representa el valor i de la variable independiente j, donde j = 1,…, n Be representa el valor del término constante (o intercepto) ⚫B; representa los parámetros correspondientes (o coeficientes) de las j variables independientes & define el término de error, que no es más que toda la información que no fue capturada por las variables independientes para la observación i
Suponiendo que las ecuaciones anteriores representan la verdadera naturaleza de la relación lineal entre las variables dependientes e independientes, entonces el modelo de regresión lineal proporciona una estimación de esos coeficientes (es decir, Bo, B1, …, Bn), que puede formalizarse mediante las siguientes ecuaciones: • Para el modelo de regresión lineal univariado, la ecuación es la siguiente: Yi = Bo+B1X1, ⚫ Para el modelo de regresión lineal multivariante, la ecuación es la siguiente: Yi = ẞo + 1X1,¿ + B2X2,i+…+nXni Las variables para estas ecuaciones son las siguientes: i representa el índice de observación, i = 1,…, N ⚫ representa la estimación de la variable dependiente i observación X, representa el valor i de la variable independiente j, donde j = 1,…, n Bo representa la estimación del término constante (o intercepto) $1,…, son la estimación de los parámetros (o coeficientes) correspondientes de las n variables independientes La estimación de los coeficientes del modelo se basa en los dos pasos siguientes: • Definir una función de costo (también conocida como función de pérdida), estableciendo alguna métrica de error para minimizar • Aplicar optimización matemática para minimizar la función de costos. El método de estimación más común es aplicar los mínimos cuadrados ordinarios (OLS). norte método como técnica de optimización para minimizar la suma de cuadrados residuales(). Cuadrar los residuos tiene dos efectos: ⚫ Evita que los valores positivos y negativos se cancelen entre sí al sumarlos • El efecto cuadrado proporciona una penalización exponencial para los residuos con mayor distancia, a medida que su costo aumenta
#Estimación de coeficientes con el método MCO El OLS es un método de optimización simple que se basa en álgebra y cálculo lineal básico, o cálculo matricial. (Esta sección es para conocimientos generales; si no está familiarizado con el cálculo matricial, puede omitir esta sección). El objetivo del MCO es identificar los coeficientes que minimizan la suma de cuadrados de los residuos. Supongamos que el residual de la i observación se define como lo siguiente:
Descripción de la imagen
#Predicción con regresión lineal El modelo de regresión lineal, a diferencia de los modelos tradicionales de series de tiempo como ARIMA o Holt-Winters, no fue diseñado explícitamente para manejar y pronosticar datos de series de tiempo. Más bien, es un modelo genérico con una amplia gama de aplicaciones, desde la inferencia causal hasta el análisis predictivo. Por tanto, la previsión con un modelo de regresión lineal se basa principalmente en los dos pasos siguientes: 1. Identificar la estructura de la serie, las características clave, los patrones, los valores atípicos y otras características. 2. Transformar esas características en variables de entrada y hacerlas retroceder con la serie para crear un modelo de pronóstico. Las características principales de un modelo de pronóstico de regresión lineal son los componentes de tendencia y estacional. La siguiente sección se centra en identificar la tendencia de la serie y los componentes estacionales y luego transformarlos en variables de entrada del modelo de regresión.
#Previsión de la tendencia y los componentes estacionales. En el Capítulo 5, Descomposición de datos de series temporales, introdujimos los componentes estructurales de la serie: los componentes de tendencia, ciclo, estacional e irregular, y los métodos para descomponerlos con la función de descomposición.
Recordemos que la descomposición de una serie se puede definir mediante una de las siguientes expresiones: Y=T+S+C+4, cuando la serie tiene estructura aditiva Y = TX St Ct It, cuando la serie tiene estructura multiplicativa La explicación es la siguiente: ⚫ Tendencia: Representa el crecimiento de la serie en el tiempo después de ajustar y eliminar los efectos estacionales. ⚫ Estacional: un patrón cíclico recurrente que se deriva directamente de las unidades de frecuencia de la serie (por ejemplo, el mes del año para una serie con una frecuencia mensual) ⚫ Ciclo: Un patrón cíclico que no está relacionado con la unidad de frecuencia en serie. • Irregular: cualquier otro patrón que no sea capturado por los componentes de tendencia, estacional y cíclico. En aras de la simplicidad, podemos eliminar el componente de ciclo, ya que normalmente se fusiona con el componente de tendencia (o ignoramos el componente de ciclo). Por lo tanto, podemos actualizar y reemplazar las ecuaciones anteriores con las siguientes: . Y1 = T + St+It, cuando la serie tiene estructura aditiva YT St It, cuando la serie tiene estructura multiplicativa Ahora podemos transformar esas ecuaciones para un modelo de regresión lineal, modificando la notación de las ecuaciones: Y = Bo+B1T+ B2 St + €t Dónde: ⚫ Y representa una serie de tiempo con n observaciones ⚫ T, una variable independiente con n observaciones, representa el componente de tendencia de la serie ⚫ S, una variable independiente con n observaciones, representa el componente estacional de la serie. • ⚫ Et, el término de error de regresión, representa el componente irregular o cualquier patrón que no sea capturado por la tendencia de la serie y el componente estacional. • Bo, B1 y B2 representan el origen del modelo y los coeficientes de los componentes tendencial y estacional, respectivamente.
La transformación de una serie con estructura multiplicativa en una formación de regresión lineal requirió una transformación de la serie en una estructura aditiva. Esto se puede hacer aplicando la transformación logarítmica para ambos lados de las ecuaciones: log(Y) = log(T;) + log(St) + log(lt) Una vez que la serie se transforma en una estructura aditiva, la transformación a una formación de regresión lineal es sencilla y sigue el mismo proceso descrito anteriormente: log(Yt) = Bo+B1 log(Ti) + B2 log(St) +&t
#Características de la ingeniería de los componentes de la serie. Antes de crear los datos de entrada de la regresión que representan la tendencia de la serie y los componentes estacionales, primero debemos comprender su estructura. En los siguientes ejemplos, demostraremos el proceso de creación de nuevas funciones a partir de series existentes utilizando la serie USgas. Carguemos la serie desde el paquete TSstudio nuevamente y grafiquemos con la función ts_plot:
library(TSstudio)
data("USgas")
ts_plot(USgas,
title = "US Monthly Natural Gas consumption",
Ytitle = "Billion Cubic Feet",
Xtitle = "Year")
ts_info(USgas)
## The USgas series is a ts object with 1 variable and 238 observations
## Frequency: 12
## Start time: 2000 1
## End time: 2019 10
Como se puede ver en el gráfico de la serie, y como vimos en los capítulos anteriores, USgas es una serie mensual con un fuerte componente estacional mensual y una línea de tendencia bastante estable. Podemos explorar más a fondo la estructura de los componentes de la serie con la función ts_decompose:
ts_decompose(USgas)
Puede ver en el gráfico anterior que la tendencia de la serie es bastante plana entre 2000 y 2010, y tiene un crecimiento bastante lineal en el futuro. Por tanto, la tendencia general entre 2000 y 2018 no es estrictamente lineal. Esta es una idea importante que nos ayudará a definir la entrada de tendencia para el modelo de regresión. Antes de usar la función 1m, la función de regresión lineal R incorporada del paquete de estadísticas, tendremos que transformar la serie de un objeto ts a un dato. objeto de marco. Por lo tanto, utilizaremos la función ts_to_prophet del paquete TSstudio:
USgas_df <- ts_to_prophet(USgas)
head(USgas_df)
## ds y
## 1 2000-01-01 2510.5
## 2 2000-02-01 2330.7
## 3 2000-03-01 2050.6
## 4 2000-04-01 1783.3
## 5 2000-05-01 1632.9
## 6 2000-06-01 1513.1
Después de transformar la serie en un objeto data.frame, podemos comenzar a crear las funciones de entrada de regresión. La primera característica que crearemos es la tendencia de la serie. Un enfoque básico para construir la variable de tendencia es indexar las series de observaciones en orden cronológico: USgas_df$tendencia <- 1:nrow (USgas_df) La regresión de la serie con el índice de la serie proporciona una estimación del crecimiento marginal de mes a mes, ya que el índice está en orden cronológico con incrementos constantes.
USgas_df$trend <- 1:nrow(USgas_df)
Después de transformar la serie en un objeto data.frame, podemos comenzar a crear las funciones de entrada de regresión. La primera característica que crearemos es la tendencia de la serie. Un enfoque básico para construir la variable de tendencia es indexar las series de observaciones en orden cronológico:
La regresión de la serie con el índice de la serie proporciona una estimación del crecimiento marginal de mes a mes, ya que el índice está en orden cronológico con incrementos constantes.
library(lubridate)
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
USgas_df$seasonal <- month(USgas_df$ds, label = T)
Usamos la función de factor para convertir el resultado de la función de mes en una variable categórica no ordenada. Revisemos ahora el marco de datos después de agregar las nuevas funciones:
head(USgas_df)
## ds y trend seasonal
## 1 2000-01-01 2510.5 1 ene
## 2 2000-02-01 2330.7 2 feb
## 3 2000-03-01 2050.6 3 mar
## 4 2000-04-01 1783.3 4 abr
## 5 2000-05-01 1632.9 5 may
## 6 2000-06-01 1513.1 6 jun
Modelando la serie tendencia y estacional. componentes Primero modelaremos la tendencia de la serie haciendo una regresión de la serie con la variable de tendencia, en la partición de entrenamiento:
h <- 12 # setting a testing partition length
train <- USgas_df[1:(nrow(USgas_df) - h), ]
test <- USgas_df[(nrow(USgas_df) - h + 1):nrow(USgas_df), ]
md_trend <- lm(y ~ trend, data = train)
summary(md_trend)
##
## Call:
## lm(formula = y ~ trend, data = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -547.2 -307.4 -153.2 333.1 1052.6
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1751.0074 52.6435 33.26 < 2e-16 ***
## trend 2.4489 0.4021 6.09 4.86e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 394.4 on 224 degrees of freedom
## Multiple R-squared: 0.1421, Adjusted R-squared: 0.1382
## F-statistic: 37.09 on 1 and 224 DF, p-value: 4.861e-09
Como puede verse en el resultado de la regresión anterior, el coeficiente de la variable de tendencia es estadísticamente significativo hasta un nivel de 0,001. Sin embargo, el R cuadrado ajustado de la regresión es bastante bajo, lo que generalmente tiene sentido, ya que la mayor parte de la variación de la serie está relacionada con el patrón estacional, como vimos en los gráficos anteriores. i Como puede observar en el resultado de la regresión anterior, la cuarta columna representa el valor p de cada uno de los coeficientes del modelo. El valor p proporciona la probabilidad de que rechacemos la hipótesis nula dado que en realidad es cierta, o el error tipo I. Por lo tanto, para el valor p menor que α, el valor umbral, rechazaremos la hipótesis nula con un nivel de significancia de α, donde los valores típicos de a son 0,1, 0,05, 0,01, etc. Como siempre, se recomienda poner algo de contexto a los números con visualización de datos. Por lo tanto, usaremos el modelo que creamos para predecir los valores ajustados en la partición de entrenamiento y los valores pronosticados en la partición de prueba. La función de predicción del paquete de estadísticas, como su nombre lo indica, predice los valores de unos datos de entrada en función de un modelo determinado. Lo usaremos para predecir los valores ajustados y pronosticados del modelo de tendencia que entrenamos antes:
train$yhat <- predict(md_trend, newdata = train)
test$yhat <- predict(md_trend, newdata = test)
Crearemos una función de utilidad que traza la serie y el resultado del modelo, utilizando el paquete plotly:
library(plotly)
plot_lm <- function(data, train, test, title = NULL){
p <- plot_ly(data = data,
x = ~ ds,
y = ~ y,
type = "scatter",
mode = "line",
name = "Actual") %>%
add_lines(x = ~ train$ds,
y = ~ train$yhat,
line = list(color = "red"),
name = "Fitted") %>%
add_lines(x = ~ test$ds,
y = ~ test$yhat,
line = list(color = "green", dash = "dot", width = 3),
name = "Forecasted") %>%
layout(title = title,
xaxis = list(title = ""),
yaxis = list(title = "Billion Cubic Feet"),
legend = list(x = 0.05, y = 0.95))
return(p)
}
Los argumentos de la función son los siguientes:
datos: Los datos de entrada, un dato. objeto frame que sigue la misma estructura que la de USgas_df (incluida la variable yhat) tren: el conjunto de entrenamiento correspondiente que se utilizó para entrenar el modelo t prueba: Asimismo, el conjunto de pruebas correspondiente que se utilizó para evaluar el modelo de pronóstico título: el título de la trama, de forma predeterminada, establecido en NULL
Configuremos las entradas de la función plot_1m con la salida del modelo:
plot_lm(data = USgas_df,
train = train,
test = test,
title = "Predicting the Trend Component of the Series")
En general, el modelo pudo capturar el movimiento general de la tendencia, pero una tendencia lineal puede no capturar la ruptura estructural de la tendencia que ocurrió alrededor de 2010. Más adelante, en este capítulo, veremos un método avanzado para capturar un tendencia no lineal. Por último, pero no menos importante, para el análisis comparativo, queremos medir la tasa de error del modelo tanto en el conjunto de entrenamiento como en el de prueba:
mape_trend <- c(mean(abs(train$y - train$yhat) / train$y),
mean(abs(test$y - test$yhat) / test$y))
mape_trend
## [1] 0.1644088 0.1299951
El proceso de modelación y pronóstico del componente estacional sigue el mismo proceso que aplicamos con la tendencia, haciendo una regresión de la serie con la variable estacional que creamos antes:
md_seasonal <- lm(y ~ seasonal, data = train)
summary(md_seasonal)
##
## Call:
## lm(formula = y ~ seasonal, data = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -608.98 -162.34 -50.77 148.40 566.89
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2030.88 14.43 140.747 < 2e-16 ***
## seasonal.L -480.00 50.24 -9.554 < 2e-16 ***
## seasonal.Q 1103.83 50.17 22.000 < 2e-16 ***
## seasonal.C 72.42 50.05 1.447 0.149389
## seasonal^4 174.60 50.07 3.487 0.000592 ***
## seasonal^5 288.01 50.13 5.745 3.13e-08 ***
## seasonal^6 -44.67 50.09 -0.892 0.373460
## seasonal^7 -187.91 49.96 -3.762 0.000218 ***
## seasonal^8 84.95 49.84 1.705 0.089706 .
## seasonal^9 46.16 49.78 0.927 0.354828
## seasonal^10 77.55 49.76 1.559 0.120587
## seasonal^11 -11.09 49.75 -0.223 0.823856
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 216.9 on 214 degrees of freedom
## Multiple R-squared: 0.7521, Adjusted R-squared: 0.7394
## F-statistic: 59.04 on 11 and 214 DF, p-value: < 2.2e-16
Dado que hacemos una regresión de la variable dependiente con una variable categórica, el modelo de regresión crea coeficientes para 11 de las 12 categorías, que son aquellas integradas con los valores de pendiente. Como puede ver en el resumen de regresión del modelo estacional, todos los coeficientes del modelo son estadísticamente significativos. Además, se puede observar que el R cuadrado ajustado del modelo estacional es algo mayor con respecto al modelo de tendencia (0,78 frente a 0,1). Antes de trazar el modelo ajustado y los valores pronosticados con la función plot_1m, actualizaremos los valores de yhat con la función de predicción:
train$yhat <- predict(md_seasonal, newdata = train)
test$yhat <- predict(md_seasonal, newdata = test)
plot_lm(data = USgas_df,
train = train,
test = test,
title = "Predicting the Seasonal Component of the Series")
Como puede verse en el gráfico anterior, el modelo está haciendo un trabajo bastante bueno al capturar la estructura del patrón estacional de la serie. Sin embargo, se puede observar que falta la tendencia de la serie. Antes de agregar los componentes de tendencia y estacional, calificaremos el desempeño del modelo:
mape_seasonal <- c(mean(abs(train$y - train$yhat) / train$y),
mean(abs(test$y - test$yhat) / test$y))
mape_seasonal
## [1] 0.08628973 0.21502100
La alta tasa de error en el conjunto de pruebas está relacionada con el componente de tendencia que no se incluyó en el modelo. El siguiente paso es unir los dos componentes en un modelo y pronosticar los valores característicos de la serie:
md1 <- lm(y ~ seasonal + trend, data = train)
summary(md1)
##
## Call:
## lm(formula = y ~ seasonal + trend, data = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -514.73 -77.17 -17.70 85.80 336.95
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1733.7153 17.0794 101.509 < 2e-16 ***
## seasonal.L -498.1709 29.6354 -16.810 < 2e-16 ***
## seasonal.Q 1115.2951 29.5872 37.695 < 2e-16 ***
## seasonal.C 78.9835 29.5103 2.676 0.00802 **
## seasonal^4 175.6505 29.5196 5.950 1.09e-08 ***
## seasonal^5 285.0192 29.5568 9.643 < 2e-16 ***
## seasonal^6 -49.3611 29.5319 -1.671 0.09610 .
## seasonal^7 -192.3050 29.4540 -6.529 4.77e-10 ***
## seasonal^8 81.8787 29.3835 2.787 0.00581 **
## seasonal^9 44.4849 29.3480 1.516 0.13106
## seasonal^10 76.8636 29.3372 2.620 0.00943 **
## seasonal^11 -11.2755 29.3353 -0.384 0.70109
## trend 2.6182 0.1305 20.065 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 127.9 on 213 degrees of freedom
## Multiple R-squared: 0.9142, Adjusted R-squared: 0.9094
## F-statistic: 189.2 on 12 and 213 DF, p-value: < 2.2e-16
La regresión de la serie con los componentes de tendencia y estacional juntos proporciona un impulso adicional al R cuadrado ajustado del modelo de 0,78 a 0,91. Esto se puede ver en el gráfico del resultado del modelo:
train$yhat <- predict(md1, newdata = train)
test$yhat <- predict(md1, newdata = test)
plot_lm(data = USgas_df,
train = train,
test = test,
title = "Predicting the Seasonal Component of the Series")
mape_md1 <- c(mean(abs(train$y - train$yhat) / train$y),
mean(abs(test$y - test$yhat) / test$y))
mape_md1
## [1] 0.04774945 0.09143290
Hacer una regresión de la serie con los componentes de tendencia y estacional proporciona un aumento significativo tanto en la calidad del ajuste del modelo como en la precisión del modelo. Sin embargo, al observar el gráfico del ajuste y pronóstico del modelo, se puede observar que la tendencia del modelo es demasiado lineal y le falta la ruptura estructural de la tendencia de la serie. Este es el punto en el que agregar un componente polinomial al modelo podría proporcionar una mejora adicional en la precisión del modelo.
Una técnica sencilla para capturar una tendencia no lineal es agregar un componente polinómico a la tendencia de la serie para capturar la curvatura de la tendencia a lo largo del tiempo. Usaremos el argumento I, que nos permite aplicar operaciones matemáticas en cualquiera de los objetos de entrada. Por lo tanto, usaremos este argumento para agregar un segundo grado del polinomio para la entrada de tendencia:
md2 <- lm(y ~ seasonal + trend + I(trend^2), data = train)
summary(md2)
##
## Call:
## lm(formula = y ~ seasonal + trend + I(trend^2), data = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -468.47 -54.66 -2.21 63.11 294.32
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.882e+03 2.199e+01 85.568 < 2e-16 ***
## seasonal.L -4.917e+02 2.530e+01 -19.438 < 2e-16 ***
## seasonal.Q 1.121e+03 2.525e+01 44.381 < 2e-16 ***
## seasonal.C 8.247e+01 2.518e+01 3.275 0.00123 **
## seasonal^4 1.763e+02 2.519e+01 6.999 3.35e-11 ***
## seasonal^5 2.835e+02 2.522e+01 11.243 < 2e-16 ***
## seasonal^6 -5.175e+01 2.520e+01 -2.054 0.04123 *
## seasonal^7 -1.946e+02 2.513e+01 -7.741 3.97e-13 ***
## seasonal^8 8.030e+01 2.507e+01 3.203 0.00157 **
## seasonal^9 4.362e+01 2.504e+01 1.742 0.08293 .
## seasonal^10 7.651e+01 2.503e+01 3.057 0.00253 **
## seasonal^11 -1.137e+01 2.503e+01 -0.454 0.65005
## trend -1.270e+00 4.472e-01 -2.840 0.00494 **
## I(trend^2) 1.713e-02 1.908e-03 8.977 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 109.1 on 212 degrees of freedom
## Multiple R-squared: 0.9379, Adjusted R-squared: 0.9341
## F-statistic: 246.1 on 13 and 212 DF, p-value: < 2.2e-16
Agregar el polinomio de segundo grado al modelo de regresión no condujo a una mejora significativa de la bondad de ajuste del modelo. En el otro modelo, como se puede ver en el siguiente gráfico de resultados del modelo, este simple cambio en la estructura del modelo nos permite capturar la ruptura estructural de la tendencia a lo largo del tiempo:
Además, como podemos ver en el modelo que sigue la puntuación MAPE, la precisión del modelo mejoró significativamente al agregar la tendencia polinómica al modelo de regresión, ya que el error en el conjunto de pruebas cayó del 9,2% al 4,5%:
train$yhat <- predict(md2, newdata = train)
test$yhat <- predict(md2, newdata = test)
plot_lm(data = USgas_df,
train = train,
test = test,
title = "Predicting the Seasonal Component of the Series")
mape_md2 <- c(mean(abs(train$y - train$yhat) / train$y),
mean(abs(test$y - test$yhat) / test$y))
mape_md2
## [1] 0.03688770 0.04212618
#La función tslm
Hasta ahora, hemos visto el proceso manual de transformar un objeto ts a un formato de modelo de pronóstico de regresión lineal. La función ts1m del paquete de pronóstico proporciona una función incorporada para transformar un objeto ts en un modelo de pronóstico de regresión lineal. Con la función tslm, puede configurar el componente de regresión junto con otras funciones. Ahora repetiremos el ejemplo anterior y pronosticaremos las últimas 12 observaciones de la serie USgas con la función tslm utilizando la tendencia, el cuadrado de la tendencia y los componentes estacionales. Primero, dividamos la serie en particiones de entrenamiento y prueba usando la función ts_split:
USgas_split <- ts_split(USgas, sample.out = h)
train.ts <- USgas_split$train
test.ts <- USgas_split$test
A continuación, aplicaremos la misma fórmula que usamos para crear el modelo de pronóstico md2 anterior usando la función tslm:
library(forecast)
md3 <- tslm(train.ts ~ season + trend + I(trend^2))
summary(md3)
##
## Call:
## tslm(formula = train.ts ~ season + trend + I(trend^2))
##
## Residuals:
## Min 1Q Median 3Q Max
## -468.47 -54.66 -2.21 63.11 294.32
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.635e+03 3.224e+01 81.738 < 2e-16 ***
## season2 -3.004e+02 3.540e+01 -8.487 3.69e-15 ***
## season3 -4.841e+02 3.540e+01 -13.676 < 2e-16 ***
## season4 -9.128e+02 3.540e+01 -25.787 < 2e-16 ***
## season5 -1.099e+03 3.540e+01 -31.033 < 2e-16 ***
## season6 -1.118e+03 3.540e+01 -31.588 < 2e-16 ***
## season7 -9.547e+02 3.540e+01 -26.968 < 2e-16 ***
## season8 -9.322e+02 3.541e+01 -26.329 < 2e-16 ***
## season9 -1.136e+03 3.541e+01 -32.070 < 2e-16 ***
## season10 -1.046e+03 3.541e+01 -29.532 < 2e-16 ***
## season11 -8.001e+02 3.590e+01 -22.286 < 2e-16 ***
## season12 -2.618e+02 3.590e+01 -7.293 5.95e-12 ***
## trend -1.270e+00 4.472e-01 -2.840 0.00494 **
## I(trend^2) 1.713e-02 1.908e-03 8.977 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 109.1 on 212 degrees of freedom
## Multiple R-squared: 0.9379, Adjusted R-squared: 0.9341
## F-statistic: 246.1 on 13 and 212 DF, p-value: < 2.2e-16
Como puede observar en el resultado anterior, ambos modelos (md2 y md3) son idénticos. Existen varias ventajas al utilizar la función ts1m, en lugar de configurar manualmente un modelo de regresión para la serie con la función 1m: • Eficiencia: no requiere transformar la serie a un dato. ingeniería de características y objetos de marco • El objeto de salida admite todas las funciones del pronóstico (como las funciones de precisión y verificación de residuos) y los paquetes de TSstudio (como las funciones test_forecast y plot_forecast)
#Modelado de eventos únicos y eventos no estacionales En algunos casos, los datos de series temporales pueden contener patrones inusuales que se repiten o no con el tiempo. Los siguientes son ejemplos de tales eventos: ⚫ Valores atípicos: uno o varios eventos únicos que se salen de los patrones normales de la serie. • Ruptura estructural: Un hecho significativo que cambia los patrones históricos de la serie. Un ejemplo común es un cambio en el crecimiento de la serie. • Eventos recurrentes no estacionales: Un evento que se repite de un ciclo a otro, pero el momento en que ocurren cambia de un ciclo a otro. Un ejemplo común de este tipo de eventos son las vacaciones de Semana Santa, que ocurren cada año alrededor de marzo/abril.
Al no expresarse en el modelo de regresión, este tipo de eventos sesgará los coeficientes estimados, ya que el modelo ponderará ese tipo de eventos junto con los eventos regulares de la serie. El uso de variables de codificación activa, binarias o de bandera podría ayudar al modelo a ignorar este tipo de eventos o ajustar los coeficientes del modelo en consecuencia. Por ejemplo, se puede observar en el gráfico descompuesto de la serie de gas estadounidense mostrada anteriormente que la tendencia de la serie tuvo una ruptura estructural alrededor del año 2010. Si bien el crecimiento antes del año 2010 fue relativamente plano, la pendiente de la tendencia cambió después, con un crecimiento positivo. . En este caso, podemos utilizar una variable binaria que sea igual a cero para las observaciones anteriores al año 2010 y un año después. Hacer una regresión en el modelo slm con variables externas requiere un objeto data.frame separado con las variables correspondientes. El siguiente ejemplo demuestra el proceso de creación de una variable binaria externa que es igual a 0 antes del año 2010 y a 1 después, utilizando la tabla USgas_df:
r <- which(USgas_df$ds == as.Date("2014-01-01"))
USgas_df$s_break <- ifelse(year(USgas_df$ds) >= 2010, 1, 0)
USgas_df$s_break[r] <- 1
md3 <- tslm(USgas ~ season + trend + I(trend^2) + s_break, data = USgas_df)
summary(md3)
##
## Call:
## tslm(formula = USgas ~ season + trend + I(trend^2) + s_break,
## data = USgas_df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -469.25 -50.68 -2.66 63.63 275.89
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.661e+03 3.200e+01 83.164 < 2e-16 ***
## season2 -3.054e+02 3.448e+01 -8.858 2.61e-16 ***
## season3 -4.849e+02 3.448e+01 -14.062 < 2e-16 ***
## season4 -9.272e+02 3.449e+01 -26.885 < 2e-16 ***
## season5 -1.108e+03 3.449e+01 -32.114 < 2e-16 ***
## season6 -1.127e+03 3.450e+01 -32.660 < 2e-16 ***
## season7 -9.568e+02 3.450e+01 -27.730 < 2e-16 ***
## season8 -9.340e+02 3.451e+01 -27.061 < 2e-16 ***
## season9 -1.138e+03 3.452e+01 -32.972 < 2e-16 ***
## season10 -1.040e+03 3.453e+01 -30.122 < 2e-16 ***
## season11 -7.896e+02 3.497e+01 -22.577 < 2e-16 ***
## season12 -2.649e+02 3.498e+01 -7.571 9.72e-13 ***
## trend -1.928e+00 4.479e-01 -4.304 2.51e-05 ***
## I(trend^2) 1.862e-02 1.676e-03 11.113 < 2e-16 ***
## s_break 6.060e+01 2.836e+01 2.137 0.0337 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 109 on 223 degrees of freedom
## Multiple R-squared: 0.9423, Adjusted R-squared: 0.9387
## F-statistic: 260.3 on 14 and 223 DF, p-value: < 2.2e-16
Como se puede observar en el resumen del modelo anterior, la variable ruptura estructural es estadísticamente significativa, con un nivel de 0,01. Del mismo modo, en el caso de valores atípicos o días festivos, se puede aplicar codificación activa estableciendo una variable binaria que sea igual a 1 siempre que ocurra un evento atípico o recurrente no estacional, y 0 en caso contrario. i Tenga en cuenta que, una vez que haya entrenado un modelo de pronóstico con tslm función con el uso de variables externas, tendrá que producir el valores futuros de esas variables, ya que se utilizarán como entrada del pronóstico.
#Previsión de una serie con componentes multiestacionales: un estudio de caso Una de las principales ventajas del modelo de regresión, frente a los modelos tradicionales de series temporales como ARIMA o Holt-Winters, es que proporciona una amplia gama de opciones de personalización y nos permite modelar y pronosticar datos de series temporales complejas como series con multiestacionalidad. En los siguientes ejemplos, utilizaremos la serie UKgrid para demostrar el enfoque de pronóstico de una serie multiestacional con un modelo de regresión lineal.
#La serie UKgrid
La serie UKgrid representa la demanda de electricidad de la red nacional en el Reino Unido y está disponible en el paquete UKgrid. Esta serie representa datos de series temporales de alta frecuencia con frecuencia de media hora. Utilizaremos la función de extracción de cuadrícula del paquete UKgrid para definir la serie y las características principales (por ejemplo, formato de datos, variables, frecuencia, etc.). Esta función de transformación nos permite agregar la frecuencia de la serie desde cada media hora a una frecuencia más baja, como por hora, por día o por mes. Como nuestro objetivo aquí es pronosticar la demanda diaria en los próximos 365 días, configuraremos la serie en frecuencia diaria utilizando la estructura data.frame:
library(UKgrid)
UKdaily <- extract_grid(type = "data.frame",
columns = "ND",
aggregate = "daily")
head(UKdaily)
## TIMESTAMP ND
## 1 2005-04-01 1920069
## 2 2005-04-02 1674699
## 3 2005-04-03 1631352
## 4 2005-04-04 1916693
## 5 2005-04-05 1952082
## 6 2005-04-06 1964584
Como puedes ver, esta serie tiene dos variables: • TIMESTAMP: un objeto de fecha utilizado como marca de tiempo o índice de la serie. ND: La demanda neta de electricidad Usaremos la función ts_plot para trazar y revisar la estructura de la serie:
ts_plot(UKdaily,
title = "The UK National Demand for Electricity",
Ytitle = "MW",
Xtitle = "Year")
Como se puede ver en el gráfico anterior, la serie tiene una clara tendencia bajista y un patrón estacional de cadena. Como vimos en el Capítulo 6, Análisis de estacionalidad, esta serie tiene múltiples patrones de estacionalidad: ⚫ Diario: Un ciclo de 365 días al año • Día de la semana: un ciclo de 7 días • Mensual: Efectuado por el clima Se puede ver evidencia de esos patrones en el siguiente mapa de calor de la serie desde 2016 usando la función ts_heatmap del paquete TSstudio:
ts_heatmap(UKdaily[which(year(UKdaily$TIMESTAMP) >= 2016),],
title = "UK the Daily National Grid Demand Heatmap")
Como puede ver en el mapa de calor de la serie, la demanda general aumenta a lo largo de las semanas de invierno (por ejemplo, las semanas 1 a 12 del calendario y las semanas 44 a 52). Además, se puede observar el cambio de la serie durante los días de la semana, ya que la demanda aumenta durante los días laborables de la semana, y disminuye durante el fin de semana.
#Preprocesamiento e ingeniería de funciones de la serie UKdaily Para capturar los componentes estacionales de la serie, estableceremos la serie como frecuencia diaria y crearemos las dos funciones siguientes: • Indicador del día de la semana ⚫ Indicador del mes del año
Además, como es razonable suponer (confirmaremos esta suposición con la función ACF una vez que hayamos transformado la serie en un objeto ts) que la serie tiene una fuerte correlación con los rezagos estacionales, crearemos una variable de rezago con un retraso de 365 observaciones. Utilizaremos el paquete dplyr para crear esas funciones:
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
UKdaily <- UKdaily %>%
mutate(wday = wday(TIMESTAMP, label = TRUE),
month = month(TIMESTAMP, label = TRUE),
lag365 = dplyr::lag(ND, 365)) %>%
filter(!is.na(lag365)) %>%
arrange(TIMESTAMP)
Recuerde que el costo de utilizar una variable de rezago con una longitud de N es la pérdida de las primeras N observaciones (ya que los rezagos de esas observaciones no pueden generarse a partir de la serie). Por lo tanto, utilizamos las funciones de filtro para eliminando las filas de la tabla en las que falta la variable lag365 (es decir, las primeras 365 observaciones).
Repasemos la estructura de la tabla UKdaily después de agregar esas nuevas características:
str(UKdaily)
## 'data.frame': 4939 obs. of 5 variables:
## $ TIMESTAMP: Date, format: "2006-04-01" "2006-04-02" ...
## $ ND : int 1718405 1691341 1960325 2023886 2026204 2008422 1981175 1770440 1749715 2012865 ...
## $ wday : Ord.factor w/ 7 levels "dom\\."<"lun\\."<..: 7 1 2 3 4 5 6 7 1 2 ...
## $ month : Ord.factor w/ 12 levels "ene"<"feb"<"mar"<..: 4 4 4 4 4 4 4 4 4 4 ...
## $ lag365 : int 1920069 1674699 1631352 1916693 1952082 1964584 1990895 2003982 1811436 1684720 ...
start_date <- min(UKdaily$TIMESTAMP)
start <- c(year(start_date),yday(start_date))
UK_ts <- ts(UKdaily$ND,
start = c(year(start_date), yday(start_date)),
frequency = 365)
```{r}# ts_acf(UK_ts, lag.max = 365 * 4)
# -------- Codigo 29 --------
El gráfico ACF anterior confirma nuestra suposición y la serie tiene una fuerte relación con los rezagos estacionales, en particular el rezago 365, el primer rezago.
Como nota al margen, puede estar seguro de que la serie también tiene una fuerte correlación con los desfases semanales (es decir, desfases 7, 14, 21, etc.). Sin embargo, en general, no se recomienda utilizar retrasos que sean menores que el horizonte de pronóstico (por ejemplo, retraso 7, cuando el horizonte de pronóstico es 365), ya que también tendrá que pronosticar esos retrasos para poder usarlos. como entrada en el modelo. Esto implica un esfuerzo adicional, ya que habrá que pronosticar esos retrasos. Además, puede aumentar el sesgo del pronóstico ya que utilizamos valores pronosticados como datos de entrada.
Ahora, después de haber creado las nuevas características para el modelo y configurar el objeto ts, estamos listos para dividir la serie de entrada y el objeto de características externas correspondiente que creamos (UKdaily) en una partición de entrenamiento y prueba. Como nuestro objetivo es pronosticar las próximas 365 observaciones y la longitud de la serie es lo suficientemente grande (2540 observaciones), podemos permitirnos establecer la partición de prueba a la longitud del horizonte de pronóstico de 365 observaciones. Estableceremos h, una variable indicadora, en 365 y la usaremos para definir las particiones y, más adelante, el horizonte de previsión:
Como antes, dividiremos la serie en particiones de entrenamiento y prueba con la función ts_split:
De manera similar, tenemos que dividir las características que creamos para el modelo de regresión (las características estacionales y de retraso) en una partición de entrenamiento y prueba siguiendo exactamente el mismo orden que usamos para el objeto ts correspondiente. Usaremos los datos. Funcionalidad de índice de cuadros para configurar la tabla UKdaily para entrenar y probar particiones:
```r
h <- 365
UKpartitions <- ts_split(UK_ts, sample.out = h)
train_ts <- UKpartitions$train
test_ts <- UKpartitions$test
train_df <- UKdaily[1:(nrow(UKdaily) - h), ]
test_df <- UKdaily[(nrow(UKdaily) - h + 1):nrow(UKdaily), ]
Entrenamiento y prueba del modelo de pronóstico. Una vez que hayamos creado las diferentes funciones para el modelo, estamos listos para iniciar el proceso de capacitación del modelo de pronóstico. Usaremos la partición de entrenamiento y comenzaremos a entrenar los siguientes tres modelos: ⚫ Modelo de referencia: Regresión de la serie con el componente estacional y de tendencia utilizando las funciones integradas de la función ts1m. Como establecemos la frecuencia de la serie en 365, la característica estacional de la serie se refiere a la estacionalidad diaria. • Modelo multiestacional: Agregando los indicadores de día de la semana y mes del año para capturar la multiestacionalidad de la serie. • Un modelo multiestacional con rezago estacional: Utilizando, además de los indicadores estacionales, la variable de rezago estacional. La comparación de estos tres modelos se basará en los siguientes criterios: • Rendimiento del modelo en el conjunto de entrenamiento y prueba usando la puntuación MAPE • Visualización de los valores ajustados y pronosticados versus los valores reales de la serie usando la función de pronóstico de prueba
Comenzar con un modelo simplista (modelo de referencia) nos permitirá observar si agregar nuevas características contribuye al rendimiento del modelo o si debemos evitarlo, ya que agregar más características o complejidad al modelo no siempre produce mejores resultados. Comenzaremos con el modelo base, haciendo una regresión de la serie con sus componentes estacionales y de tendencia:
A continuación, utilizaremos el modelo entrenado, md_ts1m1, para pronosticar los próximos 365 días de la serie, correspondientes a las observaciones de la partición de prueba, usando la función de pronóstico:
Comparemos el rendimiento del modelo en los conjuntos de entrenamiento y prueba usando la función test_forecast:
md_tslm1 <- tslm(train_ts ~ season + trend)
fc_tslm1 <- forecast(md_tslm1, h = h)
test_forecast(actual = UK_ts,
forecast.obj = fc_tslm1,
test = test_ts)
Podemos observar en el gráfico de rendimiento anterior que el modelo de referencia está haciendo un gran trabajo al capturar tanto la tendencia de la serie como la estacionalidad del día del año. Por otro lado, no logra capturar la oscilación relacionada con el día de la semana. La puntuación MAPE del modelo, como podemos ver en el resultado de la siguiente función de precisión, es 6,09 % y 7,77 % en las particiones de entrenamiento y prueba, respectivamente:
accuracy(fc_tslm1, test_ts)
## ME RMSE MAE MPE MAPE MASE
## Training set -4.781286e-12 121551.4 100439.64 -0.5721337 6.294111 0.8418677
## Test set -1.740215e+04 123156.6 96785.27 -1.8735336 7.160573 0.8112374
## ACF1 Theil's U
## Training set 0.5277884 NA
## Test set 0.5106681 1.071899
md_tslm2 <- tslm(train_ts ~ season + trend + wday, data = train_df)
Como ahora utilizamos funciones de una fuente de datos externa, tenemos que especificar los datos de entrada con el argumento de datos. Ahora repetiremos el mismo proceso que antes, utilizando un pronóstico con el modelo entrenado y evaluando el rendimiento del modelo:
fc_tslm2 <- forecast(md_tslm2, h = h, newdata = test_df)
test_forecast(actual = UK_ts,
forecast.obj = fc_tslm2,
test = test_ts)
Al observar el gráfico de rendimiento anterior del segundo modelo, podemos observar la contribución de las características estacionales en el pronóstico, ya que el segundo modelo fue capaz de capturar tanto la tendencia como los patrones multiestacionales de la serie. Esto también se puede observar en la puntuación del modelo MAPE, que cayó al 2,87 % y al 5,23 % en las particiones de entrenamiento y prueba, respectivamente:
accuracy(fc_tslm2, test_ts)
## ME RMSE MAE MPE MAPE MASE
## Training set 8.172823e-12 70245.98 52146.79 -0.1738708 3.167605 0.4370853
## Test set -1.764563e+04 80711.71 65373.21 -1.3715505 4.682071 0.5479470
## ACF1 Theil's U
## Training set 0.7513664 NA
## Test set 0.6075598 0.68445
md_tslm3 <- tslm(train_ts ~ season + trend + wday + month + lag365, data = train_df)
fc_tslm3 <- forecast(md_tslm3, h = h, newdata = test_df)
test_forecast(actual = UK_ts,
forecast.obj = fc_tslm3,
test = test_ts)
accuracy(fc_tslm3, test_ts)
## ME RMSE MAE MPE MAPE MASE
## Training set -9.836939e-13 69904.92 52006.75 -0.1717563 3.163385 0.4359116
## Test set -1.754061e+04 81783.55 65957.66 -1.3613252 4.722083 0.5528457
## ACF1 Theil's U
## Training set 0.7500146 NA
## Test set 0.6094666 0.6925767
#Selección de modelo Ahora es el momento de seleccionar un modelo. En este punto, está claro que el segundo y tercer modelo funcionan mejor que el primer modelo. Como tanto el segundo como el tercer modelo lograron una puntuación MAPE bastante similar, con una pequeña ventaja para el tercer modelo, deberíamos preguntarnos si una mejora de menos del 0,2% en la tasa de error del conjunto de pruebas vale el costo de usar el variable de rezago (es decir, la pérdida de 365 observaciones y el costo adicional de un grado de libertad para el modelo). Eso depende. No hay una respuesta sencilla a esta pregunta. Además, depende del objetivo del pronóstico. Se recomienda que consideres la siguiente prueba: • La primera pregunta que debe hacerse en este caso: ¿Es la variable de rezago estadísticamente significativa? Si la variable no es estadísticamente significativa, no tiene sentido continuar la discusión y es mejor descartarla. En el caso del tercer modelo, podemos utilizar la función de resumen para observar el nivel de significancia de la variable lag365:
summary(md_tslm3)$coefficients %>% tail(1)
## Estimate Std. Error t value Pr(>|t|)
## lag365 -0.06038328 0.01545495 -3.907051 9.490321e-05
El valor p de la variable lag365 indicó que la variable es estadísticamente significativa a un nivel de 0,001. De manera similar, podemos aplicar una única prueba ANOVA con la función anova del paquete stats y comprobar si la variable adicional es significativa:
anova(md_tslm3)
## Analysis of Variance Table
##
## Response: train_ts
## Df Sum Sq Mean Sq F value Pr(>F)
## season 364 1.4279e+14 3.9227e+11 73.5348 < 2.2e-16 ***
## trend 1 7.2634e+13 7.2634e+13 13615.7078 < 2.2e-16 ***
## wday 6 4.5009e+13 7.5016e+12 1406.2214 < 2.2e-16 ***
## month 11 1.3721e+11 1.2473e+10 2.3382 0.007266 **
## lag365 1 8.1432e+10 8.1432e+10 15.2650 9.49e-05 ***
## Residuals 4190 2.2352e+13 5.3345e+09
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
La prueba ANOVA también indicó que la variable lag365 es estadísticamente significativa. ⚫ Backtesting: podría darse el caso de que el tercer modelo sea más preciso simplemente por casualidad y no porque la variable de retraso contribuya a la precisión del modelo, ya que la diferencia es relativamente pequeña. Por lo tanto, el backtesting de ambos modelos podría ayudar a validar si la contribución de la variable de rezago es consistente durante varios períodos de prueba. Dejaré que usted realice pruebas retrospectivas para ambos modelos como un ejercicio divertido. Se pueden aplicar métodos más sólidos para la selección de características, como la regresión por pasos, de cresta o de lazo. Aunque esos métodos no están dentro del alcance de este libro, se recomienda leer sobre ellos. En el Capítulo 12, Pronósticos con modelos de aprendizaje automático, exploraremos enfoques de regresión avanzados con modelos de aprendizaje automático, que incluyen métodos de selección de características.
En aras de la simplicidad, seguiremos los criterios de precisión y seleccionaremos el tercer modelo para pronosticar la serie. El siguiente paso es volver a entrenar el modelo en todas las series y pronosticar los próximos 365 días:
final_md <- tslm(UK_ts ~ season + trend + wday + month + lag365,
data = UKdaily)
#Análisis de residuos Justo antes de finalizar el pronóstico, hagamos un análisis de los residuos del modelo seleccionado utilizando la función de verificación de residuos:
checkresiduals(final_md)
##
## Breusch-Godfrey test for serial correlation of order up to 730
##
## data: Residuals from Linear regression model
## LM test = 3301.1, df = 730, p-value < 2.2e-16
Como puede verse en el gráfico de resumen de residuos anterior, los residuos no son ruido blanco, ya que existe cierta autocorrelación entre las series de residuos y sus rezagos. Esto es técnicamente una indicación de que el modelo no capturó todos los patrones o información que existe en la serie. Una forma de abordar esta cuestión es identificar variables adicionales que puedan explicar la variación de los residuos. El principal desafío de este enfoque es que es difícil identificar variables externas que puedan explicar la variación de los residuos y que además sean factibles de pronosticar. Por ejemplo, es razonable suponer que los patrones climáticos afectan la demanda de electricidad, pero es difícil predecir esos patrones climáticos con un año de antelación.
Un enfoque alternativo, cuando quedan patrones en los residuos del modelo, es tratar los residuos del modelo como datos de series de tiempo separados y modelarlos con otro modelo de pronóstico de series de tiempo. Un enfoque común es una regresión con error ARIMA, que presentaremos en el Capítulo 11, Pronósticos con modelos ARIMA.
#Finalizando el pronóstico Finalicemos el proceso y utilicemos el modelo entrenado seleccionado para pronosticar las 365 observaciones futuras. Como usamos variables externas con la función tslm, tendremos que generar sus valores futuros. Esto es relativamente simple, ya que utilizamos variables deterministas. Por lo tanto, crearemos un objeto data.frame con los valores de wday, mes y lag365 para las próximas 365 observaciones futuras. Un enfoque simplista es crear primero las fechas correspondientes de las observaciones pronosticadas y luego extraer de este objeto el día de la semana y el mes del año:
UK_fc_df <- data.frame(date = seq.Date(from = max(UKdaily$TIMESTAMP) + days(1),
by = "day",
length.out = h))
A continuación, podemos utilizar la variable de fecha para crear las variables día y mes con el paquete lubridate:
UK_fc_df$wday <- factor(lubridate::wday(UK_fc_df$date, label = TRUE), ordered = FALSE)
UK_fc_df$month <- factor(month(UK_fc_df$date, label = TRUE), ordered = FALSE)
UK_fc_df$lag365 <- tail(UKdaily$ND, h)
UKgrid_fc <- forecast(final_md, h = h, newdata = UK_fc_df)
Por último, pero no menos importante, trazaremos el pronóstico final con la función plot_forecast del paquete TSstudio:
plot_forecast(UKgrid_fc,
title = "The UK National Demand for Electricity Forecast",
Ytitle = "MW",
Xtitle = "Year")
#Sección - 10 # Código del Capítulo 10
#Predicción con modelos de suavizado exponencial En el Capítulo 5, Descomposición de datos de series temporales, analizamos la aplicación de funciones de suavizado para la reducción de ruido en datos de series temporales y la estimación de tendencias. En este capítulo, ampliaremos el uso de funciones de suavizado e introduciremos sus aplicaciones de pronóstico. Esta familia de modelos de pronóstico puede manejar una variedad de tipos de series temporales, desde series sin tendencias ni componentes estacionales hasta series con tendencias y componentes estacionales. Comenzaremos con el modelo básico de media móvil y modelos simples de suavizado exponencial, y luego agregaremos más capas al modelo, así como la capacidad del modelo para manejar datos complejos de series de tiempo. En este capítulo, cubriremos los siguientes temas: • Previsión con modelos de media móvil • Enfoques de previsión con modelos de suavizado • Ajuste de parámetros para modelos de suavizado Requerimiento técnico Los siguientes paquetes se utilizarán en este capítulo: ⚫ pronóstico: versión 8.5 y superior ⚫ h20: Versión 3.22.1.1 y superior y Java versión 7 y superior ⚫ TSstudio: Versión 0.1.4 y superior ⚫ trama: Versión 4.8 y superior ⚫ dplyr: Versión 0.8.1 y superior ⚫ tidyr: Versión 0.8.3 y superiores • Quandl: Versión 2.9.1 y superiores
Descripción de la imagen
Descripción de la imagen
Se recomienda realizar pronósticos con la función SMA cuando la serie de entrada no tiene patrones estructurales, como componentes de tendencia y estacionales. En este caso, es razonable suponer que los valores pronosticados son relativamente cercanos a las últimas observaciones de la serie. En el siguiente ejemplo, crearemos una función SMA personalizada y la usaremos para pronosticar los precios mensuales del café Robusta en los próximos 12 meses. Los precios del café Robusta (USD por kg) son un ejemplo de datos de series temporales que no tienen tendencias específicas ni patrones estacionales. Más bien, esta serie tiene un componente de ciclo, donde la magnitud y la duración del ciclo siguen cambiando de un ciclo a otro. Esta serie es parte del conjunto de datos Coffee_Prices y está disponible en el paquete TSstudio:
#library(TSstudio)
data("Coffee_Prices")
ts_info(Coffee_Prices)
## The Coffee_Prices series is a mts object with 2 variables and 701 observations
## Frequency: 12
## Start time: 1960 1
## End time: 2018 5
Este conjunto de datos es un objeto mts y contiene los precios mensuales (USD por kg) de los precios del café Robusta y Arábica entre 1960 y 2018. Extraeremos los precios mensuales del café Robusta del objeto Coffee_Prices:
robusta <- Coffee_Prices[,1]
library(plotly)
ts_plot(robusta,
title = "The Robusta Coffee Monthly Prices",
Ytitle = "Price in USD",
Xtitle = "Year")
A continuación, crearemos una función SMA básica utilizando algunas de las funciones del paquete tidyr:
library(tidyr)
sma_forecast <- function(df, h, m, w = NULL){
# Error handling
if(h > nrow(df)){
stop("The length of the forecast horizon must be shorter than the length of the series")}
if(m > nrow(df)){
stop("The length of the rolling window must be shorter than the length of the series")}
if(!is.null(w)){
if(length(w) != m){
stop("The weight argument is not aligned with the length of the rolling window")
} else if(sum(w) !=1){
stop("The sum of the average weight is different than 1")
}
}
# Setting the average weigths
if(is.null(w)){
w <- rep(1/m, m)
}
# Setting the data frame
#-----------------------
# Changing the Date object column name
names(df)[1] <- "date"
# Setting the training and testing partition
# according to the forecast horizon
df$type <- c(rep("train", nrow(df) - h),
rep("test", h))
# Spreading the table by the partition type
df1 <- df %>% spread(key = type, value = y)
# Create the target variable
df1$yhat <- df1$train
# Simple moving average function
for(i in (nrow(df1) - h + 1):nrow(df1)){
r <- (i-m):(i-1)
df1$yhat[i] <- sum(df1$yhat[r] * w)
}
# dropping from the yhat variable the actual values
# that were used for the rolling window
df1$yhat <- ifelse(is.na(df1$test), NA, df1$yhat)
df1$y <- ifelse(is.na(df1$test), df1$train, df1$test)
return(df1)
}
Los argumentos de la función son los siguientes: df: la serie de entrada en un formato de marco de datos de dos columnas, donde la primera columna es un objeto Fecha y la segunda son los valores reales de la serie.
⚫h: El horizonte del pronóstico. Para el siguiente ejemplo, la función estableció las últimas h observaciones como un conjunto de prueba. Esto nos permite comparar el rendimiento del modelo. m: La longitud de la ventana móvil. w: Los pesos del promedio, por defecto, usando pesos iguales (o promedio aritmético).
La función sma_forecast tiene los siguientes componentes: • Manejo de errores: Pruebe y verifique si los argumentos de entrada de la función son válidos. Si una de las pruebas definidas no es verdadera, detendrá la ejecución de la función y generará un mensaje de error. ⚫ Preparación de datos: Esto define los datos. objeto de marco basado en la longitud de la ventana y el horizonte de pronóstico. • Cálculo de datos: Calcula la media móvil simple y devuelve los resultados. Utilicemos esta función para demostrar el rendimiento de la función SMA. Pronosticaremos los últimos 24 meses de la serie Robusta utilizando una ventana móvil de 3, 6, 12, 24 y 36 meses:
robusta_df <- ts_to_prophet(robusta)
robusta_fc_m1 <- sma_forecast(robusta_df, h = 24, m = 1)
robusta_fc_m6 <- sma_forecast(robusta_df, h = 24, m = 6)
robusta_fc_m12 <- sma_forecast(robusta_df, h = 24, m = 12)
robusta_fc_m24 <- sma_forecast(robusta_df, h = 24, m = 24)
robusta_fc_m36 <- sma_forecast(robusta_df, h = 24, m = 36)
Usaremos el paquete plotly para trazar los resultados de las diferentes funciones de media móvil:
library(plotly)
plot_ly(data = robusta_df[650:nrow(robusta_df),], x = ~ ds, y = ~ y,
type = "scatter", mode = "lines",
name = "Actual") %>%
add_lines(x = robusta_fc_m1$date, y = robusta_fc_m1$yhat,
name = "SMA - 1", line = list(dash = "dash")) %>%
add_lines(x = robusta_fc_m6$date, y = robusta_fc_m6$yhat,
name = "SMA - 6", line = list(dash = "dash")) %>%
add_lines(x = robusta_fc_m12$date, y = robusta_fc_m12$yhat,
name = "SMA - 12", line = list(dash = "dash")) %>%
add_lines(x = robusta_fc_m24$date, y = robusta_fc_m24$yhat,
name = "SMA - 24", line = list(dash = "dash")) %>%
add_lines(x = robusta_fc_m36$date, y = robusta_fc_m36$yhat,
name = "SMA - 36", line = list(dash = "dash")) %>%
layout(title = "Forecasting the Robusta Coffee Monthly Prices",
xaxis = list(title = ""),
yaxis = list(title = "USD per Kg."))
Las principales observaciones del gráfico anterior son las siguientes: • Si la longitud de la ventana rodante es más corta: • El rango del pronóstico está bastante cerca de las observaciones más recientes de la serie. • Cuanto más rápido converja el pronóstico a algún valor constante • Si la longitud de la ventana es mayor: ⚫ Cuanto más tiempo pase hasta que el pronóstico converja a algún valor constante ⚫ Puede manejar mejores shocks y valores atípicos • Un modelo de pronóstico SMA con una ventana móvil de una longitud de 1 es equivalente al modelo de pronóstico Naïve. Si bien la función SMA es bastante sencilla de usar y económica en potencia de cálculo, tiene algunas limitaciones: El poder de pronóstico de la función SMA se limita a un horizonte corto y puede tener un desempeño deficiente en el largo plazo. • Este método está limitado a datos de series temporales, sin tendencias ni patrones estacionales. Esto afecta principalmente al promedio aritmético que suaviza el patrón estacional y se vuelve plano en el largo plazo.
##Media móvil ponderada La media móvil ponderada (WMA) es una versión ampliada de la función SMA y se basa en el uso de la media ponderada (a diferencia de la media aritmética). La principal ventaja de la función WMA sobre la función SMA es que permite distribuir el peso de los rezagos sobre la ventana enrollable. Esto puede resultar útil cuando la serie tiene una alta correlación con algunos de sus rezagos. La función WMA se puede formalizar mediante la siguiente expresión:
YT+n=w1YT+n-m+…+wmYT+n-1
Aquí, Y+n es el valor n pronosticado de la serie en el momento T +n, y w es un escalar de tamaño m que define el peso de cada observación en la ventana móvil. El uso del promedio ponderado proporciona más flexibilidad ya que puede manejar series con un patrón estacional. En el siguiente ejemplo, usaremos la función sma_forecast que creamos anteriormente para pronosticar los últimos 24 meses del conjunto de datos de USgas. En este caso, utilizaremos el argumento w para establecer el peso promedio y así transformar la función de SMA a WMA. Como hicimos anteriormente, primero transformaremos la serie al formato data.frame con la función ts_to_prophet:
data(USgas)
USgas_df <- ts_to_prophet(USgas)
En el siguiente ejemplo, utilizaremos las dos estrategias siguientes: • El modelo WMA para aplicar todo el peso sobre el desfase estacional (desfase 12):
USgas_fc_m12a <- sma_forecast(USgas_df,
h = 24,
m = 12,
w = c(1, rep(0,11)))
• El modelo WMA para ponderar el primer rezago con 0,2 y el rezago estacional (rezago 12) con 0,8:
USgas_fc_m12b <- sma_forecast(USgas_df,
h = 24,
m = 12,
w = c(0.8, rep(0,10), 0.2))
Ambas WMA:
plot_ly(data = USgas_df[190:nrow(USgas_df),], x = ~ ds, y = ~ y,
type = "scatter", mode = "lines",
name = "Actual") %>%
add_lines(x = USgas_fc_m12a$date, y = USgas_fc_m12a$yhat,
name = "WMA - Seasonal Lag", line = list(dash = "dash")) %>%
add_lines(x = USgas_fc_m12b$date, y = USgas_fc_m12b$yhat,
name = "WMA - 12 (0.2/0.8)", line = list(dash = "dash")) %>%
layout(title = "Forecasting the Monthly Consumption of Natural Gas in the US",
xaxis = list(title = ""),
yaxis = list(title = "Billion Cubic Feet"))
Como se puede ver en el gráfico anterior, ambos modelos capturaron hasta cierto punto la oscilación estacional de la serie. Establecer el peso total en el desfase estacional es equivalente al modelo Naïve estacional. Esta estrategia podría ser útil para una serie con un patrón estacional dominante, como el gas estadounidense. En el segundo ejemplo, ponderamos el promedio entre el rezago más reciente y el rezago estacional. Tendría sentido distribuir las ponderaciones entre los diferentes rezagos cuando la serie tiene una alta correlación con esos rezagos. Si bien WMA puede capturar el componente estacional de una serie, no puede capturar la tendencia de la serie (debido al efecto promedio). Por lo tanto, este método comenzará a perder efectividad una vez que el horizonte de pronóstico cruce la longitud de la frecuencia de la serie (por ejemplo, más de un año para series mensuales). Más adelante en este capítulo, presentaremos el modelo Holt-Winters, que puede manejar series temporales con componentes tanto estacionales como de tendencia.
#Previsión con suavizado exponencial Entre los modelos tradicionales de pronóstico de series de tiempo, las funciones de suavizado exponencial son uno de los enfoques de pronóstico más populares. Este enfoque, conceptualmente, está cerca del enfoque de promedio móvil que introdujimos anteriormente, ya que ambos se basan en pronosticar los valores futuros de la serie promediando las observaciones pasadas de la serie. La principal distinción entre los enfoques de suavizado exponencial y de promedio móvil es que el primero promedia todas las series de observaciones, a diferencia de un subconjunto de m observaciones del segundo. Además, las funciones avanzadas de suavizado exponencial pueden manejar series con componentes de tendencia y estacionales. En esta sección, nos centraremos en los principales modelos de pronóstico de suavizamiento exponencial: ⚫ Modelo de suavizado exponencial simple • Modelo Holt ⚫ Modelo Holt-Winters
#Modelo de suavizado exponencial simple El suavizamiento exponencial simple (SES), como su nombre lo indica, es el modelo de pronóstico más simple dentro de la familia del suavizamiento exponencial. El supuesto principal de este modelo es que la serie se mantiene al mismo nivel (es decir, la media local de la serie es constante) a lo largo del tiempo y, por lo tanto, este modelo es adecuado para series sin componentes tendenciales ni estacionales. El modelo SES comparte algunos de los atributos del modelo WMA, ya que ambos modelos pronostican los valores futuros de la serie mediante un promedio ponderado de las observaciones pasadas de la serie. # ——– Cambio de código 10 ——–
Por otro lado, la principal distinción entre los dos es que el modelo SES utiliza todas las observaciones anteriores, mientras que el modelo WMA utiliza solo las m observaciones más recientes (para un modelo con una ventana móvil de una longitud de m). El principal atributo del modelo SES es la técnica del promedio ponderado, que se basa en la caída exponencial de los pesos de observación según su distancia cronológica (es decir, índice de serie o marca de tiempo) desde los primeros valores pronosticados. La tasa de caída de los pesos de observación está establecida por a, el parámetro de suavizado del modelo. Además, la función SES es una función escalonada, donde el n valor pronosticado del modelo se convierte en la entrada del próximo pronóstico de las siguientes observaciones, n+1. Podemos formalizar esta relación usando las siguientes ecuaciones: YT+1 = aYT + (1 - a) fr Donde, los siguientes términos se utilizan en la ecuación anterior: Y+1 es el valor previsto de la observación T+1 para una serie con n observaciones (por ejemplo, T = 1,…,n) Yris la observación T de la serie. ⚫a es el parámetro de suavizado del modelo, donde 0 <a≤1 Yr es el valor previsto de la observación T en el paso T-1 Dado que el modelo supone que el nivel del modelo no cambia con el tiempo, podemos generalizar la ecuación anterior para el pronóstico de la observación T+n de la serie (donde n ≥ 1): YT+n=YT+1 = aYT + (1 -α)ÝT Por tanto, el objetivo del modelo es calcular el nivel de la serie en función de los datos de entrada. Esto da como resultado un pronóstico plano. El proceso de identificación del nivel del modelo es relativamente simple y se puede obtener de la ecuación del modelo anterior. Dado que se trata de una ecuación recursiva, podemos ampliarla asignando al valor de YT-1 el valor previsto del período anterior, T-1: Año+1 = aYT + (1 - a) (aYT-1+ (1 − a)YT-1] = aYT + a(1-a)YT-1+ (1 - a)2YT-1)
De la misma manera, podemos seguir ampliando esta ecuación asignando recursivamente las fórmulas de todos los pronósticos de las observaciones anteriores. Este proceso de expansión continúa hasta el primer valor pronosticado de la serie (en orden cronológico) 2: YT+1 = αYT +α(1 − a)YT-1+ a(1 - a) YT-1+…+a(1 − a)7-23 + (1 - a)-1Ŷ2 Cronológicamente, la primera observación que se puede pronosticar es 1⁄2 (dado que no hay observaciones disponibles para pronosticar, la primera observación de la serie). Por lo tanto, la ecuación anterior se puede ampliar al valor previsto de la segunda observación, o de la siguiente manera: Y2 = aYi + (1 - a)Y1 Como no hay datos que respalden el pronóstico 4 (ya que esta es la primera observación disponible de la serie), necesitamos inicializar el valor de . Esto normalmente se hace asignando el valor real de la primera observación (es decir, =) o estimándolo. Podemos simplificar aún más esta ecuación asignando la ecuación de 2 y reorganizando el lado derecho de esta ecuación: YT+1 = aYT + a(1 − a)YT-1 + a(1 - a) Yr-1+…+a(1 − a)-1Y + (1 - a) Esto puede simplificar la ecuación anterior para que ahora tenga el siguiente aspecto: Y+1 = a[(1 − a) YT + (1 - a)1YT-1+ (1 - a)Y-1++(-a)] + (1 - a) Esto se puede comprimir aún más en la siguiente expresión: Año1 = a(1-a)-1Y + (1 - a)Y Se puede observar otra interpretación de la ecuación de pronóstico del modelo SES manipulando la ecuación de pronóstico de la siguiente manera: Año+1 = ayr + (1 - a) = ayr +Yr - aŸr. Como vimos en el capítulo anterior, - denota el término de error del valor ajustado de la observación t. Por lo tanto, el valor previsto de la observación T+1 por el modelo SES no es más que el valor previsto de la observación anterior, Y, y la proporción del término de error de pronóstico en función de a, o como sigue: YT+1=Año+después, donde
er se define como el error de pronóstico para T observaciones de la serie (por ejemplo, la última observación de una serie con T observaciones), o de la siguiente manera: es=YTYT Una forma más común para esta ecuación es la forma componente, que en el caso del modelo SES es la estimación de T+1 y Ŷr para denotar la estimación del modelo del nivel de serie en el tiempo T y T-1, respectivamente. La ecuación anterior se puede reescribir así: YT+1=TaYT + (1 -α)lr-1 El parámetro de suavizado del modelo, a, define la tasa de caída del peso del modelo. Como a está más cerca de 1, las ponderaciones de las observaciones más recientes son mayores. Por otro lado, la velocidad de desintegración, en este caso, es más rápida. Un caso especial es un modelo SES con a = 1, que equivale a un modelo de pronóstico ingenuo: YT+1α=1=YT Esto también es equivalente al valor previsto de n: YT+na=1=YT El siguiente ejemplo demuestra la caída de los pesos de las observaciones en las 15 observaciones más recientes, para valores entre 0,01 y 1:
alpha_df <- data.frame(index = seq(from = 1, to = 15, by = 1),
power = seq(from = 14, to = 0, by = -1))
alpha_df$alpha_0.01 <- 0.01 * (1 - 0.01) ^ alpha_df$power
alpha_df$alpha_0.2 <- 0.2 * (1 - 0.2) ^ alpha_df$power
alpha_df$alpha_0.4 <- 0.4 * (1 - 0.4) ^ alpha_df$power
alpha_df$alpha_0.6 <- 0.6 * (1 - 0.6) ^ alpha_df$power
alpha_df$alpha_0.8 <- 0.8 * (1 - 0.8) ^ alpha_df$power
alpha_df$alpha_1 <- 1 * (1 - 1) ^ alpha_df$power
plot_ly(data = alpha_df) %>%
add_lines(x = ~ index, y = ~ alpha_0.01, name = "alpha = 0.01") %>%
add_lines(x = ~ index, y = ~ alpha_0.2, name = "alpha = 0.2") %>%
add_lines(x = ~ index, y = ~ alpha_0.4, name = "alpha = 0.4") %>%
add_lines(x = ~ index, y = ~ alpha_0.6, name = "alpha = 0.6") %>%
add_lines(x = ~ index, y = ~ alpha_0.8, name = "alpha = 0.8") %>%
add_lines(x = ~ index, y = ~ alpha_1, name = "alpha = 1") %>%
layout(title = "Decay Rate of the SES Weights",
xaxis = list(title = "Index"),
yaxis = list(title = "Weight"))
Podemos transformar las ecuaciones anteriores en componentes, que describen el modelo por sus componentes. En el caso del modelo SES, tenemos un único componente: el nivel del modelo. Como mencionamos anteriormente, el supuesto principal del modelo es que el nivel de la serie permanece igual a lo largo del tiempo y, por lo tanto, podemos reescribir la ecuación del modelo usando las siguientes notaciones: YT+1 = TI Aquí, define el nivel del modelo en el momento T, que es un promedio ponderado de Yr y el nivel del período anterior, r-1: Y+1=lraYT + (1 - a)r-1
#Predicción con la función ses
El paquete de pronóstico proporciona un modelo SES personalizado con la función ses. Los principales argumentos de esta función son los siguientes: inicial: Define el método para inicializar el valor de 1, que se puede calcular utilizando las primeras observaciones de la serie estableciendo el argumento en simple o estimándolo con el modelo ets (una versión avanzada del modelo Holt-Winters de el paquete de pronóstico) al configurarlo en óptimo. alfa: Define el valor del parámetro de suavizado del modelo. Si se establece en NUL, la función lo estimará. ⚫h: Establece el horizonte de pronóstico. Usemos la función ses para pronosticar nuevamente los precios mensuales del café Robusta. Dejaremos los últimos 12 meses de la serie como un conjunto de pruebas para evaluar el rendimiento del modelo. Haremos esto usando la función ts_split del paquete TSstudio:
robusta_par <- ts_split(robusta, sample.out = 12)
train <- robusta_par$train
test <- robusta_par$test
library(forecast)
fc_ses <- ses(train, h = 12, initial = "optimal")
fc_ses$model
## Simple exponential smoothing
##
## Call:
## ses(y = train, h = 12, initial = "optimal")
##
## Smoothing parameters:
## alpha = 0.9999
##
## Initial states:
## l = 0.6957
##
## sigma: 0.161
##
## AIC AICc BIC
## 1989.646 1989.681 2003.252
Como puede ver en el resultado del modelo, la función ses estableció el valor inicial en 0,69, que está bastante cerca del valor de las primeras observaciones (1 = 0,696), y estableció el parámetro alfa en 0,9999 (que técnicamente es bastante cerca del pronóstico NAIVE). Podemos revisar el rendimiento del modelo usando la función test_forecast:
test_forecast(actual = robusta,
forecast.obj = fc_ses,
test = test) %>%
layout(title = "Robusta Coffee Prices Forecast vs. Actual",
xaxis = list(range = c(2010, max(time(robusta)))),
yaxis = list(range = c(1, 3)))
Como podemos ver en el gráfico de pronóstico anterior, la función ses utiliza el conjunto de entrenamiento para identificar el nivel de la serie estimando el parámetro alfa y el nivel inicial del modelo (o 1). El nivel del valor pronosticado está bastante cerca del valor de la última observación de la serie ya que el valor a, en este caso, es cercano a 1. Dado que el objetivo del modelo SES es pronosticar el nivel de la serie, el modelo ganó. No captura ninguna oscilación a corto plazo.
En el caso de un pronóstico plano, los intervalos de confianza del modelo juegan un papel crítico, ya que el nivel de incertidumbre es mayor. Por lo tanto, será útil evaluar si los valores previstos están dentro de los límites del intervalo de confianza del modelo. Usaremos la función plot_forecast del paquete TSstudio para crear un gráfico interactivo para el modelo fs_ses que creamos y trazaremos el conjunto de prueba:
plot_forecast(fc_ses) %>%
add_lines(x = time(test) + deltat(test),
y = as.numeric(test),
name = "Testing Partition") %>%
layout(title = "Robusta Coffee Prices Forecast vs. Actual",
xaxis = list(range = c(2010, max(time(robusta)) +
deltat(robusta))),
yaxis = list(range = c(0, 4)))
#Optimización del modelo con búsqueda de cuadrícula. La función ses optimiza los valores de los parámetros del modelo (ayb) que minimizan la suma de errores cuadrados (SSE) del modelo en el conjunto de entrenamiento. Un enfoque de optimización alternativo es utilizar una búsqueda en cuadrícula. Una búsqueda de cuadrícula es un enfoque simplista pero poderoso que se utiliza para identificar los valores de los parámetros del modelo que minimizan el error del modelo. En el caso del modelo SES, aplicaremos una búsqueda de cuadrícula para identificar el valor óptimo de a que minimice alguna métrica de error del modelo (por ejemplo, MAPE, RMSE, etc.). En el siguiente ejemplo, usaremos una búsqueda de cuadrícula para ajustar los parámetros del modelo a y %, que minimizan el MAPE del modelo para la serie de precios de Robusta. Como vimos en el gráfico de rendimiento anterior del pronóstico de Robusta, existe una brecha entre la estructura de los valores ajustados (marcados en rojo) y el valor pronosticado (marcado en verde), ya que el modelo SES tiene un pronóstico plano. Por lo tanto, dividiremos el modelo en particiones de entrenamiento, prueba y validación, y para cada valor de a, aplicaremos los siguientes pasos: 1. Entrene el modelo SES utilizando la partición de entrenamiento y pronostique las observaciones de la partición de prueba. 2. Evaluar el rendimiento del modelo tanto en la partición de entrenamiento como en la de prueba. 3. Agregar las particiones de entrenamiento y de prueba (en orden cronológico) y volver a entrenar el modelo en la nueva partición (entrenamiento y prueba) antes de pronosticar los valores de la partición de validación. 4. Evaluar el rendimiento del segundo modelo en la partición de validación. Estos pasos nos permitirán seleccionar el valor alfa que minimiza el MAPE en la partición de prueba y usar el conjunto de validación para validar el rendimiento del modelo. Antes de configurar la función, configuremos las particiones de entrenamiento, prueba y validación usando la función ts_split:
robusta_par1 <- ts_split(robusta, sample.out = 24)
train1 <- robusta_par1$train
test1 <- ts_split(robusta_par1$test, sample.out = 12)$train
robusta_par2 <- ts_split(robusta, sample.out = 12)
train2 <- robusta_par2$train
valid <- robusta_par2$test
Usaremos las variables train1 y test1 para entrenar y probar la partición, y train2 para volver a entrenar el modelo y validar los resultados en la partición válida. La siguiente variable alfa define el rango de búsqueda. Asignaremos una secuencia de valores entre 0 y 1 con un incremento de 0.01 usando la función seq: alfa <- seq(de 0, a 1, por = 0,01) Dado que el valor de alfa debe ser mayor que cero, reemplazaremos 0 con un número pequeño bastante cercano a cero:
alpha <- seq(from = 0, to = 1, by = 0.01)
alpha[1] <- 0.001
library(dplyr)
ses_grid <- lapply(alpha, function(i){
md1 <- md_accuracy1 <- md2 <- md_accuracy2 <- results <- NULL
md1 <- ses(train1, h = 12, alpha = i, initial = "simple")
md_accuracy1 <- accuracy(md1, test1)
md2 <- ses(train2, h = 12, alpha = i, initial = "simple")
md_accuracy2 <- accuracy(md2, valid)
resutls <- data.frame(alpha = i,
train = md_accuracy1[9],
test = md_accuracy1[10],
valid = md_accuracy2[10])
}) %>% bind_rows()
Como puede ver en los siguientes resultados de las pruebas, mientras que a = 1 minimiza el MAPE en la partición de entrenamiento, a = 0,03 minimiza la tasa de error en la partición de prueba. Los resultados en la partición de validación siguen el mismo patrón que la partición de prueba, con una puntuación MAPE del 9,98 % en la partición de prueba y del 6,60 % en la partición de validación:
plot_ly(data = ses_grid, x = ~ alpha, y = ~ train,
line = list(color = 'rgb(205, 12, 24)'),
type = "scatter",
mode = "lines",
name = "Training") %>%
add_lines(x = ~ alpha, y = ~ test, line = list(color = "rgb(22, 96, 167)", dash = "dash"), name= "Testing") %>%
add_lines(x = ~ alpha, y = ~ valid, line = list(color = "green", dash = "dot"), name = "Validation") %>%
layout(title = "SES Model Grid Search Results",
yaxis = list(title = "MAPE (%)"))
#Método Holt El método Holt, también conocido como modelo de suavizado doble exponencial, es una versión ampliada del modelo SES. Se basa en estimar el nivel y la tendencia más recientes con el uso de dos parámetros de suavizado, a y 8. Una vez que el modelo estima el nivel y la tendencia más recientes (LT y Tr, respectivamente), los utiliza para construir el pronóstico de la serie usando la siguiente ecuación: ⚫ Para una serie con tendencia aditiva, YT+h = Lr+hTr ⚫ Para una serie con tendencia multiplicativa, YT+1 = Lr x hTr
Aquí, las variables de esas ecuaciones son las siguientes: Yrth es el valor pronosticado del valor pronosticado h de una serie con T observaciones LT es la estimación del nivel de la serie en el tiempo T ⚫ Tr es la estimación del impacto marginal de la tendencia de la serie en el momento T (por ejemplo, el valor agregado de las series avanzadas en una unidad de frecuencia) ⚫h es el número de pasos de pronóstico desde el momento T De manera similar al modelo SES, el cálculo del nivel y la tendencia de la serie mediante el modelo de Holt se basa en un promedio ponderado con el uso de dos parámetros de suavizado, a y 3. Para una serie con una tendencia aditiva, el cálculo de la tendencia más reciente El nivel y la tendencia de la serie se pueden obtener con las siguientes ecuaciones: LT=αYT + a(1-a)(LT-1 + Tr-1) Tr=B(LT-LT-1)+(1-8)TT-1 Las siguientes ecuaciones se pueden utilizar para una serie con tendencia multiplicativa: i LT=αYT + a(1-a) (LT-1 × TT-1) LT Yo = B(-) + (1 - 0)Tr LT-1 Como vimos en los capítulos anteriores, un modelo multiplicativo puede transformarse en un modelo aditivo si aplicamos la transformación logarítmica en ambos lados de la ecuación. El modelo de Holt estima los valores de LT y Tr, el nivel de la serie y la tendencia en el tiempo T, utilizando un promedio ponderado de todas las observaciones de la serie. De manera similar al proceso de estimación del modelo SES, este proceso recursivo comienza con el pronóstico de la segunda observación del modelo: Î2 = aY + a(1-a)(Î1 +Î1) Î2 = B(Î1⁄2 – Î1) + (1 − 8)Î1
Donde, los siguientes términos se utilizan en la ecuación anterior: ⚫ y L2 son el pronóstico del nivel de la primera y segunda observación de la serie, respectivamente ⚫ y 2 son el pronóstico de tendencia de la primera y segunda observación de la serie, respectivamente Como no podemos pronosticar el nivel y el valor de la tendencia para la primera observación de la serie, Tendrá que aproximar Î1 y Î1⁄2 (similar al proceso de aproximación del modelo SES). El pronóstico de las siguientes observaciones en línea (por ejemplo, 3 a 1) se crea recursivamente utilizando la estimación de nivel y tendencia, así como los valores reales de las observaciones anteriores.
#Previsión con la función holt El paquete de pronóstico proporciona una implementación del modelo Holt con la función holt. Esta función inicializa automáticamente los valores de Î1 y Î1, e identifica los valores de a y que minimizan el SSE del modelo en el conjunto de entrenamiento. En el siguiente ejemplo, recuperaremos los datos trimestrales del Producto Interno Bruto (PIB) de EE. UU. de la API de Datos Económicos de la Reserva Federal (FRED) utilizando el paquete Quandl:
library(Quandl)
## Loading required package: xts
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
##
## ######################### Warning from 'xts' package ##########################
## # #
## # The dplyr lag() function breaks how base R's lag() function is supposed to #
## # work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or #
## # source() into this session won't work correctly. #
## # #
## # Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
## # conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop #
## # dplyr from breaking base R's lag() function. #
## # #
## # Code in packages is not affected. It's protected by R's namespace mechanism #
## # Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning. #
## # #
## ###############################################################################
##
## Attaching package: 'xts'
## The following objects are masked from 'package:dplyr':
##
## first, last
gdp <- Quandl("FRED/GDP", start_date = "2010-01-01", type = "ts")
ts_info(gdp)
## The gdp series is a ts object with 1 variable and 48 observations
## Frequency: 4
## Start time: 2010 1
## End time: 2021 4
summary(gdp)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 14765 16577 18379 18622 20698 23992
Notará en el siguiente gráfico que la serie del PIB tiene una fuerte tendencia lineal y no Componente estacional (ya que la serie está desestacionalizada):
ts_plot(gdp,
title = "Gross Domestic Product",
Ytitle = "Billions of Dollars",
Xtitle = "Source: U.S. Bureau of Economic Analysis / fred.stlouisfed.org")
gdp_par <- ts_split(gdp, sample.out = 8)
train <- gdp_par$train
test <- gdp_par$test
fc_holt <- holt(train, h = 8, initial = "optimal")
fc_holt$model
## Holt's method
##
## Call:
## holt(y = train, h = 8, initial = "optimal")
##
## Smoothing parameters:
## alpha = 0.9985
## beta = 0.0941
##
## Initial states:
## l = 14603.0487
## b = 161.9411
##
## sigma: 80.9869
##
## AIC AICc BIC
## 504.8838 506.6485 513.3282
Los valores inicializados de 1 y 1 de la función son relativamente cercanos a los valores de la primera observación de la serie (Y1 = 14721,35) y a la diferencia promedio entre cada trimestre. Además, el a seleccionado de 0,74 indicó que el modelo pesaba mucho en la última observación de la serie, YT. Por otro lado, el valor de es bastante cercano a cero, lo que indica que la actualización del valor de la tendencia de un período a otro no tiene en cuenta el cambio en el nivel y traslada el valor inicial de la tendencia, fi, hacia adelante. Comparemos el rendimiento del modelo en las particiones de entrenamiento y prueba con la función de precisión:
accuracy(fc_holt, test)
## ME RMSE MAE MPE MAPE MASE
## Training set 10.99164 76.83093 61.7626 0.05241543 0.3460477 0.08790283
## Test set -665.87355 1141.19097 854.8069 -3.29699460 4.0874667 1.21659297
## ACF1 Theil's U
## Training set 0.0192117 NA
## Test set 0.2975704 1.08055
Como puede ver en el resultado de la función de precisión, la relación entre la tasa de error en el conjunto de prueba y entrenamiento es más de 5 veces para el RMSE y 4,5 para el MAPE. Esta gran proporción en las métricas de error se deriva principalmente de las dos razones siguientes: • Los valores ajustados del modelo en el conjunto de entrenamiento no están limitados por una línea lineal (a diferencia del resultado del pronóstico). ⚫ El crecimiento de la tendencia en los últimos trimestres pasa de una tasa de crecimiento lineal a una tasa exponencial Los cambios en la tendencia de crecimiento y el pronóstico se pueden observar con la función test_forecast:
test_forecast(gdp, forecast.obj = fc_holt, test = test)
Si bien el modelo de Holt fue diseñado para manejar series temporales con tendencia lineal, el argumento exponencial de la función Holt brinda la opción de manejar series con tendencias exponenciales o decrecientes cuando se establece en VERDADERO. Para el ejemplo anterior, podemos utilizar el argumento exponencial para modificar el patrón de crecimiento de la tendencia. En este caso, nos gustaría tener una ponderación más alta para la tendencia y la estableceremos en 0,75 (un enfoque más sólido para identificar el valor óptimo de 8 sería utilizar una búsqueda en cuadrícula):
fc_holt_exp <- holt(train,
h = 8,
beta = 0.75 ,
initial = "optimal",
exponential = TRUE)
fc_holt_exp$model
## Holt's method with exponential trend
##
## Call:
## holt(y = train, h = 8, initial = "optimal", exponential = TRUE,
##
## Call:
## beta = 0.75)
##
## Smoothing parameters:
## alpha = 0.781
## beta = 0.75
##
## Initial states:
## l = 14572.2291
## b = 1.0142
##
## sigma: 0.0053
##
## AIC AICc BIC
## 515.9291 517.0720 522.6847
accuracy(fc_holt_exp, test)
## ME RMSE MAE MPE MAPE MASE
## Training set -2.792636 90.20753 74.14323 -0.01756593 0.4165413 0.1055234
## Test set -703.014806 1151.21262 858.97402 -3.46212317 4.1130726 1.2225238
## ACF1 Theil's U
## Training set -0.1541725 NA
## Test set 0.2834645 1.090637
De manera similar, podemos trazar los valores ajustados y pronosticados frente a los datos reales con las funciones test_forecast:
test_forecast(gdp, forecast.obj = fc_holt, test = test)
Como puede ver, la tasa de error del segundo modelo de Holt está más equilibrada, donde la relación entre el error en el conjunto de prueba y entrenamiento es 2,5 y 2,1 para las métricas RMSE y MAPE, respectivamente. i El uso de argumentos exponenciales o amortiguados requería algún conocimiento o suposición previa sobre la tasa de crecimiento futura de la tendencia.
#Modelo Holt-Winters
Cerraremos este capítulo con el tercer y más avanzado modelo de la familia de modelos de pronóstico de suavizamiento exponencial: el modelo de Holt-Winters. El modelo Holt-Winters (HW) es una versión ampliada del modelo Holt y puede manejar datos de series de tiempo con componentes estacionales y de tendencia. Pronosticar el componente estacional requirió un tercer parámetro y ecuación más suaves, además de los del nivel y la tendencia.
Tanto los componentes de tendencia como los estacionales podrían tener una estructura aditiva o de multiplicidad, lo que añade cierta complejidad al modelo, ya que existen múltiples combinaciones posibles: • Tendencia aditiva y componentes estacionales • Tendencia aditiva y componentes estacionales multiplicativos • Tendencia multiplicativa y componentes estacionales aditivos • Tendencia multiplicativa y componentes estacionales Por lo tanto, antes de construir un modelo HW, necesitamos identificar la estructura de la tendencia y los componentes estacionales. Las siguientes ecuaciones describen el modelo HW para una serie con componente estacional aditivo: YT+1=LT+hTT + ST+h-m LT = a(YT - ST-m) + (1 - a) (LT-1 + TT-1) Tr = B(Lr - Lr-1)+(1–8)+ (1 – 3)Tru STY(YT-LT)+(1-7)ST-m Las siguientes ecuaciones describen el modelo HW para una serie con un factor estacional multiplicativo. estructura: YT+1(LT + KTT) ST+k-m ayt LT= +(1-a)(LT-1+ Tr-1) ST-m Tr=B(LT-LT-1)+(1-8)TT-1 año STY +(1-7)ST-m LT La implementación más común del modelo HW en R son las funciones Holt Winters y hw de los paquetes de estadísticas y pronósticos. La principal diferencia entre las dos funciones es que la función hw puede manejar series de tiempo con una tendencia exponencial o amortiguada (similar al modelo de Holt). En el siguiente ejemplo, utilizaremos la función HoltWinters para pronosticar los últimos 12 meses de la serie USgas.
Usemos la función de descomposición para diagnosticar la estructura de los componentes tendencial y estacional de la serie:
data(USgas)
decompose(USgas) %>% plot()
Podemos observar en el gráfico anterior que tanto el componente tendencial como el estacional de la serie tienen una estructura aditiva. Como hicimos anteriormente, crearemos capacitación y probando particiones utilizando los últimos 12 meses de la serie para evaluar el rendimiento del modelo:
USgas_par <- ts_split(USgas, 12)
train <- USgas_par$train
test <- USgas_par$test
md_hw <- HoltWinters(train)
md_hw
## Holt-Winters exponential smoothing with trend and additive seasonal component.
##
## Call:
## HoltWinters(x = train)
##
## Smoothing parameters:
## alpha: 0.371213
## beta : 0
## gamma: 0.4422456
##
## Coefficients:
## [,1]
## a 2491.9930104
## b -0.1287005
## s1 32.0972651
## s2 597.1088003
## s3 834.9628439
## s4 353.8593860
## s5 318.1927058
## s6 -173.0721496
## s7 -346.6229990
## s8 -329.7169608
## s9 -112.1664217
## s10 -140.3186476
## s11 -324.5343787
## s12 -243.9334551
Notará en el resultado del modelo anterior que el modelo aprende principalmente el nivel y la actualización estacional (con a = 0,35 y 7 = 0,37). Por otro lado, no se puede aprender del valor inicializado de la tendencia = 0. El siguiente paso es pronosticar los próximos 12 meses (o los valores del conjunto de prueba) y evaluar el rendimiento del modelo con las funciones precision y test_forecast:
fc_hw <- forecast(md_hw, h = 12)
accuracy(fc_hw, test)
## ME RMSE MAE MPE MAPE MASE
## Training set 7.828361 115.2062 87.67420 0.2991952 4.248131 0.7614222
## Test set 51.013877 115.1555 98.06531 1.7994297 3.766099 0.8516656
## ACF1 Theil's U
## Training set 0.21911103 NA
## Test set -0.01991923 0.3652142
Las métricas de precisión del modelo están bastante equilibradas, con un MAPE del 4,3% en el conjunto de entrenamiento y del 7% en el conjunto de prueba. En el gráfico del desempeño del siguiente modelo, notará que la mayoría de los errores de pronóstico están relacionados con el pico estacional y las últimas observaciones de la serie, que el modelo estaba subestimando:
test_forecast(actual = USgas,
forecast.obj = fc_hw,
test = test)
Trazar los valores ajustados y pronosticados nos proporciona una mejor comprensión del rendimiento del modelo. Como podemos ver en el gráfico anterior, el modelo HW está haciendo un buen trabajo al capturar los patrones estacionales de ambas series. Por otro lado, al modelo le falta en la mayoría de los casos el pico del año durante el mes de enero. Alternativamente, el modelo se puede entrenar con una búsqueda en cuadrícula de manera similar a lo que hicimos con el modelo SES. En este caso, hay tres parámetros a optimizar: a, y. El paquete TSstudio proporciona una función de búsqueda de cuadrícula personalizada basada en el enfoque de backtesting para entrenar una función de HoltWinters.
Una búsqueda de cuadrícula es un enfoque de optimización genérico para ajustar modelos con múltiples parámetros de ajuste, como ajustar los parámetros a, y del modelo HW. Este algoritmo simple se basa en establecer (cuando corresponda) un conjunto de valores para cada parámetro del modelo y luego iterar y probar el modelo con una combinación diferente de los valores de los parámetros del modelo. Según la métrica de error seleccionada, la combinación que minimice los criterios de error se utilizará con el modelo final. Generalmente, la principal advertencia de este enfoque es que su cálculo podría resultar costoso a medida que aumenta el número de combinaciones de búsqueda. En el Capítulo 12, Pronósticos con modelos de aprendizaje automático, veremos un algoritmo de búsqueda de cuadrícula más sólido con el paquete h20.
Por razones de eficiencia, comenzaremos con una búsqueda superficial que incluya incrementos mayores en los valores de los parámetros. Esto nos ayudará a limitar el área de búsqueda y luego aplicar una búsqueda más profunda en esas áreas. La búsqueda superficial incluirá pruebas retrospectivas en 6 períodos diferentes utilizando una secuencia entre 0 y 1 con un incremento de 0,1:
{r}# shallow_grid <- ts_grid(train, model = "HoltWinters", periods = 6, window_space = 6, window_test = 12, hyper_params = list(alpha = seq(0,1,0.1), beta = seq(0,1,0.1), gamma = seq(0,1,0.1)), parallel = TRUE, n.cores = 8)
```{r}# library(parallel) library(forecast)
shallow_grid <- ts_grid(train, model = “HoltWinters”, periods = 6, window_space = 6, window_test = 12, hyper_params = list(alpha = seq(0,1,0.1), beta = seq(0,1,0.1), gamma = seq(0,1,0.1)), parallel = FALSE)
apply_function <- function(n) { # Tu código para cada elemento aquí }
batch_size <- 2 # ajusta el tamaño del lote según tus necesidades batches <- split(1:shallow_grid\(periods, ceiling(seq_along(1:shallow_grid\)periods)/batch_size))
shallow_grid <- lapply(batches, function(batch) { parallel::mclapply(batch, function(n) { apply_function(n) }, mc.cores = 8) })
shallow_grid <- do.call(c, shallow_grid)
# -------- Codigo 33 --------
El resultado de la siguiente cuadrícula proporciona cualquier combinación de a, y con la tasa de error en cada período de prueba y su media general. La tabla ordena la media general del modelo desde la mejor combinación hasta la peor:
```{r}#
shallow_grid$grid_df[1:10,]
La cuadrícula de trazado proporciona una vista intuitiva del rango
óptimo de valores de cada parámetro mediante el uso de un gráfico de
paracords. De forma predeterminada, la función resalta el 10% de los
mejores modelos: {r}# plot_grid(shallow_grid)
Notará en el resultado del gráfico de búsqueda que el rango óptimo de a varía entre 0,1 y 0,5, está entre 0 y 0,1 e Y está entre 0,2 y 0,4. Esto nos ayudará a establecer el rango de búsqueda de hiperparámetros para la búsqueda de cuadrícula más profunda al reducir el rango de búsqueda pero usando una búsqueda más granular:
Como puede ver en el siguiente gráfico, el rango de error de los
modelos del 10% superior ha disminuido con respecto al de la búsqueda
superficial:
{r}# deep_grid <- ts_grid(train, model = "HoltWinters", periods = 6, window_space = 6, window_test = 12, hyper_params = list(alpha = seq(0.1,0.5,0.01), beta = seq(0,0.1,0.01), gamma = seq(0.2,0.4,0.01)), parallel = TRUE, n.cores = 8)
{r}# plot_grid(deep_grid)
Este gráfico se puede ver en una vista 3D (al realizar una búsqueda por tres parámetros):
El resultado se muestra en la siguiente captura de pantalla:
{r}# plot_grid(deep_grid, type = "3D", top = 250)
El último paso de este proceso es extraer los valores de los parámetros de suavizado óptimos del modelo de cuadrícula según la búsqueda, volver a entrenar el modelo HW y utilizarlo para pronosticar los valores futuros de la serie:
```{r}# md_hw_grid <- HoltWinters(train, alpha = deep_grid\(alpha, beta = deep_grid\)beta, gamma = deep_grid$gamma)
fc_hw_grid <- forecast(md_hw_grid, h = 12)
accuracy(fc_hw_grid, test)
test_forecast(actual = USgas, forecast.obj = fc_hw_grid, test = test)
Como puede ver en el resultado de las funciones precision y test_forecast, la optimización del modelo con búsqueda de cuadrícula proporciona, en este caso, un aumento en el rendimiento del modelo al reducir la puntuación MAPE del 6,95 % al 5,92 %.
#Sección - 11
# Capítulo 11 Código
#Predicción con modelos ARIMA
El modelo de media móvil integrada autorregresiva (ARIMA) es el nombre genérico de una familia de modelos de pronóstico que se basan en los procesos autorregresivo (AR) y media móvil (MA). Entre los modelos de pronóstico tradicionales (por ejemplo, regresión lineal, suavizado exponencial, etc.), el modelo ARIMA se considera el enfoque más avanzado y sólido. En este capítulo presentaremos el modelo.
componentes los procesos AR y MA y el componente de diferenciación. Además, nos centraremos en métodos y enfoques para ajustar los parámetros del modelo con el uso de diferenciación, la función de autocorrelación (ACF) y la función de autocorrelación parcial (PACF).
En este capítulo, cubriremos los siguientes temas:
⚫ El estado estacionario de los datos de series temporales.
⚫ El proceso de caminata aleatoria
• Los procesos AR y MA
• Los modelos ARMA y ARIMA
• El modelo ARIMA estacional
• Regresión lineal con el modelo de errores ARIMA
Requerimiento técnico
Los siguientes paquetes se utilizarán en este capítulo:
⚫ pronóstico: versión 8.5 y superior
⚫TSstudio: Versión 0.1.4 y superior
⚫ trama: Versión 4.8 y superior
⚫ dplyr: Versión 0.8.1 y superior
⚫ lubricar: versión 1.7.4 y superior
⚫ estadísticas: versión 3.6.0 y superior
⚫ conjuntos de datos: versión 3.6.0 y superior
⚫ base: Versión 3.6.0 y superior
# -------- Código 1 --------
#El proceso estacionario
Uno de los principales supuestos de la familia de modelos ARIMA es que la serie de entrada sigue la estructura del proceso estacionario. Esta suposición se basa en el teorema de representación de Wold, que establece que cualquier proceso estacionario puede representarse como una combinación lineal de ruido blanco. Por lo tanto, antes de profundizar en los componentes del modelo ARIMA, hagamos una pausa y hablemos del proceso estacionario. El proceso estacionario, en el contexto de datos de series de tiempo, describe un estado estocástico de la serie. Los datos de series de tiempo son estacionarios si se dan las siguientes condiciones:
⚫ La media y la varianza de la serie no cambian con el tiempo.
⚫ La estructura de correlación de la serie, junto con sus rezagos, se mantiene igual a lo largo del tiempo.
En los siguientes ejemplos, utilizaremos la función arima.sim del paquete de estadísticas para simular datos de una serie de tiempo estacionaria y no estacionaria y los trazaremos con la función ts_plot del paquete TSstudio. La función arima.sim nos permite simular datos de series temporales basadas en los componentes y características principales del modelo ARIMA:
• Un proceso Autoregresivo (AR): Establecer una relación entre la serie y sus p lags pasados con el uso de un modelo de regresión (entre la serie y sus p lags)
• Un proceso de Media Móvil (MA): Similar al proceso AR, el proceso MA establece la relación con el término de error en el momento t y los términos de error pasados, con el uso de regresión entre los dos componentes (error en el momento t y el términos de error pasados)
• Proceso integrado (I): El proceso de diferenciar la serie con sus retrasos d para transformar la serie en un estado estacionario.
Aquí, el argumento del modelo de la función define p, q y d, así como el orden de los procesos AR, MA e I del modelo. Por ahora, no se preocupe si no está familiarizado con esta función; la analizaremos en detalle más adelante en este capítulo.
i
La función arima.sim tiene un componente aleatorio. Por lo tanto, para poder reproducir los ejemplos a lo largo de este capítulo, usaremos la función set.seed. La función set.seed le permite crear números aleatorios de manera reproducible en R configurando el valor de semilla de generación aleatoria.
Por ejemplo, en el siguiente ejemplo, simularemos un proceso AR con un retraso (es decir, p = 1) y 500 observaciones con la función arima.sim. Antes de ejecutar la simulación, estableceremos el valor inicial en 12345:
```r
set.seed(12345)
stationary_ts <- arima.sim(model = list(order = c(1,0,0),
ar = 0.5 ),
n = 500)
Ahora tracemos la serie temporal simulada con la función ts_Plot:
library(TSstudio)
ts_plot(stationary_ts,
title = "Stationary Time Series",
Ytitle = "Value",
Xtitle = "Index")
En este caso, se puede ver que, en general, la media de la serie, a lo largo del tiempo, se mantiene alrededor de la línea cero. Además, la variación de la serie no cambia con el tiempo. Utilicemos el Función arima.sim para crear un ejemplo para series no estacionarias:
set.seed(12345)
non_stationary_ts <- arima.sim(model = list(order = c(1,1,0),ar = 0.3 ),n = 500)
ts_plot(non_stationary_ts,
title = "Non-Stationary Time Series",
Ytitle = "Value",
Xtitle = "Index")
Por otro lado, el segundo ejemplo viola la condición estacionaria ya que tiene una tendencia en el tiempo, lo que significa que está cambiando con el tiempo. Consideraríamos que los datos de una serie temporal no son estacionarios siempre que esas condiciones no se cumplan. Ejemplos comunes de una serie con una estructura no estacionaria son los siguientes: • Una serie con una tendencia dominante: la media de la serie cambia a lo largo del tiempo en función del cambio en la tendencia de la serie y, por lo tanto, la serie no es estacionaria. • Una serie con un componente estacional multiplicativo: en este caso, la varianza de la serie es función de la oscilación estacional en el tiempo, que aumenta o disminuye con el tiempo.
La clásica serie AirPassenger (el número mensual de pasajeros de aerolíneas entre 1949 y 1960) del paquete de conjuntos de datos es un buen ejemplo de una serie que viola las dos condiciones del proceso estacionario. Dado que la serie tiene una fuerte tendencia lineal y un componente estacional multiplicativo, tanto la media como la varianza cambian con el tiempo:
data(AirPassengers)
ts_plot(AirPassengers,
title = "Monthly Airline Passenger Numbers 1949-1960",
Ytitle = "Thousands of Passengers",
Xtitle = "Year")
#Transformar una serie no estacionaria en una serie estacionaria
En la mayoría de los casos, a menos que tenga mucha suerte, sus datos sin procesar probablemente tendrán una tendencia u otra forma de oscilación que viola los supuestos del proceso estacionario. Por lo tanto, para manejar esto, tendrás que aplicar algunos pasos de transformación para llevar la serie a un estado estacionario. Los métodos de transformación comunes son diferenciar la serie (o eliminar la tendencia) y la transformación logarítmica (o ambas). Repasemos las aplicaciones de estos métodos.
#Diferenciación de series temporales El enfoque más común para transformar datos de una serie temporal no estacionaria en un estado estacionario es diferenciar la serie con sus rezagos. El principal efecto de diferenciar una serie es la eliminación de la tendencia de la serie (o la eliminación de la tendencia de la serie), lo que ayuda a estabilizar la media de la serie. Medimos el grado u orden de la serie diferenciando por el número de veces que diferenciamos la serie con sus rezagos. Por ejemplo, la siguiente ecuación define la diferencia de primer orden: Y’ = Y1 - Y1-1 Aquí, representa la diferencia de primer orden de la serie, e Y e Y-1 representan la serie misma y su primer rezago. En algunos casos, el uso de la diferencia de primer orden no es suficiente para llevar la serie a un estado estacionario y es posible que desee aplicar la diferenciación de segundo orden: Y Y Y = (Y-Y-1)-(Y-1-Y-2)=Y-2Y-1+Y-2 Otra forma de diferenciación es la diferenciación estacional, que se basa en diferenciar la serie con el rezago estacional: Y = Y1 - Y1-1 Aquí, f representa la frecuencia de la serie y - representa el desfase estacional de la serie. Es común utilizar la diferenciación estacional cuando una serie tiene un componente estacional. La función diff del paquete base diferencia la serie de entrada con un retraso específico estableciendo el argumento de retraso de la función en el retraso relevante. Volvamos a la serie AirPassenger y veamos cómo el primer orden y la diferenciación estacional afectan la estructura de la serie. Comenzaremos con la diferencia de primer orden:
ts_plot(diff(AirPassengers, lag = 1),
title = "AirPassengers Series - First Differencing",
Xtitle = "Year",
Ytitle = "Differencing of Thousands of Passengers")
Puede ver que la primera diferencia de la serie AirPassenger eliminó la tendencia de la serie y que la media de la serie es, en general, constante a lo largo del tiempo. Por otro lado, existe evidencia clara de que la variación de la serie aumenta con el tiempo y, por lo tanto, la serie aún no es estacionaria. Además de la diferencia de primer orden, tomar la diferencia estacional de la serie podría resolver este problema. Sumemos la diferencia estacional a la diferencia de primer orden y grafiquemos nuevamente:
ts_plot(diff(diff(AirPassengers, lag = 1), 12),
title = "AirPassengers Series - First and Seasonal Differencing",
Xtitle = "Year",
Ytitle = "Differencing of Thousands of Passengers")
La diferencia estacional hizo un buen trabajo al estabilizar la variación de la serie, ya que ahora la serie parece estacionaria. # ——– Codigo 7 ——–
#Transformación de registro En el Capítulo 5, Descomposición de datos de series temporales, vimos las aplicaciones de la transformación logarítmica para manejar datos de series temporales multiplicativas. Asimismo, podemos utilizar este enfoque para estabilizar una oscilación estacional multiplicativa, si existe. Este enfoque no sustituye a la diferenciación, sino que es un complemento. Por ejemplo, en el ejemplo del AirPassenger en la sección anterior, vimos que la primera diferenciación está haciendo un gran trabajo al estabilizar la media de la serie, pero no es suficiente para estabilizar la varianza de la serie. Por lo tanto, podemos aplicar una transformación logarítmica para transformar la estructura estacional de multiplicable a aditiva y luego aplicar la diferencia de primer orden para estacionariar la serie:
ts_plot(diff(log(AirPassengers), lag = 1),
title = "AirPassengers Series - First Differencing with Log Transformation",
Xtitle = "Year",
Ytitle = "Differencing/Log of Thousands of Passengers")
La transformación logarítmica con diferenciación de primer orden está haciendo un mejor trabajo al transformar la serie en un estado estacionario con respecto al enfoque de doble diferenciación (primer orden con diferenciación estacional) que utilizamos anteriormente.
#El proceso de caminata aleatoria El paseo aleatorio, en el contexto de series de tiempo, describe un proceso estocástico de un objeto a lo largo del tiempo, donde las principales características de este proceso son las siguientes: • El punto de partida de este proceso en el momento 0 - Yo se conoce • El movimiento (o la caminata) de la serie con proceso de caminata aleatoria desde el tiempo t-1 hasta el tiempo t se definen con la siguiente ecuación: Y1 = Y1-1+ y
Aquí, Y-1 e Yt representan el valor de la serie en el tiempo t-1 y t, respectivamente, y t representa un número aleatorio (o un ruido blanco) con una media de 0 y una varianza de o. Mientras que el proceso de paseo aleatorio no es estacionario, la primera diferencia de un paseo aleatorio representa un proceso estacionario como el siguiente: Y = YtYt-1=et
Como mencionamos anteriormente, Et tiene una media y una varianza constantes y, por lo tanto, Y es un proceso estacionario. Un paseo aleatorio se utiliza habitualmente para simular posibles trayectorias futuras de una serie. Por ejemplo, podemos simular un paseo aleatorio con la función arima.sim estableciendo el parámetro d del argumento de orden en 1. Esto es equivalente a una serie no estacionaria con una estructura de primeras diferencias. El siguiente código demuestra la simulación de 20 caminos aleatorios diferentes de 500 pasos, todos comenzando en el punto 0 en el tiempo 0. Crearemos dos gráficos: uno para los caminos aleatorios y otro para su diferencia de primer orden. Usaremos el paquete plotly para hacer esto:
library(plotly)
set.seed(12345)
p1 <- plot_ly()
p2 <- plot_ly()
for(i in 1:20){
rm <- NULL
rw <- arima.sim(model = list(order = c(0, 1, 0)), n = 500)
p1 <- p1 %>% add_lines(x = time(rw), y = as.numeric(rw))
p2 <- p2 %>% add_lines(x = time(diff(rw)), y = as.numeric(diff(rw)))
}
Aquí p1 representa la gráfica de la simulación de paseo aleatorio:
p1 %>% layout(title = "Simulate Random Walk",
yaxis = list(title = "Value"),
xaxis = list(title = "Index")) %>%
hide_legend()
Aquí p2, representa la gráfica correspondiente de la diferenciación de primer orden de la simulación de paseo aleatorio:
p2 %>% layout(title = "Simulate Random Walk with First-Order Differencing",
yaxis = list(title = "Value"),
xaxis = list(title = "Index")) %>%
hide_legend()
Descripción de la imagen
Descripción de la imagen
set.seed(12345)
ar2 <- arima.sim(model = list(order = c(2,0,0),
ar = c(0.9, -0.3)),
n = 500)
Repasemos la serie temporal simulada:
ts_plot(ar2,
title = "Simulate AR(2) Series",
Ytitle = "Value",
Xtitle = "Index")
La función ar del paquete de estadísticas nos permite ajustar un modelo AR a datos de series de tiempo y luego pronosticar sus valores futuros. Esta función identifica la orden AR automáticamente según el Criterio de información de Akaike (AIC). El argumento del método le permite definir el método de estimación de coeficientes, como los mínimos cuadrados ordinarios (MCO) (que vimos en el Capítulo 9, Pronósticos con regresión lineal), la estimación de máxima verosimilitud (MLE) y Yule-Walker (predeterminado). Apliquemos la función ar para identificar el orden AR y estimemos sus coeficientes en consecuencia:
md_ar <- ar(ar2)
Repasemos los detalles del modelo ajustado:
md_ar
##
## Call:
## ar(x = ar2)
##
## Coefficients:
## 1 2
## 0.8851 -0.2900
##
## Order selected 2 sigma^2 estimated as 1.049
#Identificando el proceso de RA y sus características
En el ejemplo anterior, simulamos una serie AR(2) y quedó claro que necesitamos aplicar un modelo AR a los datos. Sin embargo, cuando trabaje con datos de series en tiempo real, deberá identificar la estructura de la serie antes de modelarla. En el mundo de la familia de modelos ARIMA no estacionales, una serie podría tener una de las siguientes estructuras: • RA • MA • Caminata aleatoria • Una combinación de los tres anteriores (por ejemplo, procesos AR y MA) En esta sección, presentaremos el método para identificar el primer caso, es decir, una serie con solo una estructura AR. En las siguientes secciones generalizaremos este método al resto de casos (por ejemplo, MA, AR, MA, etc.).
Identificar la estructura de la serie incluye los dos pasos siguientes: ⚫ Categorizar el tipo de proceso (por ejemplo, AR, MA, etc.) • Una vez que hayamos clasificado el tipo de proceso, necesitamos identificar el orden del proceso (por ejemplo, AR(1), AR(2), etc.) La utilización de la función de autocorrelación (ACF) y la función de autocorrelación parcial (PACF), que presentamos en el Capítulo 7, Análisis de correlación, nos permite clasificar el tipo de proceso e identificar su orden. Si la salida del ACF disminuye y la salida del PACF disminuye en el retraso p, esto indica que la serie es un proceso AR(p). Calculemos y tracemos el ACF y PACF para la serie AR(2) simulada que creamos anteriormente con las funciones ACF y PACF. Primero, usaremos la función par para trazar los dos gráficos uno al lado del otro estableciendo el argumento mfrow en c(1, 2) (una fila, dos columnas):
par(mfrow=c(1,2))
Ahora generaremos los gráficos con las funciones acf y pacf:
acf(ar2)
pacf(ar2)
Descripción de la imagen
De manera similar, dado que simulamos la serie AR(2) en la sección anterior, utilizaremos la función arima.sim para simular una serie con una estructura MA(2). En este caso, estableceremos el parámetro q en el argumento de orden en 2 y estableceremos los coeficientes MA en 01 = 0,5 y 02 = -0,3.
set.seed(12345)
ma2 <- arima.sim(model = list(order = c(0, 0, 2),
ma = c(0.5, -0.3)),
n = 500)
Usaremos la función ts_plot para trazar la serie simulada:
ts_plot(ma2,
title = "Simulate MA(2) Series",
Ytitle = "Value",
Xtitle = "Index")
El modelado del proceso MA se puede realizar con la función arima del paquete de estadísticas. Esta función, al establecer el orden de AR y los componentes diferenciadores del modelo en 0 con el argumento de orden (es decir, p = 0 y d = 0), modela solo el componente MA. Por ejemplo, apliquemos un modelo MA de segundo orden con la función arima en la serie MA(2) simulada:
De manera similar a la función ar, puede seleccionar el enfoque de estimación de coeficientes. En este caso, existen tres métodos: máxima verosimilitud (ML), minimizar suma de cuadrados condicional (CSS) y la combinación de ambos, que se conoce como CSS-ML. La salida de la función arima es más detallada que la de la función ar, ya que también proporciona el nivel de significancia de cada coeficiente (el se):
md_ma <- arima(ma2, order = c(0,0,2), method = "ML")
md_ma
##
## Call:
## arima(x = ma2, order = c(0, 0, 2), method = "ML")
##
## Coefficients:
## ma1 ma2 intercept
## 0.530 -0.3454 0.0875
## s.e. 0.041 0.0406 0.0525
##
## sigma^2 estimated as 0.9829: log likelihood = -705.81, aic = 1419.62
#Identificando el proceso de MA y sus características De manera similar al proceso AR, podemos identificar un proceso MA y su orden con las funciones ACF y PACF. Si el ACF se corta en el retraso 9 y la función PACF disminuye, podemos concluir que el proceso es un MA(q). Repitamos el proceso que aplicamos en la serie ar2 con la serie ma2:
par(mfrow=c(1,2))
acf(ma2)
pacf(ma2)
Descripción de la imagen
Descripción de la imagen
set.seed(12345)
arma <- arima.sim(model = list(order(1,0,2), ar = c(0.7), ma = c(0.5,-0.3)), n = 500)
Tracemos y revisemos la estructura de la serie con la función ts_plot:
ts_plot(arma,
title = "Simulate ARMA(1,2) Series",
Ytitle = "Value",
Xtitle = "Index")
Instalar un modelo ARMA es sencillo con la función arima. En este caso, tenemos que configurar los parámetros p y q en el argumento de orden:
arma_md <- arima(arma, order = c(1,0,2))
Puede observar en el siguiente resultado del modelo ajustado que los valores de los coeficientes del modelo son bastante cercanos al que simulamos:
arma_md
##
## Call:
## arima(x = arma, order = c(1, 0, 2))
##
## Coefficients:
## ar1 ma1 ma2 intercept
## 0.7439 0.4785 -0.3954 0.2853
## s.e. 0.0657 0.0878 0.0849 0.1891
##
## sigma^2 estimated as 1.01: log likelihood = -713.18, aic = 1436.36
#Identificando un proceso ARMA
La identificación del proceso ARMA sigue el mismo enfoque que utilizamos anteriormente con los procesos AR y MA. Existe un proceso ARMA en datos de series de tiempo si los gráficos ACF y PACF disminuyen, como podemos ver en el siguiente ejemplo:
par(mfrow=c(1,2))
acf(arma)
pacf(arma)
Por otro lado, a diferencia de los procesos AR y MA, no podemos concluir el orden del proceso ARMA. Existen varios enfoques para ajustar los parámetros ARMA p y q: • Ajuste manual: comenzando con una combinación de p y q y utilizando algunos criterios de error para identificar los parámetros del modelo. • Búsqueda de cuadrícula: probando diferentes combinaciones de los parámetros p y q según la matriz de cuadrícula. Asimismo, el ajuste manual y la selección de una combinación específica de parámetros p y q deben basarse en criterios de error. • Búsqueda basada en algoritmos: mediante el uso de una función o algoritmo para ajustar los parámetros del modelo.
Más adelante en este capítulo, presentaremos el automóvil. Función arima del paquete de pronóstico, que es uno de los algoritmos más populares para la automatización de la familia de modelos ARIMA, incluido el modelo ARMA.
#Sintonización manual del modelo ARMA El ajuste manual del modelo ARMA se basa principalmente en la experimentación, la intuición, el sentido común y la experiencia. El proceso de sintonización se basa en los siguientes pasos: 1. Establezca algunos valores iniciales para p y q. Normalmente, se recomienda comenzar con los valores mínimos de pyq (por ejemplo, p= 1 y q = 1). 2. Evaluar el ajuste del modelo con base en algún criterio de error. Los criterios de error más comunes son el Criterio de Información de Akaike (AIC) o el Criterio Bayesiano. Criterio de información (BIC). 3. Ajuste los valores de pyq. 4. Evaluar el cambio en la métrica de error. 5. Repita los dos últimos pasos hasta que no pueda lograr mejoras adicionales en la métrica de error. Las principales razones para iniciar la sintonización con valores mínimos de p y q están relacionadas con las siguientes razones: ⚫ Costo: dado que el orden del modelo es mayor, el costo del modelo también es mayor y el grado de libertad del modelo se reduce • Complejidad: aumenta a medida que aumenta el orden del modelo, lo que puede resultar en un sobreajuste. AIC y BIC son los más apropiados para usar en este caso ya que estos dos métodos penalizan los modelos con orden superior. Esto se puede ver en las siguientes fórmulas: AIC=2k-2ln(L), y BIC = ln(n)k - 2ln(L) Los siguientes son los términos utilizados en las ecuaciones anteriores: ⚫ k representa el número de parámetros del modelo, o p+q ⚫ Î representa el valor máximo de la función de verosimilitud ⚫n es el número de observaciones de entrada
Cuanto menor sea la puntuación AIC o BIC, mejor se ajustará el modelo. Puede observar que la penalización de la métrica BIC es mayor con respecto a AIC siempre que en(n) > 2, o n> e2. Usemos la serie ARMA simulada que creamos anteriormente para aplicar este enfoque de ajuste. Por razones de simplicidad, no incluiremos una intersección y restringiremos el valor máximo de k, es decir, el orden del modelo (o p+q), a cuatro. Primero probaremos el modelo ARMA(1,1):
arima(arma, order = c(1, 0, 1), include.mean = FALSE)
##
## Call:
## arima(x = arma, order = c(1, 0, 1), include.mean = FALSE)
##
## Coefficients:
## ar1 ma1
## 0.4144 0.8864
## s.e. 0.0432 0.0248
##
## sigma^2 estimated as 1.051: log likelihood = -723, aic = 1452
arima(arma, order = c(2, 0, 1), include.mean = FALSE)
##
## Call:
## arima(x = arma, order = c(2, 0, 1), include.mean = FALSE)
##
## Coefficients:
## ar1 ar2 ma1
## 0.3136 0.1918 0.9227
## s.e. 0.0486 0.0484 0.0183
##
## sigma^2 estimated as 1.019: log likelihood = -715.26, aic = 1438.52
Aumentar p de 1 a 2 mejora la puntuación AIC y, por lo tanto, el modelo ARMA(2,1) es superior al modelo ARMA(1,1). Ahora comprobaremos ARMA(1,2):
arima(arma, order = c(1, 0, 2), include.mean = FALSE)
##
## Call:
## arima(x = arma, order = c(1, 0, 2), include.mean = FALSE)
##
## Coefficients:
## ar1 ma1 ma2
## 0.7602 0.4654 -0.4079
## s.e. 0.0626 0.0858 0.0832
##
## sigma^2 estimated as 1.014: log likelihood = -714.27, aic = 1436.54
La puntuación AIC de ARMA(1,2) es incluso más baja que la de ARMA(2,1) y, por lo tanto, ahora es el modelo superior. Ahora probaremos la última combinación de ARMA(2,2) y veremos si podemos lograr mejoras adicionales:
arima(arma, order = c(2, 0, 2), include.mean = FALSE)
##
## Call:
## arima(x = arma, order = c(2, 0, 2), include.mean = FALSE)
##
## Coefficients:
## ar1 ar2 ma1 ma2
## 0.7239 0.0194 0.4997 -0.3783
## s.e. 0.2458 0.1257 0.2427 0.2134
##
## sigma^2 estimated as 1.014: log likelihood = -714.26, aic = 1438.51
El uso de la puntuación AIC o BIC como criterio de selección de modelo no garantiza que el modelo seleccionado (ya sea por puntuación AIC o BIC) no viole los supuestos del modelo. Por lo tanto, el siguiente paso es aplicar un análisis residual para verificar que, como vimos en el Capítulo 8, Estrategias de pronóstico, los residuos son los siguientes: • La media y la varianza son constantes • Ruido blanco o no correlacionado • Normalmente distribuido Por ejemplo, podemos utilizar la función de verificación de residuos del paquete de pronóstico para validar esos supuestos en el modelo seleccionado:
library(forecast)
best_arma <- arima(arma, order = c(1, 0, 2), include.mean = FALSE)
checkresiduals(best_arma)
##
## Ljung-Box test
##
## data: Residuals from ARIMA(1,0,2) with zero mean
## Q* = 5.3129, df = 7, p-value = 0.6218
##
## Model df: 3. Total lags used: 10
Pronosticar cualquiera de los modelos que vimos hasta ahora fue sencillo: utilizamos el función de pronóstico del paquete de pronóstico de manera similar a como la usamos en el capítulo anterior. Por ejemplo, el siguiente código demuestra el pronóstico de las próximas 100 observaciones del modelo AR que entrenamos anteriormente en la sección El proceso AR con la función ar:
ar_fc <- forecast(md_ar, h = 100)
Podemos usar plot_forecast para trazar el resultado del pronóstico:
plot_forecast(ar_fc,
title = "Forecast AR(2) Model",
Ytitle = "Value",
Xtitle = "Year")
Descripción de la imagen
Como puedes ver tanto el modelo AR como MA se pueden representar con el modelo ARMA, también puedes representar los modelos AR, MA o ARMA con el modelo ARIMA, por ejemplo: • El modelo ARIMA(0, 0, 0) es equivalente al ruido blanco • El modelo ARIMA(0, 1, 0) es equivalente a un paseo aleatorio • El modelo ARIMA(1, 0, 0) es equivalente a un proceso AR(1) • El modelo ARIMA(0, 0, 1) es equivalente a un proceso MA(1) • El modelo ARIMA(1, 0, 1) es equivalente a un proceso ARMA(1,1)
#Identificando un proceso ARIMA En esta sección aplicaremos un modelo ARIMA, en lugar del modelo ARMA, siempre que la serie de entrada no sea estacionaria. Se requiere diferenciación para transferirlo a un estado estacionario. Identificar y configurar el modelo ARIMA es un proceso de dos pasos y se basa en la siguientes pasos: 1. Identifique el grado de diferenciación que se requiere para transferir la serie a un estado estacionario. 2. Identificar el proceso ARMA (o procesos AR y MA), como se presentó en la sección anterior. Con base en los hallazgos de estos pasos, estableceremos los parámetros del modelo p, d y q.
#Identificar el grado de diferenciación del modelo De manera similar a los parámetros p y q, la configuración del parámetro d (el grado de diferenciación de la serie) se puede realizar con los gráficos ACF y PACF. En el siguiente ejemplo, usaremos los precios mensuales del café Robusta desde 2000. Esta serie es parte de los múltiples objetos de series temporales Coffee_Prices del paquete TSstudio. Comenzaremos cargando la serie Coffee_Prices y restando los precios mensuales de Robusta desde enero de 2010 usando la función de ventana:
data(Coffee_Prices)
robusta_price <- window(Coffee_Prices[,1], start = c(2000, 1))
Tracemos la serie robusta_price y revisemos su estructura con la función ts_plot:
ts_plot(robusta_price,
title = "The Robusta Coffee Monthly Prices",
Ytitle = "Price in USD",
Xtitle = "Year")
Como puede ver, los precios del café Robusta con el tiempo tienen una tendencia alcista y, por lo tanto, no se encuentran en un estado estacionario. Además, dado que esta serie representa precios continuos, es probable que tenga una fuerte correlación con sus desfases pasados (ya que los cambios en el precio suelen ser cercanos al precio anterior). Usaremos nuevamente la función acf para identificar el tipo de relación entre la serie y su rezago: # ——– Código 32 ——–
acf(robusta_price)
Como puede ver en el resultado anterior del gráfico ACF, la correlación de la serie con sus rezagos está disminuyendo lentamente con el tiempo de manera lineal. Se puede eliminar tanto la tendencia de la serie como la correlación entre la serie y sus rezagos diferenciando las series. Comenzaremos con la primera diferenciación usando la función diff:
robusta_price_d1 <- diff(robusta_price)
Repasemos la primera diferencia de la serie con las funciones acf y pacf:
par(mfrow=c(1,2))
acf(robusta_price_d1)
pacf(robusta_price_d1)
Los gráficos ACF y PACF de la primera diferencia de la serie indican que es apropiado utilizar un proceso AR(1) en las series diferenciadas ya que el ACF está disminuyendo y el PACF se reduce en el primer rezago. Por lo tanto, aplicaremos un modelo ARIMA(1,1,0) en el Serie de precios robusta que incluye la primera diferencia:
robusta_md <- arima(robusta_price, order = c(1, 1, 0))
summary(robusta_md)
##
## Call:
## arima(x = robusta_price, order = c(1, 1, 0))
##
## Coefficients:
## ar1
## 0.2780
## s.e. 0.0647
##
## sigma^2 estimated as 0.007142: log likelihood = 231.38, aic = -458.76
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 0.002595604 0.08432096 0.06494772 0.08104715 4.254984 1.001542
## ACF1
## Training set 0.001526295
Residual del modelo:
checkresiduals(robusta_md)
##
## Ljung-Box test
##
## data: Residuals from ARIMA(1,1,0)
## Q* = 26.896, df = 23, p-value = 0.2604
##
## Model df: 1. Total lags used: 24
Descripción de la imagen
#Tuning del modelo SARIMA El proceso de ajuste del modelo SARIMA sigue la misma lógica que uno de los modelos ARIMA. Sin embargo, la complejidad del modelo aumenta ya que ahora hay seis parámetros para ajustar, es decir, p, d, q, P, D y Q, a diferencia de los tres del modelo ARIMA. Afortunadamente, el ajuste de los parámetros estacionales P, D y Q sigue la misma lógica que los de p, d, q, respectivamente, con el uso de los gráficos ACF y PACF. La principal diferencia entre la sintonización de estos dos grupos de parámetros (no estacionales y estacionales) es que los parámetros no estacionales se sintonizan con los rezagos no estacionales, como vimos anteriormente con el modelo ARIMA. Por otro lado, la sintonización de los parámetros estacionales se sintoniza con los desfases estacionales (por ejemplo, para series mensuales con desfases 12, 24, 36, etcétera).
#Ajustando los parámetros no estacionales Aplicando la misma lógica que usamos con el modelo ARIMA, el ajuste de los parámetros no estacionales del modelo SARIMA se basa en los gráficos ACF y PACF: • Se debe utilizar un proceso AR(p) si los retrasos no estacionales del gráfico ACF están disminuyendo, mientras que los retrasos correspondientes de los gráficos PACF están disminuyendo en el retraso p • De manera similar, se debe utilizar un proceso MA(q) si los rezagos no estacionales de la gráfica ACF se están acortando en el rezago q y los rezagos correspondientes de las gráficas PACF están disminuyendo. • Cuando los retrasos no estacionales del ACF y del PACF están disminuyendo, se debe utilizar un modelo ARMA • La diferenciación de la serie con los desfases no estacionales debe aplicarse cuando los desfases no estacionales del gráfico ACF están decayendo de manera lineal.
#Ajustando los parámetros estacionales El ajuste de los parámetros estacionales del modelo SARIMA con ACF y PACF sigue las mismas pautas que utilizamos para seleccionar los parámetros ARIMA: • Usaremos un proceso autorregresivo estacional con un orden de P, o SAR(P), si los retrasos estacionales del gráfico ACF están disminuyendo y los retrasos estacionales del gráfico PACF están cortados por el retraso estacional P. • De manera similar, aplicaremos un proceso de promedio móvil estacional con un orden de Q, o SMA(Q), si los retrasos estacionales del gráfico ACF están cortados por el retraso estacional Q y los retrasos estacionales del gráfico PACF están disminuyendo.
Se debe utilizar un modelo ARMA siempre que los retrasos estacionales de los gráficos ACF y PACF estén disminuyendo. • Se debe aplicar la diferenciación estacional si la correlación de los desfases estacionales está decayendo de manera lineal.
#Previsión del consumo mensual de gas natural en EE. UU. con el modelo SARIMA: un estudio de caso En esta sección, aplicaremos lo que aprendimos a lo largo de este capítulo y pronosticaremos el consumo mensual de gas natural en EE. UU. utilizando el modelo SARIMA. Carguemos la serie USgas desde el paquete TSstudio:
data(USgas)
Tracemos la serie con la función ts_plot y repasemos las principales características de la serie:
ts_plot(USgas,
title = "US Monthly Natural Gas consumption",
Ytitle = "Billion Cubic Feet",
Xtitle = "Year")
Como vimos en los capítulos anteriores, la serie USgas tiene un fuerte patrón estacional y, por lo tanto, entre la familia de modelos ARIMA, el modelo SARIMA es el modelo más apropiado a utilizar. Además, dado que la serie tiene una tendencia ascendente, ya podemos concluir que la serie no es estacionaria y se requiere cierta diferenciación de la serie. Comenzaremos configurando las particiones de entrenamiento y prueba con las funciones ts_split, dejando los últimos 12 meses de la serie como partición de prueba:
USgas_split <- ts_split(USgas, sample.out = 12)
train <- USgas_split$train
test <- USgas_split$test
Antes de comenzar el proceso de entrenamiento del modelo SARIMA, realizaremos diagnósticos con respecto a la correlación de series con las funciones ACF y PACF. Dado que estamos interesados en ver la relación de la serie con sus desfases estacionales, aumentaremos el número de desfases para calcular y mostrar estableciendo el argumento lag.max en 60 desfases:
par(mfrow=c(1,2))
acf(train, lag.max = 60)
pacf(train, lag.max = 60)
El gráfico ACF anterior indica que la serie tiene una fuerte correlación tanto con los rezagos estacionales como con los no estacionales. Además, la caída lineal de los rezagos estacionales indica que la serie no es estacionaria y que se requiere diferenciación estacional. Comenzaremos con una diferenciación estacional de la serie y trazaremos el resultado para identificar si la serie está en un estado estacionario:
USgas_d12 <- diff(train, 12)
ts_plot(USgas_d12,
title = "US Monthly Natural Gas consumption - First Seasonal Difference",
Ytitle = "Billion Cubic Feet (First Difference)",
Xtitle = "Year")
Si bien eliminamos la tendencia de la serie, la variación de la serie aún no es estable. Por tanto, intentaremos coger también la primera diferencia de la serie:
USgas_d12_1 <- diff(diff(USgas_d12, 1))
ts_plot(USgas_d12_1,
title = "US Monthly Natural Gas consumption - First Seasonal and Non-Seasonal Differencing",
Ytitle = "Billion Cubic Feet (Difference)",
Xtitle = "Year")
Después de tomar la diferenciación de primer orden, junto con la diferenciación estacional de primer orden, la serie parece estabilizarse alrededor de la línea cero del eje x (o bastante cerca de ser estable). Después de transformar la serie a un estado estacionario, podemos revisar las funciones ACF y PACF nuevamente para identificar el proceso requerido:
par(mfrow=c(1,2))
acf(USgas_d12_1, lag.max = 60)
pacf(USgas_d12_1, lag.max = 60)
La principal observación de los gráficos ACF y PACF anteriores es que tanto los rezagos estacionales como los no estacionales (en ambos gráficos) están disminuyendo. Por lo tanto, podemos concluir que después de diferenciar las series y transformarlas en un estado estacionario, debemos aplicar un proceso ARMA tanto para los componentes estacionales como para los no estacionales del modelo SARIMA. El proceso de ajuste de los parámetros del modelo SARIMA sigue los mismos pasos que aplicamos anteriormente con el modelo ARMA: • Establecemos el orden máximo del modelo (es decir, la suma de los seis parámetros del modelo) • Establecemos un rango de una posible combinación de los valores de los parámetros bajo la restricción de orden máximo del modelo. • Probamos y puntuamos cada modelo, es decir, una metodología de puntuación típica con el AIC (que utilizamos anteriormente) o BIC Seleccionamos un conjunto de combinaciones de parámetros que dan los mejores resultados.
Ahora, comenzaremos el proceso de ajuste para la serie USgas estableciendo el orden del modelo en siete y estableciendo los valores de los parámetros del modelo en el rango de 0 y 2. Dado que ya identificamos los valores de d y D (para Por ejemplo, d = 1 y D = 1), que son los parámetros diferenciadores del modelo SARIMA, ahora podemos centrarnos en ajustar los cuatro parámetros restantes del modelo, es decir, p, q, P y Q. Definamos esos parámetros y asignar los valores de búsqueda:
p <- q <- P <- Q <- 0:2
Según la restricción de orden del modelo y el posible rango de valores de los parámetros del modelo, hay 66 combinaciones posibles. Por lo tanto, tendrá sentido, en este caso, automatizar el proceso de búsqueda y crear una función de búsqueda de cuadrícula para identificar los valores de los parámetros que minimizan la puntuación AIC. Utilizaremos la expansión. función grid para crear un data.frame con todas las combinaciones de búsqueda posibles:
arima_grid <- expand.grid(p,q,P,Q)
names(arima_grid) <- c("p", "q", "P", "Q")
arima_grid$d <- 1
arima_grid$D <- 1
A continuación, recortaremos la tabla de búsqueda de la cuadrícula utilizando combinaciones que excedan la restricción de orden del modelo (por ejemplo, k ≤7). Calcularemos y asignaremos esto a la variable k con la función RowSums:
arima_grid$k <- rowSums(arima_grid)
Utilizaremos la función de filtro del paquete dplyr para eliminar combinaciones donde el valor k sea mayor que 7:
library(dplyr)
arima_grid <- arima_grid %>% filter(k <= 7)
Ahora que la tabla de búsqueda de la cuadrícula está lista, podemos iniciar el proceso de búsqueda. Usaremos la función lapply para iterar sobre la tabla de búsqueda de la cuadrícula. Esta función entrenará el modelo SARIMA y calificará su AIC para cada conjunto de parámetros en la tabla de búsqueda de la cuadrícula. La función arima puede entrenar el modelo SARIMA estableciendo el argumento estacional del modelo con los valores de P, D y Q: ```{r}# arima_search <- lapply(1:nrow(arima_grid), function(i){ md <- NULL md <- arima(train, order = c(arima_grid\(p[i], 1, arima_grid\)q[i]), seasonal = list(order = c(arima_grid\(P[i], 1, arima_grid\)Q[i])))
results <- data.frame(p = arima_grid\(p[i], d = 1, q = arima_grid\)q[i], P = arima_grid\(P[i], D = 1, Q = arima_grid\)Q[i], AIC = md$aic) }) %>% bind_rows() %>% arrange(AIC)
```{r}#
library(forecast)
arima_search <- lapply(1:nrow(arima_grid), function(i){
tryCatch({
md <- arima(train,
order = c(arima_grid$p[i], 1, arima_grid$q[i]),
seasonal = list(order = c(arima_grid$P[i], 1, arima_grid$Q[i])),
method = "CSS") # Usar método CSS para mayor robustez
results <- data.frame(p = arima_grid$p[i],
d = 1,
q = arima_grid$q[i],
P = arima_grid$P[i],
D = 1,
Q = arima_grid$Q[i],
AIC = md$aic)
}, error = function(e) {
NULL # Devolver NULL en caso de error para manejar problemas de convergencia
})
})
# Filtrar los resultados no nulos y combinarlos en un solo dataframe
arima_results <- do.call(rbind, Filter(Negate(is.null), arima_search))
# Ordenar los resultados por AIC
arima_results <- arima_results[order(arima_results$AIC), ]
Usamos las funciones bind_rows y organizar para agregar los
resultados de la búsqueda y organizamos la tabla para las funciones
dplyr. Repasemos los principales resultados de la tabla de búsqueda:
{r}# head(arima_search)
El modelo principal basado en la tabla de búsqueda anterior es el modelo SARIMA(1, 1, 1)(2, 1, 1)12. Antes de finalizar el pronóstico, evaluemos el rendimiento del modelo seleccionado en el conjunto de pruebas. Volveremos a entrenar el modelo utilizando la configuración del modelo seleccionado:
USgas_best_md <- arima(train, order = c(1,1,1), seasonal = list(order = c(2,1,1)))
Los coeficientes del modelo, como podemos ver en el siguiente resumen del modelo, son todos estadísticamente significativos a un nivel de 0,1:
USgas_best_md
##
## Call:
## arima(x = train, order = c(1, 1, 1), seasonal = list(order = c(2, 1, 1)))
##
## Coefficients:
## ar1 ma1 sar1 sar2 sma1
## 0.4247 -0.9180 0.0132 -0.2639 -0.7449
## s.e. 0.0770 0.0376 0.0894 0.0834 0.0753
##
## sigma^2 estimated as 10160: log likelihood = -1292.96, aic = 2597.91
Utilicemos el modelo entrenado USgas_best_md para pronosticar las observaciones correspondientes del conjunto de pruebas:
USgas_test_fc <- forecast(USgas_best_md, h = 12)
Como hicimos anteriormente, evaluaremos el desempeño del modelo con la función de precisión:
accuracy(USgas_test_fc, test)
## ME RMSE MAE MPE MAPE MASE
## Training set 6.081099 97.85701 73.36854 0.1298714 3.517097 0.6371821
## Test set 42.211253 104.79281 83.09943 1.4913412 3.314280 0.7216918
## ACF1 Theil's U
## Training set 0.004565602 NA
## Test set -0.049999868 0.3469228
Podemos utilizar el rendimiento del modelo ingenuo estacional que utilizamos en el Capítulo 8, Estrategias de pronóstico (usando el mismo conjunto de entrenamiento y pruebas), como punto de referencia para el rendimiento del modelo SARIMA. Recuerde que la puntuación MAPE del modelo ingenuo estacional fue del 5,2% en el conjunto de entrenamiento y del 9,7% en el conjunto de prueba. Por lo tanto, SARIMA nos proporciona un aumento en la precisión con una puntuación MAPE del 3,4 % y 7,3 % en las particiones de entrenamiento y prueba, respectivamente. Ahora usaremos la función test_forecast para obtener una vista más intuitiva del rendimiento del modelo en las particiones de entrenamiento y prueba:
test_forecast(USgas,
forecast.obj = USgas_test_fc,
test = test)
Como puede ver, el modelo SARIMA captura con éxito el patrón estacional y de tendencia de la serie. Por otro lado, al modelo le resulta difícil capturar los picos estacionales (mes de enero) en la partición de entrenamiento y tiene un error absoluto del 6,7 % para el mes de enero (pico anual) en la partición de prueba. Este es el resultado de la gran fluctuación durante el invierno. Podemos manejar esta incertidumbre del modelo durante las horas pico con los intervalos de confianza del modelo o simulaciones de trayectoria que analizamos en el Capítulo 8, Estrategias de pronóstico. En este punto, debes hacer una pausa y evaluar el desempeño general del modelo hasta el momento en las métricas principales: ⚫ Puntuación AIC en el conjunto de entrenamiento • Los coeficientes del modelo son significativos. • Puntuación MAPE tanto en el conjunto de entrenamiento como en el de pruebas ⚫ Comparar el rendimiento con otros modelos (por ejemplo, un modelo ingenuo) • Gráfico ajustado y pronosticado
Ahora que hemos cumplido las condiciones anteriores, podemos pasar al último paso del proceso de pronóstico y generar el pronóstico final con el modelo seleccionado. Comenzaremos reentrenando el modelo seleccionado en todas las series:
final_md <- arima(USgas, order = c(1,1,1), seasonal = list(order = c(2,1,1)))
Antes de pronosticar los próximos 12 meses, verifiquemos que el residuo de los modelos satisface la condición del modelo:
checkresiduals(final_md)
##
## Ljung-Box test
##
## data: Residuals from ARIMA(1,1,1)(2,1,1)[12]
## Q* = 30.173, df = 19, p-value = 0.04964
##
## Model df: 5. Total lags used: 24
Al observar el gráfico de residuos anterior, puede ver que los residuos son ruido blanco y están distribuidos normalmente. Además, la prueba de Ljung-Box confirma que no queda ninguna autocorrelación en los residuos; con un valor p de 0,12, no podemos rechazar la hipótesis nula de que los residuos son ruido blanco. ¡Estamos listos para comenzar! Usemos la función de pronóstico para pronosticar los próximos 12 meses de la serie USgas:
USgas_fc <- forecast(final_md, h = 12)
Podemos trazar el pronóstico con la función plot_forecast:
plot_forecast(USgas_fc,
title = "US Natural Gas Consumption - Forecast",
Ytitle = "Billion Cubic Feet",
Xtitle = "Year")
#La función auto.arima Uno de los principales desafíos de la previsión con la familia de modelos ARIMA es el engorroso proceso de ajuste de los modelos. Como vimos en este capítulo, este proceso incluye muchos pasos manuales que se requieren para verificar la estructura de la serie (estacionaria o no estacionaria), transformaciones de datos, análisis descriptivo con los gráficos ACF y PACF para identificar el tipo de proceso, y eventualmente ajustar los parámetros del modelo. Si bien puede llevar unos minutos entrenar un modelo ARIMA para una sola serie, es posible que no se amplíe si tiene docenas de series que pronosticar.
La función auto.arima del paquete de pronóstico proporciona una solución a este problema. Este algoritmo automatiza el proceso de ajuste del modelo ARIMA con el uso de métodos estadísticos para identificar tanto la estructura de la serie (estacionaria o no) como el tipo (estacional o no), y establece los parámetros del modelo en consecuencia. Por ejemplo, podemos utilizar la función para pronosticar el gas estadounidense:
USgas_auto_md1 <- auto.arima(train)
El uso de los argumentos predeterminados de la función auto.arima devuelve el modelo ARIMA que minimiza la puntuación AIC. En este caso, se seleccionó un modelo SARIMA(1, 0, 4) (1, 1, 2)12 con una puntuación AIC de 2480,57:
USgas_auto_md1
## Series: train
## ARIMA(2,1,1)(2,1,1)[12]
##
## Coefficients:
## ar1 ar2 ma1 sar1 sar2 sma1
## 0.4301 -0.0372 -0.9098 0.0117 -0.2673 -0.7431
## s.e. 0.0794 0.0741 0.0452 0.0887 0.0830 0.0751
##
## sigma^2 = 10446: log likelihood = -1292.83
## AIC=2599.67 AICc=2600.22 BIC=2623.2
De forma predeterminada, la función auto.arima aplica una búsqueda de modelo más corta mediante un enfoque paso a paso para reducir el tiempo de búsqueda. La desventaja de este enfoque es que el modelo puede omitir algunos modelos que pueden lograr mejores resultados. Esto se puede ver en los resultados de USgas_auto_md1, que logró una puntuación AIC más alta que el modelo que ajustamos con la búsqueda de cuadrícula que utilizamos anteriormente (2480,57 frente a 2459,81 en el modelo anterior). Podemos improvisar con los resultados de auto.arima estableciendo el argumento de búsqueda del modelo. El argumento paso a paso, cuando se establece en FALSO, le permite establecer una búsqueda más sólida y exhaustiva, con el costo de un mayor tiempo de búsqueda. Esta compensación entre rendimiento y tiempo de cálculo se puede equilibrar siempre que se tenga conocimiento previo sobre la estructura y las características de la serie. Por ejemplo, volvamos a entrenar el conjunto de entrenamiento de la serie USgas, esta vez usando las siguientes configuraciones: • Establezca los parámetros de diferenciación & y D en 1. ⚫ Limite el orden del modelo a siete usando el máximo. argumento de orden. El argumento max.order define los valores máximos de p+q+P+Q, por lo tanto, debemos establecerlo en cinco (dado que d y D están establecidos en 1). ⚫ Bajo estas restricciones, busque todas las combinaciones posibles estableciendo el argumento paso a paso en FALSO. • Establezca el argumento de aproximación en FALSO para realizar cálculos más precisos de los criterios de información:
USgas_auto_md2 <- auto.arima(train,
max.order = 5,
D = 1,
d = 1,
ic = "aic",
stepwise = FALSE,
approximation = FALSE)
Puede ver en los siguientes resultados que con la búsqueda robusta, el auto. El algoritmo Arima devuelve el mismo modelo que se identificó con la búsqueda de cuadrícula que utilizamos en la sección del modelo ARIMA estacional:
USgas_auto_md2
## Series: train
## ARIMA(1,1,1)(2,1,1)[12]
##
## Coefficients:
## ar1 ma1 sar1 sar2 sma1
## 0.4247 -0.9180 0.0132 -0.2639 -0.7449
## s.e. 0.0770 0.0376 0.0894 0.0834 0.0753
##
## sigma^2 = 10405: log likelihood = -1292.96
## AIC=2597.91 AICc=2598.32 BIC=2618.08
#Regresión lineal con errores ARIMA En el Capítulo 9, Pronósticos con regresión lineal, vimos que con algunos pasos simples podemos utilizar un modelo de regresión lineal como modelo de pronóstico de series de tiempo. Recuerde que una forma general del modelo de regresión lineal se puede representar mediante la siguiente ecuación: Yt = Bo+BX+Et
Uno de los principales supuestos del modelo de regresión lineal es que el término de error de la serie, €, es la serie de ruido blanco (por ejemplo, no existe correlación entre los residuos y sus rezagos). Sin embargo, cuando se trabaja con datos de series de tiempo, esta suposición se facilita ya que, normalmente, los predictores del modelo no explican todas las variaciones de la serie y algunos patrones quedan en los residuos del modelo. Se puede ver un ejemplo del fracaso de esta suposición al ajustar un modelo de regresión lineal para pronosticar la serie AirPassenger.
#Violación del supuesto de ruido blanco Para ilustrar este punto, utilizaremos la función ts1m para hacer una regresión de la serie AirPassenger con su tendencia, componente estacional y desfase estacional (retraso 12), y luego evaluaremos los residuos del modelo con la función checkresiduals. Antes de comenzar con el proceso de capacitación, preparemos los datos y creemos nuevas características para representar la tendencia de la serie y los componentes estacionales (usando la función mensual del paquete lubridate), así como el desfase estacional:
df <- ts_to_prophet(AirPassengers) %>% setNames(c("date", "y"))
df$lag12 <- dplyr::lag(df$y, n = 12)
library(lubridate)
df$month <- factor(month(df$date, label = TRUE), ordered = FALSE)
df$trend <- 1:nrow(df)
Aquí, las tres variables representan lo siguiente: ⚫ desfase12: Una variable numérica que representa el desfase estacional de la serie (es decir, desfase 12) mes: Una variable categórica (12 categorías, una para cada mes del año) que representa el componente estacional de la serie. ⚫ tendencia: Una variable numérica que representa el efecto marginal de moverse en el tiempo una unidad de índice, que en este caso es el cambio marginal de la serie al moverse en el tiempo un mes Ahora, dividiremos la serie en particiones de entrenamiento y prueba, dejando los últimos 12 meses para probar usando la función ts_split:
par <- ts_split(ts.obj = AirPassengers, sample.out = 12)
train <- par$train
test <- par$test
Para la regresión del objeto de serie temporal con un objeto data.frame externo (df), usaremos Aplicar la misma división para entrenar y probar particiones en el predictor. objeto de marco de datos:
train_df <- df[1:(nrow(df) - 12), ]
test_df <- df[(nrow(df) - 12 + 1):nrow(df), ]
Ahora podemos empezar a entrenar el modelo con la función tslm:
md1 <- tslm(train ~ season + trend + lag12, data = train_df)
Usemos la función checkresiduals para ver los residuos del modelo:
checkresiduals(md1)
##
## Breusch-Godfrey test for serial correlation of order up to 24
##
## data: Residuals from Linear regression model
## LM test = 83.147, df = 24, p-value = 1.903e-08
En el gráfico ACF anterior, la serie de residuos tiene una fuerte correlación con sus rezagos pasados y, por lo tanto, la serie no es ruido blanco. Podemos concluir del gráfico de residuos que el modelo de regresión no pudo capturar todos los patrones de la serie. En general, esto no debería sorprender ya que la variación de la serie podría verse afectada por otras variables, como los precios del billete y del petróleo, la tasa de desempleo y otros factores externos.
Una solución sencilla a este problema es modelar los términos de error con el modelo ARIMA y agregarlo a la regresión.
Descripción de la imagen
La ecuación modificada del modelo AirPassengers anterior es una representación simplista del modelo de regresión lineal con errores ARIMA (que en este caso son errores ARMA). Sin embargo, cualquiera de los modelos ARIMA que hemos visto en este capítulo, incluido el modelo SARIMA, se puede utilizar para modelar los residuos del modelo.
Remodelemos la serie AirPassengers, pero esta vez con un modelo de regresión lineal con errores ARIMA. Al modelar el término de error del modelo con un modelo ARIMA, debemos tratar los residuos como una serie en sí misma y establecer los componentes del modelo p, d y q (y P, D y Q si la serie de residuos tiene un componente estacional) utilizando cualquier de los enfoques que presentamos en este capítulo (por ejemplo, ajuste manual, búsqueda en cuadrícula o automatización del proceso de búsqueda con el proceso auto.arima). Para simplificar, utilizaremos la función auto.arima.
La función auto.arima se puede utilizar para modelar un modelo de regresión lineal con errores ARIMA mediante el uso del argumento xreg:
md2 <- auto.arima(train,
xreg = cbind(model.matrix(~ month,train_df)[,-1],
train_df$trend,
train_df$lag12),
seasonal = TRUE,
stepwise = FALSE,
approximation = FALSE)
Establecemos el argumento estacional en VERDADERO para incluir una búsqueda de modelos estacionales y no estacionales. La función auto.arima realizará una búsqueda completa cuando los argumentos paso a paso y de aproximación estén establecidos en FALSO. Usemos la función de resumen para revisar el resumen del modelo:
summary(md2)
## Series: train
## Regression with ARIMA(2,0,0)(2,0,0)[12] errors
##
## Coefficients:
## ar1 ar2 sar1 sar2 monthfeb monthmar monthabr monthmay
## 0.5849 0.3056 -0.4421 -0.2063 -2.7523 0.8231 0.2066 2.8279
## s.e. 0.0897 0.0903 0.1050 0.1060 1.8417 2.3248 2.4162 2.5560
## monthjun monthjul monthago monthsept monthoct monthnov monthdic
## 6.6057 11.2337 12.1909 3.8269 0.6350 -2.2723 -0.9918
## s.e. 3.2916 4.2324 4.1198 2.9243 2.3405 2.4211 1.9172
##
## 0.2726 1.0244
## s.e. 0.1367 0.0426
##
## sigma^2 = 72.82: log likelihood = -426.93
## AIC=889.86 AICc=896.63 BIC=940.04
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 0.4117148 8.353629 6.397538 0.2571543 2.549682 0.2100998
## ACF1
## Training set 0.005966712
La función auto.arima hace una regresión de la serie con las variables tendencia, mes y lag12. Además, el modelo utilizó los procesos AR(2) y SAR(2) para modelar el término de error. Ahora podemos revisar los residuales del modelo md2 modificado con las funciones checkresiduals:
checkresiduals(md2)
##
## Ljung-Box test
##
## data: Residuals from Regression with ARIMA(2,0,0)(2,0,0)[12] errors
## Q* = 25.816, df = 20, p-value = 0.172
##
## Model df: 4. Total lags used: 24
Puede ver el cambio en el gráfico ACF después de aplicar el modelo de regresión lineal con errores ARIMA, ya que la mayoría de los retrasos no son estadísticamente significativos, a diferencia del modelo de regresión lineal anterior (md1). Evaluemos el rendimiento de ambos modelos (md1 y md2) en el conjunto de prueba:
fc1 <- forecast(md1, newdata = test_df)
fc2 <- forecast(md2, xreg = cbind(model.matrix(~ month,test_df)[,-1],
test_df$trend,
test_df$lag12))
Ahora, usemos la función de precisión para revisar el rendimiento de los dos modelos tanto en la partición de prueba como en la de entrenamiento:
accuracy(fc1, test)
## ME RMSE MAE MPE MAPE MASE
## Training set -1.894492e-15 13.99206 11.47136 -0.1200023 4.472154 0.3541758
## Test set 1.079611e+01 20.82782 18.57391 1.9530865 3.828115 0.5734654
## ACF1 Theil's U
## Training set 0.7502578 NA
## Test set 0.1653119 0.4052175
Probemos la precisión del segundo modelo:
accuracy(fc2, test)
## ME RMSE MAE MPE MAPE MASE
## Training set 0.4117148 8.353629 6.397538 0.2571543 2.549682 0.2100998
## Test set -11.2123706 17.928195 13.353910 -2.4898843 2.924174 0.4385521
## ACF1 Theil's U
## Training set 0.005966712 NA
## Test set -0.325044929 0.3923795
Puede ver en el resultado de la función de precisión que la regresión lineal con el modelo ARIMA proporciona una mejora significativa en la precisión del modelo con un aumento del 43% en la puntuación MAPE en el conjunto de entrenamiento (del 4,5% al 2,5%) y 31 % en el conjunto de pruebas (del 3,8% al 2,9%).
#Sección - 12 # Capítulo 12 Código
#Predicción con modelos de aprendizaje automático En el Capítulo 9, Pronósticos con regresión lineal, vimos cómo un modelo de regresión básico podría utilizar algunos pasos simples para crear un pronóstico de serie temporal sólido. El uso de un modelo de regresión lineal para el pronóstico de series temporales se puede generalizar fácilmente a otros enfoques de regresión, en particular, regresiones basadas en aprendizaje automático. En este capítulo, nos centraremos en el uso de modelos de aprendizaje automático para el pronóstico de series temporales utilizando el paquete h2o. Este capítulo requiere algunos conocimientos básicos del proceso de entrenamiento y ajuste de modelos de aprendizaje automático. En este capítulo, cubriremos los siguientes temas: • Introducción al paquete h20 y su funcionalidad • Ingeniería de funciones de datos de series temporales. ⚫ Previsión con el modelo Random Forest • Previsión con el modelo de aprendizaje automático que potencia el gradiente • Previsión con el modelo automatizado de aprendizaje automático.
#Requerimiento técnico Los siguientes paquetes se utilizarán en este capítulo: ⚫ h20: Versión 3.22.1.1 y superior y Java versión 7 y superior ⚫ TSstudio: Versión 0.1.4 y superior ⚫ trama: Versión 4.8 y superior ⚫ lubricar: versión 1.7.4 y superior
#¿Por qué y cuándo deberíamos utilizar el aprendizaje automático? En los últimos años, el uso de modelos de aprendizaje automático (ML) se ha vuelto popular y accesible debido a una mejora significativa en la potencia de cálculo estándar. Esto condujo a un nuevo mundo de métodos y enfoques para modelos de regresión y clasificación. El proceso de creación de pronósticos de series de tiempo con modelos ML sigue el mismo proceso que utilizamos en el Capítulo 9, Pronósticos con regresión lineal, con el modelo de regresión lineal. Antes de comenzar a profundizar en los detalles, es importante advertir sobre el uso de modelos ML en el contexto del pronóstico de series temporales: ⚫ Costo: el uso de modelos ML suele ser más costoso que los modelos de regresión típicos, tanto en potencia de cálculo como en tiempo. • Precisión: el desempeño del modelo ML depende en gran medida de la calidad (es decir, una fuerte relación de causalidad con la variable dependiente) de los predictores. Es probable que los modelos de ML tengan un rendimiento superior al de los métodos tradicionales, como la regresión lineal, cuando se dispone de predictores sólidos. • Ajuste: el procesamiento de modelos de ML típicos es más complejo que el de los modelos de regresión comunes, ya que esos modelos tienen más parámetros de ajuste y, por lo tanto, requieren cierta experiencia. ⚫ Caja negra: la mayoría de los modelos ML se consideran cajas negras, ya que es difícil interrumpir su salida. ⚫ Incertidumbre: Generalmente, no existe un método sencillo para cuantificar la incertidumbre del pronóstico con intervalos de confianza como lo hace el modelo tradicional de series de tiempo. Por otro lado, la principal ventaja de los modelos ML es su poder predictivo (cuando se dispone de insumos de calidad), que, en muchos casos, vale la pena el esfuerzo y el tiempo involucrado en el proceso.
En el contexto del pronóstico de series de tiempo, será beneficioso pronosticar con modelos ML en los siguientes casos: • Patrones estructurales: salidas en la serie, ya que pueden producir características nuevas y predictivas. • Estacionalidad múltiple: como caso especial de los patrones estructurales ya que, por lo general, los modelos tradicionales de series de tiempo tienen dificultades para capturar esos patrones cuando existen. En este capítulo, nos centraremos en el pronóstico de las ventas mensuales de vehículos en EE. UU. utilizando los siguientes modelos: • Bosque aleatorio • Máquina de refuerzo de gradiente • Modelo de aprendizaje automático automático Tenga en cuenta que los objetivos de este capítulo no son enseñarle los principios de ML (ya que es un tema en sí mismo y fuera del alcance de este libro), sino los principios de construcción de modelos de pronóstico con el uso de métodos de ML. Para este capítulo se recomiendan algunos conocimientos básicos sobre entrenamiento y ajuste de modelos de ML.
#¿Por qué agua? En este capítulo, utilizaremos el paquete h20 para crear y entrenar modelos de pronóstico con el uso de modelos ML. H2O es una biblioteca de código abierto, distribuida y basada en Java para aplicaciones de aprendizaje automático. Tiene API tanto para R (el paquete h2o) como para Python, e incluye aplicaciones para modelos de aprendizaje supervisados y no supervisados. Esto incluye algoritmos como el aprendizaje profundo (DL), la máquina de aumento de gradiente (GBM), XGBoost, el bosque aleatorio distribuido (RF) y el modelo lineal generalizado (GLM). La principal ventaja del paquete h20 es que se basa en procesamiento distribuido y, por lo tanto, puede usarse en memoria o ampliarse con el uso de potencia informática externa. Además, los algoritmos del paquete h20 proporcionan varios métodos para que podamos entrenar y ajustar modelos de aprendizaje automático, como el método de validación cruzada y la función de búsqueda de cuadrícula incorporada. # ——– Cambio de código 1 ——–
#Previsión de ventas mensuales de vehículos en EE. UU.: un estudio de caso
En este capítulo, nos centraremos en pronosticar las ventas mensuales totales de vehículos en los EE. UU. en los próximos 12 meses con métodos de ML. Las ventas mensuales totales de vehículos en la serie de EE. UU. son disponible en el paquete TSstudio:
library(TSstudio)
data(USVSales)
#Análisis exploratorio de la serie USVSales En este apartado nos centraremos en explorar y conocer las principales características de la serie. Estos conocimientos se utilizarán para crear nuevas funciones como entradas para el modelo de aprendizaje automático. El análisis exploratorio de la serie USVSales se centrará en los siguientes temas: ⚫ Ver la estructura de la serie temporal (frecuencia, inicio y final de la serie, etc.) • Explorar los componentes de la serie (componentes estacionales, cíclicos, de tendencia y aleatorios) • Análisis de estacionalidad • Análisis de correlación La estructura de la serie. Comencemos con ts_info y revisemos la estructura de la serie USVsales:
ts_info(USVSales)
## The USVSales series is a ts object with 1 variable and 528 observations
## Frequency: 12
## Start time: 1976 1
## End time: 2019 12
La serie USVsales es un objeto ts mensual que representa las ventas totales de vehículos en EE. UU. entre 1976 y 2018 en miles de unidades. Tracemos la serie y repasemos su estructura con la función ts_plot:
ts_plot(USVSales,
title = "US Total Monthly Vehicle Sales",
Ytitle = "Thousands of Units",
Xtitle = "Year")
Los componentes de la serie. Podemos obtener una visión más profunda de los componentes de la serie descomponiendo la serie en sus componentes y trazándolos con la función ts_decompose:
ts_decompose(USVSales)
#Análisis estacional Para observar más de cerca el componente estacional de la serie, restaremos de la serie, descompondremos la tendencia que analizamos anteriormente y usaremos la función ts_seasonal para trazar el diagrama de caja del componente estacional de la serie de detendencia:
USVSales_detrend <- USVSales - decompose(USVSales)$trend
ts_seasonal(USVSales_detrend, type = "box")
## Warning: Ignoring 1 observations
## Warning: Ignoring 1 observations
## Warning: Ignoring 1 observations
## Warning: Ignoring 1 observations
## Warning: Ignoring 1 observations
## Warning: Ignoring 1 observations
## Warning: Ignoring 1 observations
## Warning: Ignoring 1 observations
## Warning: Ignoring 1 observations
## Warning: Ignoring 1 observations
## Warning: Ignoring 1 observations
## Warning: Ignoring 1 observations
Podemos ver en el gráfico estacional anterior que, normalmente, el pico del año se produjo durante los meses de marzo, mayo y junio. Además, se puede ver que las ventas decaen a partir de los meses de verano y vuelven a alcanzar su punto máximo en diciembre durante las temporadas navideñas. Por otro lado, el mes de enero suele ser el mes más bajo del año en términos de ventas. # ——– Código 6 ——–
Análisis de correlación Como vimos en el Capítulo 7, Análisis de correlación, la serie USVSales tiene una alta correlación con su primer rezago estacional. Podemos revisar esta evaluación con el uso de la función ts_acf del paquete TSstudio para revisar la autocorrelación de la serie:
acf(USVSales)
Podemos ampliar la relación de la serie con los últimos tres desfases estacionales usando la función ts_lag:
ts_lags(USVSales, lags = c(12, 24, 36))
#Análisis exploratorio: hallazgos clave Podemos concluir nuestro breve análisis exploratorio de la serie USVsales con las siguientes observaciones: • La serie USVSales es una serie mensual con una clara estacionalidad mensual • La tendencia de la serie tiene una forma cíclica, por lo que la serie tiene un componente cíclico incorporado en la tendencia. • El ciclo más reciente de la serie comienza justo después del final de la crisis económica de 2008, entre 2009 y 2010. • Parece que el ciclo actual alcanzó su punto máximo a medida que la tendencia comienza a aplanarse •La serie tiene una fuerte correlación con su primer desfase estacional
Además, como pretendemos tener una previsión a corto plazo (de 12 meses), no tiene sentido utilizar la serie completa, ya que puede introducir algo de ruido en el modelo debido al cambio de dirección de la tendencia cada dos años. (Si intenta crear un pronóstico a largo plazo, puede ser una buena idea utilizar todas o la mayor parte de las series). Por lo tanto, utilizaremos las observaciones de entrenamiento del modelo desde 2010 en adelante. Usaremos la función ts_to_prophet del paquete TSstudio para transformar la serie de un objeto ts en datos. marco y la función de ventana para subconjuntos de observaciones de la serie desde enero de 2010:
df <- ts_to_prophet(window(USVSales, start = c(2010, 1)))
names(df) <- c("date", "y")
head(df)
## date y
## 1 2010-01-01 712.469
## 2 2010-02-01 793.362
## 3 2010-03-01 1083.953
## 4 2010-04-01 997.334
## 5 2010-05-01 1117.570
## 6 2010-06-01 1000.455
Antes de seguir adelante y comenzar con la etapa de ingeniería de características, tracemos y revisemos la serie de subconjuntos de USVSales con la función ts_plot:
ts_plot(df,
title = "US Total Monthly Vehicle Sales (Subset)",
Ytitle = "Thousands of Units",
Xtitle = "Year")
#Ingeniería de características
La ingeniería de características juega un papel fundamental al modelar con algoritmos de ML. Nuestro siguiente paso, basado en las observaciones anteriores, es crear nuevas características que puedan usarse como información para el modelo. En el contexto de la previsión de series temporales, a continuación se muestran algunos ejemplos de posibles nuevas características que se pueden crear a partir de la propia serie:
⚫ La serie de tendencia: Utiliza un índice numérico. Además, como la tendencia de la serie no es lineal, utilizaremos un segundo polinomio del índice para capturar la curvatura general de la tendencia de la serie. • Componente estacional: Crea una variable categórica para el mes del año para capturar la estacionalidad de la serie. • Correlación de series: utiliza la fuerte correlación de la serie con su desfase estacional y utiliza el desfase estacional (1ag12) como entrada al modelo.
Usaremos los paquetes dplyr y lubridate para crear esas características, como podemos ver en el siguiente código:
library(dplyr)
library(lubridate)
df <- df %>% mutate(month = factor(lubridate::month(date, label = TRUE), ordered = FALSE),
lag12 = lag(y, n = 12)) %>%
filter(!is.na(lag12))
Luego sumaremos el componente de tendencia y su segundo polinomio (tendencia al cuadrado).
df$trend <- 1:nrow(df)
df$trend_sqr <- df$trend ^ 2
Veamos la estructura del objeto df después de agregar las nuevas características:
str(df)
## 'data.frame': 108 obs. of 6 variables:
## $ date : Date, format: "2011-01-01" "2011-02-01" ...
## $ y : num 836 1007 1277 1174 1081 ...
## $ month : Factor w/ 12 levels "ene","feb","mar",..: 1 2 3 4 5 6 7 8 9 10 ...
## $ lag12 : num 712 793 1084 997 1118 ...
## $ trend : int 1 2 3 4 5 6 7 8 9 10 ...
## $ trend_sqr: num 1 4 9 16 25 36 49 64 81 100 ...
Hay pasos de ingeniería de características adicionales, que en el caso de la serie USVSales no son necesarios, pero pueden ser necesarios en otros casos: • Escalado: esto puede ser necesario cuando las entradas numéricas de la regresión (ya sea la serie misma o al menos uno de los predictores numéricos de la regresión) están en una escala numérica diferente, por ejemplo, hacer una regresión de ingresos en miles de millones de USD con una bandera binaria ( por ejemplo, 0 o 1). Es posible que algunos modelos no encuentren coeficientes o no identifiquen la verdadera relación entre la variable. Algunos métodos de escalado comunes son los siguientes: • Transformación de registros • Normalizar la serie con Z-score • Escalar la entrada entre 0 y 1 Restar la media de la entrada
⚫ Codificación en caliente: Es posible que se requieran variables categóricas cuando el algoritmo no admite el uso de factores (variables categóricas) como entrada. En este caso, para una variable con N categorías, crearemos nuevas N+1 variables: una variable para capturar la pendiente y N variables binarias y una para cada categoría. Dado que los valores de la serie de entradas oscilan entre 800 y 1700, no es necesario escalar la serie y sus entradas. Además, dado que el paquete h20 admite las entradas como factores R, no es necesario aplicar codificación en caliente.
#Capacitación, pruebas y evaluación de modelos. Para comparar los diferentes modelos que probaremos en este capítulo, usaremos las mismas entradas que usamos anteriormente. Esto incluye la ejecución de las mismas particiones de entrenamiento y prueba a lo largo de este capítulo.
Dado que nuestro horizonte de pronóstico es de 12 meses, dejaremos los últimos 12 meses de la serie como particiones de prueba y usaremos el resto de la serie como partición de entrenamiento:
h <- 12
train_df <- df[1:(nrow(df) - h), ]
test_df <- df[(nrow(df) - h + 1):nrow(df), ]
Anteriormente, la variable h representaba el horizonte de pronóstico, que, en este caso, también es igual a la longitud de la partición de prueba. Evaluaremos el rendimiento del modelo en función de la puntuación MAPE en la partición de prueba.
Una de las principales características de los modelos ML es la tendencia a sobreajustarse en el conjunto de entrenamiento. Por lo tanto, se debe esperar que la relación entre la puntuación de error en las particiones de prueba y entrenamiento sea relativamente mayor que los resultados correspondientes de los modelos de series de tiempo tradicionales, como ARIMA, Holt-Winters y la regresión lineal de series de tiempo.
Además de las particiones de entrenamiento y prueba, necesitamos crear las entradas para el pronóstico en sí. Crearemos un dato. encuadre con las fechas de los siguientes 12 meses y construya el resto de características:
forecast_df <- data.frame(date = seq.Date(from = max(df$date) + lubridate::month(1),
length.out = h, by = "month"),
trend = seq(from = max(df$trend) + 1, length.out = h, by = 1))
forecast_df$trend_sqr <- forecast_df$trend ^ 2
# to avoid conflict with the h2o `month` function use the "lubridate::month" to explicly call the month from the lubridate function
forecast_df$month <- factor(lubridate::month(forecast_df$date, label = TRUE), ordered= FALSE)
Por último, pero no menos importante, extraeremos las últimas 12 observaciones de la serie del objeto df y las evaluaremos como los retrasos futuros de la serie:
forecast_df$lag12 <- tail(df$y, 12)
#Modelo de referencia
Introducido en el Capítulo 8, Estrategias de pronóstico, el desempeño de un modelo de pronóstico debe medirse por la tasa de error, principalmente en la partición de prueba, pero también en la partición de entrenamiento. Debe evaluar el desempeño del modelo con respecto a algún modelo de referencia. En los capítulos anteriores, comparamos el pronóstico de la serie de gas estadounidense con el uso del modelo ingenuo estacional.
En este capítulo, dado que utilizamos una familia de modelos de regresión de ML, tiene más sentido utilizar un modelo de regresión como punto de referencia para los modelos de ML. Por lo tanto, entrenaremos un modelo de regresión lineal de series de tiempo, que presentamos en el Capítulo 9, Pronósticos con regresión lineal. Usando las particiones de entrenamiento y prueba que creamos anteriormente, entrenemos el modelo de regresión lineal y evaluemos su rendimiento con las particiones de prueba:
lr <- lm(y ~ month + lag12 + trend + trend_sqr, data = train_df)
Usaremos la función de resumen para revisar los detalles del modelo:
summary(lr)
##
## Call:
## lm(formula = y ~ month + lag12 + trend + trend_sqr, data = train_df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -146.625 -38.997 0.111 39.196 112.577
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 542.93505 72.59490 7.479 7.91e-11 ***
## monthfeb 112.73160 34.16141 3.300 0.001439 **
## monthmar 299.20932 54.24042 5.516 4.03e-07 ***
## monthabr 182.52406 42.53129 4.292 4.88e-05 ***
## monthmay 268.75603 51.28464 5.240 1.24e-06 ***
## monthjun 224.66897 44.26374 5.076 2.41e-06 ***
## monthjul 177.88564 42.21898 4.213 6.49e-05 ***
## monthago 241.63260 47.00693 5.140 1.86e-06 ***
## monthsept 152.99058 37.04199 4.130 8.76e-05 ***
## monthoct 125.16484 35.04896 3.571 0.000601 ***
## monthnov 127.97288 34.18772 3.743 0.000338 ***
## monthdic 278.67994 51.09552 5.454 5.21e-07 ***
## lag12 0.33906 0.10738 3.158 0.002236 **
## trend 7.73667 1.72415 4.487 2.36e-05 ***
## trend_sqr -0.05587 0.01221 -4.576 1.69e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 59.6 on 81 degrees of freedom
## Multiple R-squared: 0.9198, Adjusted R-squared: 0.9059
## F-statistic: 66.36 on 14 and 81 DF, p-value: < 2.2e-16
A continuación, predeciremos los valores correspondientes de la serie en la partición de prueba con la función de predicción usando test_df como entrada:
test_df$yhat <- predict(lr, newdata = test_df)
Now, we can evaluate the models performance on the testing partition:
mape_lr <- mean(abs(test_df$y - test_df$yhat) / test_df$y)
mape_lr
## [1] 0.03594578
#Iniciar un grupo de agua
El paquete h20 se basa en el uso de computación distribuida y paralela para acelerar el tiempo de cómputo y poder escalar para big data. Todo esto se realiza en clústeres de procesamiento distribuido en memoria (basado en la RAM interna de la computadora) o en paralelo (por ejemplo, AWS, Google Cloud, etc.). Por lo tanto, cargaremos el paquete y luego configuraremos el clúster en memoria con h2o. función de inicio:
# Especifica la ruta al directorio de instalación de Java SE JDK
java_home_path <- "C:\\Program Files\\Java\\jdk-22"
# Configura la variable de entorno JAVA_HOME
Sys.setenv(JAVA_HOME = java_home_path)
# Verifica que la variable de entorno JAVA_HOME se haya configurado correctamente
cat("JAVA_HOME:", Sys.getenv("JAVA_HOME"), "\n")
## JAVA_HOME: C:\Program Files\Java\jdk-22
library(h2o)
##
## ----------------------------------------------------------------------
##
## Your next step is to start H2O:
## > h2o.init()
##
## For H2O package documentation, ask for help:
## > ??h2o
##
## After starting H2O, you can use the Web UI at http://localhost:54321
## For more information visit https://docs.h2o.ai
##
## ----------------------------------------------------------------------
##
## Attaching package: 'h2o'
## The following objects are masked from 'package:lubridate':
##
## day, hour, month, week, year
## The following objects are masked from 'package:stats':
##
## cor, sd, var
## The following objects are masked from 'package:base':
##
## %*%, %in%, &&, ||, apply, as.factor, as.numeric, colnames,
## colnames<-, ifelse, is.character, is.factor, is.numeric, log,
## log10, log1p, log2, round, signif, trunc
h2o.init(max_mem_size = "16G")
##
## H2O is not running yet, starting it now...
##
## Note: In case of errors look at the following log files:
## C:\Users\Juan\AppData\Local\Temp\RtmpiQ7sO3\filebf7027fc3f5a/h2o_Juan_started_from_r.out
## C:\Users\Juan\AppData\Local\Temp\RtmpiQ7sO3\filebf7033fe6c47/h2o_Juan_started_from_r.err
##
##
## Starting H2O JVM and connecting: Connection successful!
##
## R is connected to the H2O cluster:
## H2O cluster uptime: 2 seconds 536 milliseconds
## H2O cluster timezone: America/Bogota
## H2O data parsing timezone: UTC
## H2O cluster version: 3.44.0.3
## H2O cluster version age: 4 months and 9 days
## H2O cluster name: H2O_started_from_R_Juan_dfv347
## H2O cluster total nodes: 1
## H2O cluster total memory: 15.98 GB
## H2O cluster total cores: 16
## H2O cluster allowed cores: 16
## H2O cluster healthy: TRUE
## H2O Connection ip: localhost
## H2O Connection port: 54321
## H2O Connection proxy: NA
## H2O Internal Security: FALSE
## R Version: R version 4.3.3 (2024-02-29 ucrt)
## Warning in h2o.clusterInfo():
## Your H2O cluster version is (4 months and 9 days) old. There may be a newer version available.
## Please download and install the latest version from: https://h2o-release.s3.amazonaws.com/h2o/latest_stable.html
h2o.init le permite configurar el tamaño de la memoria del clúster con el argumento max_mem_size. El resultado de la función, como se muestra en el código anterior, proporciona información sobre la configuración del clúster. Cualquier dato que se utilice durante el proceso de entrenamiento y prueba de los modelos mediante el paquete h20 debe cargarse en el propio clúster. La función as.h2o nos permite transformar cualquier objeto data.frame en un cluster h2o:
train_h <- as.h2o(train_df)
## | | | 0% | |======================================================================| 100%
test_h <- as.h2o(test_df)
## | | | 0% | |======================================================================| 100%
Además, transformaremos el objeto predict_df (los valores futuros de las entradas de la serie) en un objeto h2o, que se utilizará para generar, más adelante en este capítulo, el pronóstico final:
forecast_h <- as.h2o(forecast_df)
## | | | 0% | |======================================================================| 100%
Para nuestra comodidad, etiquetaremos los nombres de las variables dependientes e independientes:
x <- c("month", "lag12", "trend", "trend_sqr")
y <- "y"
Ahora que los datos se han cargado en el clúster de trabajo, podemos comenzar el proceso de capacitación.
#Entrenamiento de un modelo de ML
El paquete h20 proporciona un conjunto de herramientas para entrenar y probar modelos de ML. Los enfoques de entrenamiento de modelos más comunes son los siguientes:
• Entrenamiento/prueba: Esto se basa en dividir los datos de entrada en particiones de entrenamiento y prueba asignando la mayor parte de los datos de entrada a la partición de entrenamiento y dejando el resto a la partición de prueba. Como lo implican los nombres de las particiones, el conjunto de entrenamiento se usa para entrenar el modelo, mientras que la partición de prueba se usa para probar su rendimiento con datos nuevos. Las asignaciones típicas son 70/30 (es decir, 70 % de los datos a la partición de entrenamiento y 30 % a la partición de prueba), o aproximadamente cerca de esa proporción, donde la asignación de datos entre las dos particiones debe ser aleatoria. • Capacitación/pruebas/validación: esto es relativamente similar al enfoque anterior, excepto que se agregan particiones de validación. La partición de validación se utiliza durante el proceso de entrenamiento para evaluar el ajuste de los parámetros del modelo. Luego, los modelos sintonizados se prueban en la partición de prueba.
• Validación cruzada: este es uno de los métodos de entrenamiento más populares para modelos de ML, ya que reduce la posibilidad de sobreajuste del modelo. Este método se basa en los siguientes pasos: 1. Dividir el conjunto de entrenamiento, aleatoriamente, en K carpetas 2. Entrenar el modelo K veces, dejando cada vez una carpeta diferente como una partición de prueba y entrenar el modelo con las carpetas K-1 restantes 3. A lo largo del proceso de capacitación, el modelo ajusta los parámetros del modelo. 4. El modelo final se prueba en la partición de prueba.
A lo largo de este capítulo, utilizaremos el enfoque de validación cruzada (CV) para entrenar estos modelos.
#Predicción con el modelo Random Forest Ahora que hemos preparado los datos, creado nuevas funciones y lanzado un clúster de agua, estamos listos para construir nuestro primer modelo de pronóstico con el algoritmo Random Forest (RF). El algoritmo RF es uno de los modelos ML más populares y se puede utilizar tanto para problemas de clasificación como de regresión. En pocas palabras, el algoritmo de RF se basa en un conjunto de múltiples modelos de árboles.
Como su nombre lo indica, tiene dos componentes principales:
• Aleatorio: la entrada para cada modelo de árbol se basa en una muestra aleatoria, junto con el reemplazo tanto de las columnas como de las filas de los datos de entrada. Este método también se conoce como embolsado. • Bosque: la colección de modelos basados en árboles, que eventualmente crea el bosque. Una vez construido el bosque, el algoritmo reúne la predicción de todos los árboles del bosque en una sola salida. Esta combinación de aleatorizar la entrada para cada modelo de árbol y luego promediar sus resultados reduce la probabilidad de sobreajustar el modelo.
RF tiene varios parámetros de ajuste que le permiten controlar el nivel de aleatorización del proceso de muestreo y la profundidad del bosque. El agua. La función randomForest del paquete h20 proporciona el marco para entrenar y ajustar el modelo de RF. Los siguientes son los principales parámetros del agua. Función de bosque aleatorio:
⚫nx Un vector de caracteres con nombres de variables independientes ⚫y: Una cadena, que es el nombre de la variable dependiente ⚫ Training_frame: los datos de entrada deben ser datos. marco del paquete h20 v ⚫nvalidation_frame: Opcional; debe usarse al aplicar un enfoque de entrenamiento con el conjunto de validación ⚫nfolds: El número de K-folds para el proceso CV ⚫nntrees: Número de árboles a crear ⚫nsample_rate: define el número de filas para muestrear por árbol • max_ Depth: establece la profundidad máxima del árbol (número de nodos); A medida que los árboles crecen más profundos, la complejidad del modo aumenta, así como el riesgo de sobreajuste. ⚫ semilla: establece el valor inicial del generador de números aleatorios
Además, esta función tiene varios argumentos de control, lo que le permite controlar el tiempo de ejecución del modelo. Además, le permiten establecer criterios de parada para el modelo si agregar árboles adicionales no mejora el rendimiento del modelo. Estos argumentos son los siguientes:
•stopping_metric: Define el tipo de métrica de error que se utilizará para evaluar si el modelo debe detenerse y construir nuevos árboles ya que no hay mejoras adicionales. •stopping_tolerance: Establece la mejora mínima que se requiere para continuar con el proceso de formación • detener rondas: establece el número de rondas que se deben utilizar antes de detener el entrenamiento.
Comenzaremos con un modelo de RF simplista utilizando 500 árboles y entrenamiento CV de 5 carpetas. Además, agregaremos un criterio de parada para evitar que el modelo se ajuste al modelo mientras no haya cambios significativos en el rendimiento del modelo. En este caso, estableceremos la métrica de parada como RMSE, la tolerancia de parada como 0,0001 y los redondeos de parada a 10:
rf_md <- h2o.randomForest(training_frame = train_h,
nfolds = 5,
x = x,
y = y,
ntrees = 500,
stopping_rounds = 10,
stopping_metric = "RMSE",
score_each_iteration = TRUE,
stopping_tolerance = 0.0001,
seed = 1234)
## | | | 0% | |======================================================================| 100%
La función h2o.randomForest devuelve un objeto con información sobre la configuración de los parámetros del modelo y su rendimiento en el conjunto de entrenamiento (y validación, si se usa). Podemos ver la contribución de las entradas del modelo con la función h2o.varimp_plot. Esta función devuelve un gráfico con la clasificación de la contribución de las variables de entrada al rendimiento del modelo utilizando una escala entre 0 y 1, como se muestra en el siguiente código:
h2o.varimp_plot(rf_md)
Como podemos ver en el gráfico de importancia de la variable anterior, la variable de retraso, lag12, es la más importante para el modelo. Esto no debería ser una sorpresa ya que vimos la fuerte relación entre la serie y su desfase estacional en el análisis de correlación. Inmediatamente después de esto, las variables más importantes son trend_sqr, mes y tendencia.
La salida del modelo contiene (además del modelo en sí) información sobre el rendimiento y los parámetros del modelo. Repasemos el resumen del modelo:
rf_md@model$model_summary
## Model Summary:
## number_of_trees number_of_internal_trees model_size_in_bytes min_depth
## 1 41 41 31592 8
## max_depth mean_depth min_leaves max_leaves mean_leaves
## 1 12 10.04878 45 66 56.70732
Podemos ver que utilizamos sólo 42 árboles de los 500 establecidos por el argumento ntrees. Esto se debe a los parámetros de parada que se utilizaron en el modelo. El siguiente gráfico demuestra el proceso de aprendizaje del modelo en función del número de árboles:
library(plotly)
tree_score <- rf_md@model$scoring_history$training_rmse
plot_ly(x = seq_along(tree_score), y = tree_score,
type = "scatter", mode = "line") %>%
layout(title = "Random Forest Model - Trained Score History",
yaxis = list(title = "RMSE"),
xaxis = list(title = "Num. of Trees"))
## Warning: Ignoring 1 observations
Por último, pero no menos importante, midamos el rendimiento del modelo en la partición de prueba. Usaremos la función h2o.predict para predecir los valores correspondientes de la serie en la partición de prueba:
test_h$pred_rf <- h2o.predict(rf_md, test_h)
## | | | 0% | |======================================================================| 100%
A continuación, transferiremos el marco de datos h20 a un dato. objeto de marco con la función as.data.frame:
test_1 <- as.data.frame(test_h)
Ahora podemos calcular la puntuación MAPE del modelo RF en la partición de prueba:
mape_rf <- mean(abs(test_1$y - test_1$pred_rf) / test_1$y)
mape_rf
## [1] 0.04647164
Como puede ver en la puntuación de error del modelo, el modelo RF con su configuración predeterminada pudo lograr una tasa de error más baja que nuestro modelo de referencia, es decir, el modelo de regresión lineal, con una puntuación MAPE del 3,7 % en comparación con el 4 %. .
Generalmente, cuando se utiliza la opción predeterminada para los parámetros del modelo, el modelo puede funcionar bien, pero es posible que quede algo de espacio para optimización y mejora adicionales con respecto al rendimiento del modelo. Existe una variedad de técnicas para la optimización del modelo y el ajuste de parámetros, como el ajuste manual, la búsqueda en cuadrícula y el ajuste basado en algoritmos. En los capítulos anteriores, analizamos ejemplos de estos tres enfoques, ajustamos manualmente el modelo ARMA en el Capítulo 11, Pronóstico con modelos ARIMA, usamos la función ts_grid en el Capítulo 10, Pronóstico con modelos de suavizado exponencial, para ajustar los parámetros de Holt-Winters. con búsqueda de cuadrícula y automatizó el ajuste del modelo ARIMA (nuevamente en el Capítulo 11, Pronósticos con modelos ARIMA) con el algoritmo auto.arima.
Demostraremos un enfoque de búsqueda de cuadrícula para ajustar un modelo de ML con la función h2o.grid. Más adelante en este capítulo, veremos un enfoque basado en algoritmos para ajustar un modelo N ML con h2o. función automática1. La función h2o.grid le permite establecer un conjunto de valores para algunos parámetros seleccionados y probar su rendimiento en el modelo para identificar los parámetros de ajuste que optimizan el rendimiento del modelo. Los principales argumentos de esta función son los siguientes:
algoritmo: Define el tipo de algoritmo a utilizar en la búsqueda del grid hyper_params: Establece los valores del parámetro de búsqueda criterios_búsqueda: establece roles para la búsqueda, como la métrica de detención del modelo, la cantidad de modelos y el tiempo de ejecución. Cualquiera de los enfoques de entrenamiento del paquete h2o (como CV, entrenamiento con validación) se puede aplicar con la función h2o.grid.
{r}################################### search_criteria_rf <- list( strategy = "RandomDiscrete", stopping_metric = "rmse", stopping_tolerance = 0.0001, stopping_rounds = 10, max_runtime_secs = 60 * 20 )
Empezaremos configurando los parámetros de búsqueda:
hyper_params_rf <- list(mtries = c(2, 3, 4),
sample_rate = c(0.632, 0.8, 0.95),
col_sample_rate_per_tree = c(0.5, 0.9, 1.0),
max_depth = c(seq(1, 30, 3)),
min_rows = c(1, 2, 5, 10))
Aquí, los parámetros que seleccionamos son los siguientes:
•mtries: Define las columnas a seleccionar aleatoriamente en cada nodo del árbol. •sample_rate: establece el muestreo de filas para cada árbol •col_sample_rate_per_tree: Define la frecuencia de muestreo de la columna por árbol. •max_ Depth: especifica la profundidad máxima del árbol. •min_rows: establece el número mínimo de observaciones para una hoja
Cuantos más parámetros agregue o defina para una amplia gama de valores, mayor será la combinación de búsqueda posible. Esto podría llegar fácilmente a cientos de combinaciones, que pueden ejecutarse durante un par de horas (según la potencia informática disponible). Por lo tanto, establecer el argumento criterios de búsqueda es muy importante por razones de eficiencia. El argumento de estrategia del argumento criterios de búsqueda le permite establecer una búsqueda cartesiana (cartesiana) o una búsqueda aleatoria (aleatoria y discreta). La búsqueda cartesiana itera y calcula los modelos para todas las opciones de búsqueda posibles en orden cronológico. Por otro lado, la búsqueda aleatoria selecciona aleatoriamente una combinación de búsqueda de la tabla de búsqueda de la cuadrícula.
Tendría sentido utilizar el método cartesiano cuando el número de combinaciones de búsqueda sea bastante pequeño o cuando no esté limitado en el tiempo de búsqueda. Por otro lado, para una gran cantidad de combinaciones de búsqueda, o cuando tenemos una limitación de tiempo, se recomienda utilizar la búsqueda aleatoria. Por razones de eficiencia, estableceremos una búsqueda aleatoria y restringiremos el tiempo de búsqueda a 20 minutos con max_runtime_sec. Usaremos la misma métrica de parada que usamos anteriormente.
search_criteria_rf <- list(strategy = "RandomDiscrete",
stopping_metric = "rmse",
stopping_tolerance = 0.0001,
stopping_rounds = 10,
max_runtime_secs = 60 * 20)
Después configuramos los argumentos de búsqueda para el h20. función grid, podemos iniciar la búsqueda:
rf2 <- h2o.grid(algorithm = "randomForest",
search_criteria = search_criteria_rf,
hyper_params = hyper_params_rf,
x = x,
y = y,
training_frame = train_h,
ntrees = 5000,
nfolds = 5,
grid_id = "rf_grid",
seed = 1234)
## | | | 0% | | | 1% | |= | 1% | |= | 2% | |== | 2% | |== | 3% | |=== | 4% | |=== | 5% | |==== | 5% | |==== | 6% | |===== | 6% | |===== | 7% | |===== | 8% | |====== | 8% | |====== | 9% | |======= | 9% | |======= | 10% | |======= | 11% | |======== | 11% | |======== | 12% | |========= | 12% | |========= | 13% | |========== | 14% | |========== | 15% | |=========== | 15% | |=========== | 16% | |============ | 17% | |============ | 18% | |============= | 18% | |============= | 19% | |============== | 19% | |============== | 20% | |============== | 21% | |=============== | 21% | |=============== | 22% | |================ | 22% | |================ | 23% | |================ | 24% | |================= | 24% | |================= | 25% | |================== | 25% | |================== | 26% | |=================== | 27% | |=================== | 28% | |==================== | 29% | |===================== | 30% | |====================== | 31% | |======================= | 32% | |======================= | 33% | |======================== | 34% | |======================== | 35% | |========================= | 35% | |========================= | 36% | |========================== | 37% | |=========================== | 38% | |=========================== | 39% | |============================ | 40% | |============================= | 41% | |============================= | 42% | |============================== | 43% | |=============================== | 44% | |=============================== | 45% | |================================ | 46% | |================================= | 47% | |================================= | 48% | |================================== | 48% | |================================== | 49% | |=================================== | 50% | |==================================== | 51% | |==================================== | 52% | |===================================== | 53% | |====================================== | 54% | |====================================== | 55% | |======================================= | 56% | |======================================== | 57% | |======================================== | 58% | |========================================= | 58% | |========================================= | 59% | |========================================== | 60% | |============================================ | 62% | |============================================ | 63% | |============================================= | 64% | |============================================== | 65% | |============================================== | 66% | |=============================================== | 67% | |================================================ | 68% | |================================================= | 69% | |================================================= | 70% | |================================================== | 71%Error in .h2o.doSafeREST(h2oRestApiVersion = h2oRestApiVersion, urlSuffix = urlSuffix, :
## Unexpected CURL error: Timeout was reached: [localhost:54321] Connection timeout after 10139 ms
## [1] "Job request failed Unexpected CURL error: Timeout was reached: [localhost:54321] Connection timeout after 10139 ms, will retry after 3s."
## | |=================================================== | 73% | |=================================================== | 74% | |==================================================== | 74% | |===================================================== | 75% | |===================================================== | 76% | |====================================================== | 77% | |======================================================= | 78% | |======================================================= | 79% | |======================================================== | 79% | |======================================================== | 80% | |======================================================== | 81% | |========================================================= | 81% | |========================================================= | 82% | |========================================================== | 82% | |========================================================== | 83% | |=========================================================== | 84% | |=========================================================== | 85% | |============================================================ | 85% | |============================================================ | 86% | |============================================================= | 87% | |============================================================== | 89% | |=============================================================== | 90% | |=============================================================== | 91% | |================================================================ | 91% | |================================================================ | 92% | |================================================================= | 92% | |================================================================= | 93% | |================================================================== | 94% | |================================================================== | 95% | |=================================================================== | 95% | |==================================================================== | 96% | |==================================================================== | 97% | |==================================================================== | 98% | |===================================================================== | 98% | |===================================================================== | 99% | |======================================================================| 99% | |======================================================================| 100%
Notarás que mantuvimos el mismo enfoque de capacitación, es decir, 5 carpetas de CV y la cantidad de árboles como 5000.
Configurar una gran cantidad de árboles con un modelo basado en árboles como RF o GBM, junto con una métrica de detención, garantizará que el modelo seguirá construyendo árboles adicionales hasta que cumpla con los criterios de detención. Por lo tanto, establecer los criterios de parada juega un papel crítico tanto en la eficiencia del modelo como en sus resultados.
Ahora extraeremos los resultados de la cuadrícula, ordenaremos los modelos por su puntuación RMSE y extraeremos el modelo principal:
rf2_grid_search <- h2o.getGrid(grid_id = "rf_grid",
sort_by = "rmse",
decreasing = FALSE)
rf_grid_model <- h2o.getModel(rf2_grid_search@model_ids[[1]])
Probemos el modelo en la partición de prueba y evaluemos su rendimiento:
test_h$rf_grid <- h2o.predict(rf_grid_model, test_h)
## | | | 0% | |======================================================================| 100%
mape_rf2 <- mean(abs(test_1$y - test_1$rf_grid) / test_1$y)
mape_rf2
## [1] NaN
El paso de optimización adicional contribuyó a aumentar la precisión del modelo, con una puntuación MAPE de 3,33 % en comparación con 3,7 % y 4 % para el primer modelo de RF que entrenamos y el modelo de regresión lineal, respectivamente. El siguiente gráfico proporciona una vista adicional del rendimiento del modelo:
{r}# plot_ly(data = test_1) %>% add_lines(x = ~ date, y = ~y, name = "Actual") %>% add_lines(x = ~ date, y = ~ yhat, name = "Linear Regression", line = list(dash = "dot")) %>% add_lines(x = ~ date, y = ~ pred_rf, name = "Random Forest", line = list(dash = "dash")) %>% add_lines(x = ~ date, y = ~ rf_grid, name = "Random Forest (grid)", line = list(dash = "dash")) %>% layout(title = "Total Vehicle Sales - Actual vs. Prediction (Random Forest)", yaxis = list(title = "Thousands of Units"), xaxis = list(title = "Month"))
Ahora, veamos el modelo GBM.
#Predicciones con el modelo GBM
El algoritmo GBM es otro modelo basado en conjuntos y árboles. Utiliza el impulso enfoque para entrenar diferentes subconjuntos de datos, y repite el entrenamiento de subconjuntos que tenía el modelo con una alta tasa de error. Esto permite que el modelo aprenda de los errores pasados y mejore el poder predictivo del modelo.
Los principales argumentos del modelo GBM son los siguientes
•x: Un vector de caracteres con los nombres de las variables independientes. •y: Una cadena, que es el nombre de la variable dependiente. •training_frame: Los datos de entrada deben ser datos. marco del paquete h20. •validation_frame: Opcional; esto debe usarse cuando aplicamos un enfoque de entrenamiento con el conjunto de validación. •nfolds: El número de K-folds para el proceso CV. •ntrees: Número de árboles a crear. •sample_rate: Define el número de filas a muestrear por árbol. •max_ Depth: establece la profundidad máxima del árbol (número de nodos). A medida que los árboles crecen más profundos, aumenta la complejidad del modo, así como el riesgo de sobreajuste. •tasa de aprendizaje: Define la tasa de aprendizaje del modelo con valores entre 0 y 1. La tasa predeterminada es 0,1. A medida que la tasa de aprendizaje se acerca a 0, mejor El aprendizaje del modelo será, con el costo de un mayor tiempo de cálculo, junto con una mayor cantidad de árboles. •semilla: establece el valor inicial del generador de números aleatorios.
El siguiente ejemplo demuestra el uso de la función h2o.gbom para entrenar el modelo GBM con los mismos datos de entrada que usamos anteriormente:
gbm_md <- h2o.gbm(
training_frame = train_h,
nfolds = 5,
x = x,
y = y,
max_depth = 20,
distribution = "gaussian",
ntrees = 500,
learn_rate = 0.1,
score_each_iteration = TRUE
)
## | | | 0% | |================ | 22% | |========================================================== | 83% | |=================================================================== | 95% | |======================================================================| 100%
De manera similar al modelo RF, podemos revisar el rango de importancia de las variables del modelo con la función h2o.varimp_plot:
h2o.varimp_plot(gbm_md)
Para RF, el modelo GBM clasifica la variable de retraso12 como la más importante para el modelo. Probemos el rendimiento del modelo en el conjunto de prueba:
test_h$pred_gbm <- h2o.predict(gbm_md, test_h)
## | | | 0% | |======================================================================| 100%
test_1 <- as.data.frame(test_h)
mape_gbm <- mean(abs(test_1$y - test_1$pred_gbm) / test_1$y)
mape_gbm
## [1] 0.03857898
El modelo GBM obtuvo el MAPE más bajo entre los modelos que hemos probado hasta ahora con una tasa de error del 2,7 %, en comparación con el 3,3 % y el 4 % del modelo RF (con búsqueda de cuadrícula) y el modelo de regresión lineal, respectivamente. Sería un gran ejercicio aplicar una búsqueda en cuadrícula en el modelo GBM y probar si se pueden lograr mejoras adicionales.
Visualicemos los resultados y comparemos la predicción con la predicción real y de referencia:
plot_ly(data = test_1) %>%
add_lines(x = ~ date, y = ~y, name = "Actual") %>%
add_lines(x = ~ date, y = ~ yhat, name = "Linear Regression", line = list(dash = "dot")) %>%
add_lines(x = ~ date, y = ~ pred_gbm, name = "Gradient Boosting Machine", line = list(dash = "dash")) %>%
layout(title = "Total Vehicle Sales - Actual vs. Prediction (Gradient Boosting Machine)",
yaxis = list(title = "Thousands of Units"),
xaxis = list(title = "Month"))
#Previsión con el modelo AutoML
Hasta ahora, en este capítulo, hemos analizado dos enfoques de modelado: el primero utiliza la configuración predeterminada del algoritmo con los modelos RF y GBM, y el segundo aplica una búsqueda de cuadrícula con el modelo RF. Echemos un vistazo a un tercer enfoque para ajustar los modelos de ML. Aquí usaremos la función h2o.automl. El agua. La función automl proporciona un enfoque automatizado para entrenar, ajustar y probar múltiples algoritmos de ML antes de seleccionar el modelo que funcionó mejor según la evaluación del modelo. Utiliza algoritmos como RF, GBM, DL y otros que utilizan diferentes enfoques de ajuste. De manera similar, la función h2o.grid puede aplicar cualquiera de los enfoques de entrenamiento (CV, entrenamiento con validación, etc.) durante el proceso de entrenamiento de los modelos. Usemos la misma entrada que antes y entrenemos el modelo de pronóstico:
autoML1 <- h2o.automl(training_frame = train_h,
x = x,
y = y,
nfolds = 5,
max_runtime_secs = 60*20,
seed = 1234)
## | | | 0%
## 22:26:42.770: AutoML: XGBoost is not available; skipping it.
## 22:26:46.502: _min_rows param, The dataset size is too small to split for min_rows=100.0: must have at least 200.0 (weighted) rows, but have only 96.0. | |============== | 20% | |================== | 26% | |=================== | 27% | |============================ | 40%Error in .h2o.__checkConnectionHealth() :
## H2O connection has been severed. Cannot connect to instance at http://localhost:54321/
## Timeout was reached: [localhost:54321] Connection timeout after 11565 ms
## | |============================ | 41% | |============================= | 41% | |============================= | 42% | |============================== | 42% | |============================== | 43% | |=============================== | 44% | |================================= | 47% | |=================================== | 50% | |==================================== | 52% | |====================================== | 54% | |======================================== | 57% | |=========================================== | 61% | |============================================= | 64% | |============================================== | 65% | |=============================================== | 67% | |================================================ | 69% | |================================================= | 70% | |================================================= | 71% | |================================================== | 71% | |================================================== | 72% | |=================================================== | 72% | |=================================================== | 73% | |==================================================== | 74% | |==================================================== | 75% | |===================================================== | 75% | |===================================================== | 76% | |====================================================== | 77% | |====================================================== | 78% | |======================================================= | 78% | |======================================================= | 79% | |======================================================== | 79% | |======================================================== | 80% | |======================================================== | 81% | |========================================================= | 81% | |========================================================= | 82% | |========================================================== | 82% | |========================================================== | 83% | |========================================================== | 84% | |=========================================================== | 84% | |=========================================================== | 85% | |============================================================ | 85% | |============================================================ | 86% | |============================================================= | 87% | |============================================================= | 88% | |============================================================== | 88% | |============================================================== | 89% | |=============================================================== | 90% | |================================================================ | 91% | |================================================================ | 92% | |================================================================= | 92% | |================================================================= | 93% | |================================================================== | 94% | |================================================================== | 95% | |=================================================================== | 95% | |=================================================================== | 96% | |==================================================================== | 97% | |==================================================================== | 98% | |======================================================================| 100%
CONSEJO Podemos configurar el tiempo de ejecución de la función. Un tiempo de ejecución más prolongado podría producir mejores resultados. En el ejemplo anterior, el tiempo de ejecución de la función se estableció en 20 minutos. La función devuelve una lista con la clasificación de todos los modelos probados:
autoML1@leaderboard
## model_id rmse mse
## 1 StackedEnsemble_BestOfFamily_5_AutoML_1_20240429_222642 60.04994 3605.996
## 2 StackedEnsemble_BestOfFamily_6_AutoML_1_20240429_222642 61.25732 3752.459
## 3 DeepLearning_grid_1_AutoML_1_20240429_222642_model_50 61.87294 3828.261
## 4 GBM_grid_1_AutoML_1_20240429_222642_model_358 62.01144 3845.419
## 5 StackedEnsemble_BestOfFamily_3_AutoML_1_20240429_222642 62.18865 3867.428
## 6 GBM_grid_1_AutoML_1_20240429_222642_model_397 62.27158 3877.750
## mae rmsle mean_residual_deviance
## 1 49.19556 0.04536209 3605.996
## 2 49.89074 0.04650614 3752.459
## 3 50.04104 0.04703857 3828.261
## 4 50.87293 0.04743618 3845.419
## 5 50.74415 0.04676779 3867.428
## 6 49.25747 0.04872755 3877.750
##
## [393 rows x 6 columns]
Puedes ver que en este caso, los modelos superiores son modelos DL con diferentes configuraciones de ajuste. Seleccionaremos el modelo líder y probaremos su rendimiento en el conjunto de pruebas:
test_h$pred_autoML <- h2o.predict(autoML1@leader, test_h)
## | | | 0% | |======================================================================| 100%
test_1 <- as.data.frame(test_h)
mape_autoML <- mean(abs(test_1$y - test_1$pred_autoML) / test_1$y)
mape_autoML
## [1] 0.04272069
El modelo líder de producción de h2o.automl logró una puntuación MAPE del 3,48%. Aunque los modelos anteriores lograron una puntuación más alta, todavía proporciona un aumento significativo con respecto al modelo base. Visualicemos la predicción del modelo en las particiones de prueba con respecto a la predicción real y de referencia:
plot_ly(data = test_1) %>%
add_lines(x = ~ date, y = ~y, name = "Actual") %>%
add_lines(x = ~ date, y = ~ yhat, name = "Linear Regression", line = list(dash = "dot")) %>%
add_lines(x = ~ date, y = ~ pred_autoML, name = "autoML", line = list(dash = "dash")) %>%
layout(title = "Total Vehicle Sales - Actual vs. Prediction (Auto ML Model)",
yaxis = list(title = "Thousands of Units"),
xaxis = list(title = "Month"))
Seleccionando el modelo final Ahora que hemos terminado el proceso de entrenamiento y prueba de los modelos, es hora de finalizar el proceso y elegir el modelo a pronosticar con la serie. Entrenamos los siguientes modelos: ⚫ Modelo de referencia: modelo de regresión lineal con puntuación MAPE del 4% en la partición de prueba ⚫ RF: uso de parámetros de ajuste predeterminados con una puntuación MAPE del 3,74 % en la partición de prueba ⚫ RF: uso de la búsqueda de cuadrícula para ajustar los parámetros del modelo con una puntuación MAPE del 3,33 % en la partición de prueba ⚫ GBM: utilizando los parámetros de ajuste predeterminados con una puntuación MAPE del 2,75% en el partición de prueba • AutoML: selección de un modelo de aprendizaje profundo con una puntuación MAPE del 3,48 % en la prueba dividir Dado que todos estos modelos lograron mejores resultados que el modelo de referencia, podemos descartar el modelo de referencia. Además, dado que el segundo modelo de RF (con búsqueda de cuadrícula) logró mejores resultados que el primero, no tiene sentido conservar el primer modelo. Esto nos deja con tres modelos de pronóstico, es decir, RF (con búsqueda de cuadrícula), GBM y AutoML. Generalmente, dado que el modelo GBM logró los mejores resultados MAPE, lo seleccionaremos. Sin embargo, siempre es bueno trazar el pronóstico real y comprobar cómo se ve el pronóstico real. Antes de trazar los resultados, usemos estos tres modelos para pronosticar los próximos 12 meses usando el pronóstico del marco de datos que creamos en las secciones Pronóstico con el modelo Random Forest y Pronóstico con el modelo GBM:
forecast_h$pred_gbm <- h2o.predict(gbm_md, forecast_h)
## | | | 0% | |======================================================================| 100%
forecast_h$pred_rf <- h2o.predict(rf_grid_model, forecast_h)
## | | | 0% | |======================================================================| 100%
forecast_h$pred_automl <- h2o.predict(autoML1@leader, forecast_h)
## | | | 0% | |======================================================================| 100%
final_forecast <- as.data.frame(forecast_h)
plot_ly(x = df$date, y = df$y,
type = "scatter",
mode = "line",
name = "Actual") %>%
add_lines(x = final_forecast$date, y = final_forecast$pred_rf, name = "Random Forest") %>%
add_lines(x = final_forecast$date, y = final_forecast$pred_gbm, name = "GBM") %>%
add_lines(x = final_forecast$date, y = final_forecast$pred_automl, name = "Auto ML") %>%
layout(title = "Total Vehicle Sales - Final Forecast",
yaxis = list(title = "Thousands of Units", range = c(1100, 1750)),
xaxis = list(title = "Month", range = c(as.Date("2016-01-01"), as.Date("2020-01-01"))))
Parece que los tres modelos capturan el componente estacional de la serie de ventas de vehículos. Sin embargo, parece que la oscilación de AutoML es mayor con respecto a uno de los modelos RF y GBM. Por lo tanto, tendría sentido seleccionar los modelos GBM o RF como pronóstico final. Un enfoque más conservador sería crear y combinar los tres pronósticos ponderándolos según el promedio regular. Por ejemplo, puede utilizar una función simple para probar el promedio diferente de diferentes modelos y seleccionar la combinación que minimice la tasa de error de pronóstico en el conjunto de prueba