Local Global Trend (LGT)

In this section, we will cover:

  • LGT model structure

  • difference between DLT and LGT

  • syntax to call LGT classes with different estimation methods

LGT stands for Local and Global Trend and is a refined model from Rlgt (Smyl et al., 2019). The main difference is that LGT is an additive form taking log-transformation response as the modeling response. This essentially converts the model into a multicplicative with some advantages (Ng and Wang et al., 2020). However, one drawback of this approach is that negative response values are not allowed due to the existence of the global trend term and because of that we start to deprecate the support of regression of this model.

[1]:
%matplotlib inline

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

import orbit
from orbit.models import LGT
from orbit.diagnostics.plot import plot_predicted_data
from orbit.diagnostics.plot import plot_predicted_components
from orbit.utils.dataset import load_iclaims
[2]:
print(orbit.__version__)
1.1.0dev

Model Structure

\[\begin{split}\begin{align*} y_{t} &= \mu_t + s_t + \epsilon_t \\ \mu_t &= l_{t-1} + \xi_1 b_{t-1} + \xi_2 l_{t-1}^{\lambda}\\ \epsilon_t &~\sim \mathtt{Student}(\nu, 0, \sigma)\\ \sigma &~\sim \mathtt{HalfCauchy}(0, \gamma_0) \end{align*}\end{split}\]

with the update process,

\[\begin{split}\begin{align*} l_t &= \rho_l(y_t - s_t) + (1-\rho_l)l_{t-1}\\ b_t &= \rho_b(l_t - l_{t-1}) + (1-\rho_b)b_{t-1}\\ s_{t+m} &= \rho_s(y_t - l_t ) + (1-\rho_s)s_t \end{align*}\end{split}\]

Unlike DLT model which has a deterministic trend, LGT introduces a hybrid trend where it consists of

  • local trend takes on a fraction \(\xi_1\) rather than a damped factor

  • global trend is with a auto-regrssive term \(\xi_2\) and a power term \(\lambda\)

We will continue to use the iclaims data with 52 weeks train-test split.

[3]:
# load data
df = load_iclaims()
# define date and response column
date_col = 'week'
response_col = 'claims'
df.dtypes
test_size = 52
train_df = df[:-test_size]
test_df = df[-test_size:]

LGT Model

In orbit, we provide three methods for LGT model estimation and inferences, which are * MAP * MCMC (also providing the point estimate method, mean or median), which is also the default * SVI

Orbit follows a sklearn style model API. We can create an instance of the Orbit class and then call its fit and predict methods.

In this notebook, we will only cover MAP and MCMC methods. Refer to this notebook for the pyro estimation.

LGT - MAP

To use MAP, specify the estimator as stan-map.

[4]:
lgt = LGT(
    response_col=response_col,
    date_col=date_col,
    estimator='stan-map',
    seasonality=52,
    seed=8888,
)
[5]:
%%time
lgt.fit(df=train_df)
CPU times: user 174 ms, sys: 8.18 ms, total: 182 ms
Wall time: 414 ms
[6]:
predicted_df = lgt.predict(df=test_df)
[7]:
_ = plot_predicted_data(training_actual_df=train_df, predicted_df=predicted_df,
                        date_col=date_col, actual_col=response_col,
                        test_actual_df=test_df, title='Prediction with LGTMAP Model')
../_images/tutorials_lgt_16_0.png

LGT - MCMC

To use MCMC sampling, specify the estimator as stan-mcmc (the default).

  • By dedault, full Bayesian samples will be used for the predictions: for each set of parameter posterior samples, the prediction will be conducted once and the final predictions are aggregated over all the results. To be specific, the final predictions will be the median (aka 50th percentile) along with any additional percentiles provided.

  • One can also specify point_method (either mean or median) via .fit to have the point estimate: the parameter posterior samples are aggregated first (mean or median) then conduct the prediction once.

LGT - full

[8]:
lgt = LGT(
    response_col=response_col,
    date_col=date_col,
    seasonality=52,
    seed=8888,
)
[9]:
%%time
lgt.fit(df=train_df)
WARNING:pystan:Maximum (flat) parameter count (1000) exceeded: skipping diagnostic tests for n_eff and Rhat.
To run all diagnostics call pystan.check_hmc_diagnostics(fit)
WARNING:pystan:1 of 100 iterations ended with a divergence (1 %).
WARNING:pystan:Try running with adapt_delta larger than 0.8 to remove the divergences.
CPU times: user 75 ms, sys: 70.4 ms, total: 145 ms
Wall time: 7.24 s
[10]:
predicted_df = lgt.predict(df=test_df)
[11]:
predicted_df.tail(5)
[11]:
week prediction_5 prediction prediction_95
47 2018-05-27 12.099949 12.232984 12.330652
48 2018-06-03 12.060341 12.173674 12.293869
49 2018-06-10 12.118473 12.262561 12.408782
50 2018-06-17 12.097858 12.239122 12.341881
51 2018-06-24 12.193468 12.281816 12.383324
[12]:
_ = plot_predicted_data(training_actual_df=train_df, predicted_df=predicted_df,
                    date_col=lgt.date_col, actual_col=lgt.response_col,
                    test_actual_df=test_df, title='Prediction with LGTFull Model')
../_images/tutorials_lgt_24_0.png

LGT - point estimate

[13]:
lgt = LGT(
    response_col=response_col,
    date_col=date_col,
    seasonality=52,
    seed=8888,
)
[14]:
%%time
lgt.fit(df=train_df, point_method='mean')
WARNING:pystan:Maximum (flat) parameter count (1000) exceeded: skipping diagnostic tests for n_eff and Rhat.
To run all diagnostics call pystan.check_hmc_diagnostics(fit)
WARNING:pystan:1 of 100 iterations ended with a divergence (1 %).
WARNING:pystan:Try running with adapt_delta larger than 0.8 to remove the divergences.
CPU times: user 74.3 ms, sys: 78.1 ms, total: 152 ms
Wall time: 7.15 s
[15]:
predicted_df = lgt.predict(df=test_df)
[16]:
predicted_df.tail(5)
[16]:
week prediction
47 2018-05-27 12.204437
48 2018-06-03 12.139519
49 2018-06-10 12.233509
50 2018-06-17 12.200898
51 2018-06-24 12.247008
[17]:
_ = plot_predicted_data(training_actual_df=train_df, predicted_df=predicted_df,
                    date_col=lgt.date_col, actual_col=lgt.response_col,
                    test_actual_df=test_df, title='Predictibon with LGTAggregated Model')
../_images/tutorials_lgt_30_0.png