```python
from google.colab import drive
drive.mount('/content/drive')
```

    Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).

## Time-Series Forecasting
After the analysis of the time-Series has been performed in [this notebook](https://github.com/DrUkachi/online_retail_project/blob/main/online_retail_time_series_analysis.ipynb)

We can then confidently use some time-series algorithms to forecast the aggregated weekly sales for the next for weeks.

Several methods can be used to perform time-series, however model selection should be based on the analysis of the data.

In developing time-series models it is important to choose whether forecasts will depend only on the series itself (univariate) or whether predictions should be made on additional attributes (regressors). In this section, these two methods will be used to make the forecast.

```python

!pip install pmdarima
!pip install bayesian-optimization
!pip install gluonts
!pip install mxnet
```

    Collecting pmdarima
      Downloading pmdarima-1.8.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (1.4 MB)
         |████████████████████████████████| 1.4 MB 9.3 MB/s
    Requirement already satisfied: setuptools!=50.0.0,>=38.6.0 in /usr/local/lib/python3.7/dist-packages (from pmdarima) (57.4.0)
    Requirement already satisfied: Cython!=0.29.18,>=0.29 in /usr/local/lib/python3.7/dist-packages (from pmdarima) (0.29.24)
    Collecting statsmodels!=0.12.0,>=0.11
      Downloading statsmodels-0.13.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (9.8 MB)
         |████████████████████████████████| 9.8 MB 54.4 MB/s
    Requirement already satisfied: numpy>=1.19.3 in /usr/local/lib/python3.7/dist-packages (from pmdarima) (1.19.5)
    Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from pmdarima) (1.0.1)
    Requirement already satisfied: scikit-learn>=0.22 in /usr/local/lib/python3.7/dist-packages (from pmdarima) (0.22.2.post1)
    Requirement already satisfied: pandas>=0.19 in /usr/local/lib/python3.7/dist-packages (from pmdarima) (1.1.5)
    Requirement already satisfied: scipy>=1.3.2 in /usr/local/lib/python3.7/dist-packages (from pmdarima) (1.4.1)
    Requirement already satisfied: urllib3 in /usr/local/lib/python3.7/dist-packages (from pmdarima) (1.24.3)
    Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas>=0.19->pmdarima) (2018.9)
    Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.7/dist-packages (from pandas>=0.19->pmdarima) (2.8.2)
    Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.7.3->pandas>=0.19->pmdarima) (1.15.0)
    Collecting patsy>=0.5.2
      Downloading patsy-0.5.2-py2.py3-none-any.whl (233 kB)
         |████████████████████████████████| 233 kB 75.1 MB/s
    Installing collected packages: patsy, statsmodels, pmdarima
      Attempting uninstall: patsy
        Found existing installation: patsy 0.5.1
        Uninstalling patsy-0.5.1:
          Successfully uninstalled patsy-0.5.1
      Attempting uninstall: statsmodels
        Found existing installation: statsmodels 0.10.2
        Uninstalling statsmodels-0.10.2:
          Successfully uninstalled statsmodels-0.10.2
    Successfully installed patsy-0.5.2 pmdarima-1.8.3 statsmodels-0.13.0
    Collecting bayesian-optimization
      Downloading bayesian-optimization-1.2.0.tar.gz (14 kB)
    Requirement already satisfied: numpy>=1.9.0 in /usr/local/lib/python3.7/dist-packages (from bayesian-optimization) (1.19.5)
    Requirement already satisfied: scipy>=0.14.0 in /usr/local/lib/python3.7/dist-packages (from bayesian-optimization) (1.4.1)
    Requirement already satisfied: scikit-learn>=0.18.0 in /usr/local/lib/python3.7/dist-packages (from bayesian-optimization) (0.22.2.post1)
    Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn>=0.18.0->bayesian-optimization) (1.0.1)
    Building wheels for collected packages: bayesian-optimization
      Building wheel for bayesian-optimization (setup.py) ... done
      Created wheel for bayesian-optimization: filename=bayesian_optimization-1.2.0-py3-none-any.whl size=11685 sha256=59acfb19e8ff13808b220623b2d791efbee4f1c1f5716619f4ff73433d653271
      Stored in directory: /root/.cache/pip/wheels/fd/9b/71/f127d694e02eb40bcf18c7ae9613b88a6be4470f57a8528c5b
    Successfully built bayesian-optimization
    Installing collected packages: bayesian-optimization
    Successfully installed bayesian-optimization-1.2.0
    Collecting gluonts
      Downloading gluonts-0.8.1-py3-none-any.whl (2.1 MB)
         |████████████████████████████████| 2.1 MB 7.0 MB/s
    Requirement already satisfied: toolz~=0.10 in /usr/local/lib/python3.7/dist-packages (from gluonts) (0.11.1)
    Requirement already satisfied: pandas~=1.0 in /usr/local/lib/python3.7/dist-packages (from gluonts) (1.1.5)
    Requirement already satisfied: numpy~=1.16 in /usr/local/lib/python3.7/dist-packages (from gluonts) (1.19.5)
    Collecting pydantic~=1.1
      Downloading pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl (10.1 MB)
         |████████████████████████████████| 10.1 MB 45.4 MB/s
    Requirement already satisfied: holidays>=0.9 in /usr/local/lib/python3.7/dist-packages (from gluonts) (0.10.5.2)
    Requirement already satisfied: matplotlib~=3.0 in /usr/local/lib/python3.7/dist-packages (from gluonts) (3.2.2)
    Requirement already satisfied: tqdm~=4.23 in /usr/local/lib/python3.7/dist-packages (from gluonts) (4.62.3)
    Collecting typing-extensions~=3.10.0.0
      Downloading typing_extensions-3.10.0.2-py3-none-any.whl (26 kB)
    Requirement already satisfied: korean-lunar-calendar in /usr/local/lib/python3.7/dist-packages (from holidays>=0.9->gluonts) (0.2.1)
    Requirement already satisfied: hijri-converter in /usr/local/lib/python3.7/dist-packages (from holidays>=0.9->gluonts) (2.2.1)
    Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from holidays>=0.9->gluonts) (1.15.0)
    Requirement already satisfied: convertdate>=2.3.0 in /usr/local/lib/python3.7/dist-packages (from holidays>=0.9->gluonts) (2.3.2)
    Requirement already satisfied: python-dateutil in /usr/local/lib/python3.7/dist-packages (from holidays>=0.9->gluonts) (2.8.2)
    Requirement already satisfied: pytz>=2014.10 in /usr/local/lib/python3.7/dist-packages (from convertdate>=2.3.0->holidays>=0.9->gluonts) (2018.9)
    Requirement already satisfied: pymeeus<=1,>=0.3.13 in /usr/local/lib/python3.7/dist-packages (from convertdate>=2.3.0->holidays>=0.9->gluonts) (0.5.11)
    Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib~=3.0->gluonts) (1.3.2)
    Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib~=3.0->gluonts) (0.10.0)
    Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib~=3.0->gluonts) (2.4.7)
    Installing collected packages: typing-extensions, pydantic, gluonts
      Attempting uninstall: typing-extensions
        Found existing installation: typing-extensions 3.7.4.3
        Uninstalling typing-extensions-3.7.4.3:
          Successfully uninstalled typing-extensions-3.7.4.3
    ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
    tensorflow 2.6.0 requires typing-extensions~=3.7.4, but you have typing-extensions 3.10.0.2 which is incompatible.
    Successfully installed gluonts-0.8.1 pydantic-1.8.2 typing-extensions-3.10.0.2
    Collecting mxnet
      Downloading mxnet-1.8.0.post0-py2.py3-none-manylinux2014_x86_64.whl (46.9 MB)
         |████████████████████████████████| 46.9 MB 52 kB/s
    Collecting graphviz<0.9.0,>=0.8.1
      Downloading graphviz-0.8.4-py2.py3-none-any.whl (16 kB)
    Requirement already satisfied: requests<3,>=2.20.0 in /usr/local/lib/python3.7/dist-packages (from mxnet) (2.23.0)
    Requirement already satisfied: numpy<2.0.0,>1.16.0 in /usr/local/lib/python3.7/dist-packages (from mxnet) (1.19.5)
    Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests<3,>=2.20.0->mxnet) (1.24.3)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests<3,>=2.20.0->mxnet) (3.0.4)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests<3,>=2.20.0->mxnet) (2021.5.30)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests<3,>=2.20.0->mxnet) (2.10)
    Installing collected packages: graphviz, mxnet
      Attempting uninstall: graphviz
        Found existing installation: graphviz 0.10.1
        Uninstalling graphviz-0.10.1:
          Successfully uninstalled graphviz-0.10.1
    Successfully installed graphviz-0.8.4 mxnet-1.8.0.post0

```python
import pickle
import warnings
from math import sqrt

import lightgbm as lgb
import matplotlib as mpl
import numpy as np
import pandas as pd  # Basic library for all of our dataset operations
import pmdarima as pm
import tensorflow as tf
import xgboost as xgb
from bayes_opt import BayesianOptimization
from fbprophet import Prophet
from gluonts.dataset.common import ListDataset
from gluonts.evaluation.backtest import make_evaluation_predictions
from gluonts.model.deepar import DeepAREstimator
from gluonts.mx.trainer import Trainer
from matplotlib import pyplot as plt
from sklearn import linear_model, svm
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import make_scorer, mean_squared_error
from sklearn.model_selection import GridSearchCV, cross_val_score
from sklearn.neighbors import KNeighborsRegressor
from sklearn.preprocessing import StandardScaler
from statsmodels.tsa.ar_model import AR
from statsmodels.tsa.arima_model import ARIMA, ARMA
from statsmodels.tsa.holtwinters import ExponentialSmoothing, SimpleExpSmoothing
from statsmodels.tsa.statespace.sarimax import SARIMAX
from tqdm import tqdm
```

    /usr/local/lib/python3.7/dist-packages/gluonts/json.py:46: UserWarning:

    Using `json`-module for json-handling. Consider installing one of `orjson`, `ujson` to speed up serialization and deserialization.

```python
CLEANED_DATASET_PATH = "/content/drive/MyDrive/Colab_Notebooks/online_retail/further_cleaned_retail_data.csv"
cleaned_data = pd.read_csv(CLEANED_DATASET_PATH)
cleaned_data.drop(columns=['Unnamed: 0'], inplace=True)

# Generate the aggregated weekly sales
dated_retail_data = cleaned_data.copy()
dated_retail_data["Date"] = pd.to_datetime(cleaned_data["InvoiceDate"]) - pd.to_timedelta(7, unit="d")
dated_retail_data.index = dated_retail_data["Date"]
#resampling to get the weekly sales and returns
weekly_sale = dated_retail_data[dated_retail_data['Quantity']>0].Quantity.resample('W').sum()
weekly_retail = dated_retail_data[dated_retail_data['Quantity']<0].Quantity.resample('W').sum().abs()

series = weekly_sale
```

## Preparing the Data

```python
df_series = pd.DataFrame(series)
```

## Defining Metrics
Metrics are used to measure the quality of predictions. To measure the accuracy of time-series forecasts, several methods exists. However, in this project there will be a focus the following metrisc:

1. **R-squred**: This is also known as the coefficient of determination and it is denoted by:

\begin{align}
        R^2 = 1 -\frac{SS_{res}} {SS_{tot}}
    \end{align}

  where,
$SS_{res}$ is the sum of squared residuals from the predicted values, and $SS_{tot}$ is the sum of squared deviations of the dependent variable from the sample mean.

2. **Mean Absolute Error**: This is the average of the absolute values of the deviation. This type of error measurement is useful when measuring prediction errors in the same unit as the original series.

\begin{align}
        MAE = \frac{\sum_{i=0}^n |y_i - \hat{y}_i|}{n}.
    \end{align}

3. **Root Mean Squared Error (RMSE)**: This is the square root of the mean of the square of all the error. TThe use of RMSE is very common, and it is considered an excellent general-purpose error metric for numerical predictions.

\begin{align}
        RMSE = \sqrt{\frac{1}{n}\sum_{i=1}^n(S_i- O_i)^2}
    \end{align}

where $O_i$ are the observations, $S_i$ predicted values of a variable, and $n$ is the number of observations avaialable for analysis. RMSE is a good measure for accuracy, but mainly used to compare prediction errors of different models or model configurations for a particular set of variable and not between variables because it is scale dependent.

4. **Mean Absolute Percentage Error**:The mean absolute percentage error (MAPE), also known as mean absolute percentage deviation (MAPD), is a measure of prediction accuracy of a forecasting method in statistics. It usually expresses the accuracy as a ratio defined by the formula:

\begin{align}
        MAPE = \frac{100}{n}\sum_{i=1}^n|{\frac{A_t - F_t}{A_t}}|
    \end{align}

where $A_t$ is the actual value and $F_t$ is the forecast value. Their difference is divided by the actual value $A_t$. The absolute value in this ratio is summed for every forecasted point in time and divided by the number of fitted points $n$.

```python
import numpy as np
from sklearn.metrics import r2_score

EPSILON = 1e-10

def _error(actual: np.ndarray, predicted: np.ndarray):
    """Simple error"""
    return actual - predicted

def _percentage_error(actual: np.ndarray, predicted: np.ndarray):
    """
    Percentage error

    Note: result is NOT multiplied by 100
    """
    return _error(actual, predicted) / (actual + EPSILON)

def _naive_forecasting(actual: np.ndarray, seasonality: int = 1):
    """Naive forecasting method which just repeats previous samples"""
    return actual[:-seasonality]

def _relative_error(
    actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None
):
    """Relative Error"""
    if benchmark is None or isinstance(benchmark, int):
        # If no benchmark prediction provided - use naive forecasting
        if not isinstance(benchmark, int):
            seasonality = 1
        else:
            seasonality = benchmark
        return _error(actual[seasonality:], predicted[seasonality:]) / (
            _error(actual[seasonality:], _naive_forecasting(actual, seasonality))
            + EPSILON
        )

    return _error(actual, predicted) / (_error(actual, benchmark) + EPSILON)

def _bounded_relative_error(
    actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None
):
    """Bounded Relative Error"""
    if benchmark is None or isinstance(benchmark, int):
        # If no benchmark prediction provided - use naive forecasting
        if not isinstance(benchmark, int):
            seasonality = 1
        else:
            seasonality = benchmark

        abs_err = np.abs(_error(actual[seasonality:], predicted[seasonality:]))
        abs_err_bench = np.abs(
            _error(actual[seasonality:], _naive_forecasting(actual, seasonality))
        )
    else:
        abs_err = np.abs(_error(actual, predicted))
        abs_err_bench = np.abs(_error(actual, benchmark))

    return abs_err / (abs_err + abs_err_bench + EPSILON)

def _geometric_mean(a, axis=0, dtype=None):
    """Geometric mean"""
    if not isinstance(a, np.ndarray):  # if not an ndarray object attempt to convert it
        log_a = np.log(np.array(a, dtype=dtype))
    elif dtype:  # Must change the default dtype allowing array type
        if isinstance(a, np.ma.MaskedArray):
            log_a = np.log(np.ma.asarray(a, dtype=dtype))
        else:
            log_a = np.log(np.asarray(a, dtype=dtype))
    else:
        log_a = np.log(a)
    return np.exp(log_a.mean(axis=axis))

def mse(actual: np.ndarray, predicted: np.ndarray):
    """Mean Squared Error"""
    return np.mean(np.square(_error(actual, predicted)))

def rmse(actual: np.ndarray, predicted: np.ndarray):
    """Root Mean Squared Error"""
    return np.sqrt(mse(actual, predicted))

def nrmse(actual: np.ndarray, predicted: np.ndarray):
    """Normalized Root Mean Squared Error"""
    return rmse(actual, predicted) / (actual.max() - actual.min())

def me(actual: np.ndarray, predicted: np.ndarray):
    """Mean Error"""
    return np.mean(_error(actual, predicted))

def mae(actual: np.ndarray, predicted: np.ndarray):
    """Mean Absolute Error"""
    return np.mean(np.abs(_error(actual, predicted)))

mad = mae  # Mean Absolute Deviation (it is the same as MAE)

def gmae(actual: np.ndarray, predicted: np.ndarray):
    """Geometric Mean Absolute Error"""
    return _geometric_mean(np.abs(_error(actual, predicted)))

def mdae(actual: np.ndarray, predicted: np.ndarray):
    """Median Absolute Error"""
    return np.median(np.abs(_error(actual, predicted)))

def mpe(actual: np.ndarray, predicted: np.ndarray):
    """Mean Percentage Error"""
    return np.mean(_percentage_error(actual, predicted))

def mape(actual: np.ndarray, predicted: np.ndarray):
    """
    Mean Absolute Percentage Error

    Properties:
        + Easy to interpret
        + Scale independent
        - Biased, not symmetric
        - Undefined when actual[t] == 0

    Note: result is NOT multiplied by 100
    """
    return np.mean(np.abs(_percentage_error(actual, predicted)))

def mdape(actual: np.ndarray, predicted: np.ndarray):
    """
    Median Absolute Percentage Error

    Note: result is NOT multiplied by 100
    """
    return np.median(np.abs(_percentage_error(actual, predicted)))

def smape(actual: np.ndarray, predicted: np.ndarray):
    """
    Symmetric Mean Absolute Percentage Error

    Note: result is NOT multiplied by 100
    """
    return np.mean(
        2.0
        * np.abs(actual - predicted)
        / ((np.abs(actual) + np.abs(predicted)) + EPSILON)
    )

def smdape(actual: np.ndarray, predicted: np.ndarray):
    """
    Symmetric Median Absolute Percentage Error

    Note: result is NOT multiplied by 100
    """
    return np.median(
        2.0
        * np.abs(actual - predicted)
        / ((np.abs(actual) + np.abs(predicted)) + EPSILON)
    )

def maape(actual: np.ndarray, predicted: np.ndarray):
    """
    Mean Arctangent Absolute Percentage Error

    Note: result is NOT multiplied by 100
    """
    return np.mean(np.arctan(np.abs((actual - predicted) / (actual + EPSILON))))

def mase(actual: np.ndarray, predicted: np.ndarray, seasonality: int = 1):
    """
    Mean Absolute Scaled Error

    Baseline (benchmark) is computed with naive forecasting (shifted by @seasonality)
    """
    return mae(actual, predicted) / mae(
        actual[seasonality:], _naive_forecasting(actual, seasonality)
    )

def std_ae(actual: np.ndarray, predicted: np.ndarray):
    """Normalized Absolute Error"""
    __mae = mae(actual, predicted)
    return np.sqrt(
        np.sum(np.square(_error(actual, predicted) - __mae)) / (len(actual) - 1)
    )

def std_ape(actual: np.ndarray, predicted: np.ndarray):
    """Normalized Absolute Percentage Error"""
    __mape = mape(actual, predicted)
    return np.sqrt(
        np.sum(np.square(_percentage_error(actual, predicted) - __mape))
        / (len(actual) - 1)
    )

def rmspe(actual: np.ndarray, predicted: np.ndarray):
    """
    Root Mean Squared Percentage Error

    Note: result is NOT multiplied by 100
    """
    return np.sqrt(np.mean(np.square(_percentage_error(actual, predicted))))

def rmdspe(actual: np.ndarray, predicted: np.ndarray):
    """
    Root Median Squared Percentage Error

    Note: result is NOT multiplied by 100
    """
    return np.sqrt(np.median(np.square(_percentage_error(actual, predicted))))

def rmsse(actual: np.ndarray, predicted: np.ndarray, seasonality: int = 1):
    """Root Mean Squared Scaled Error"""
    q = np.abs(_error(actual, predicted)) / mae(
        actual[seasonality:], _naive_forecasting(actual, seasonality)
    )
    return np.sqrt(np.mean(np.square(q)))

def inrse(actual: np.ndarray, predicted: np.ndarray):
    """Integral Normalized Root Squared Error"""
    return np.sqrt(
        np.sum(np.square(_error(actual, predicted)))
        / np.sum(np.square(actual - np.mean(actual)))
    )

def rrse(actual: np.ndarray, predicted: np.ndarray):
    """Root Relative Squared Error"""
    return np.sqrt(
        np.sum(np.square(actual - predicted))
        / np.sum(np.square(actual - np.mean(actual)))
    )

def mre(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None):
    """Mean Relative Error"""
    return np.mean(_relative_error(actual, predicted, benchmark))

def rae(actual: np.ndarray, predicted: np.ndarray):
    """Relative Absolute Error (aka Approximation Error)"""
    return np.sum(np.abs(actual - predicted)) / (
        np.sum(np.abs(actual - np.mean(actual))) + EPSILON
    )

def mrae(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None):
    """Mean Relative Absolute Error"""
    return np.mean(np.abs(_relative_error(actual, predicted, benchmark)))

def mdrae(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None):
    """Median Relative Absolute Error"""
    return np.median(np.abs(_relative_error(actual, predicted, benchmark)))

def gmrae(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None):
    """Geometric Mean Relative Absolute Error"""
    return _geometric_mean(np.abs(_relative_error(actual, predicted, benchmark)))

def mbrae(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None):
    """Mean Bounded Relative Absolute Error"""
    return np.mean(_bounded_relative_error(actual, predicted, benchmark))

def umbrae(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None):
    """Unscaled Mean Bounded Relative Absolute Error"""
    __mbrae = mbrae(actual, predicted, benchmark)
    return __mbrae / (1 - __mbrae)

def mda(actual: np.ndarray, predicted: np.ndarray):
    """Mean Directional Accuracy"""
    return np.mean(
        (
            np.sign(actual[1:] - actual[:-1]) == np.sign(predicted[1:] - predicted[:-1])
        ).astype(int)
    )

def bias(actual: np.ndarray, predicted: np.ndarray):
    """Mean forecast error(or Forecast Bias)"""
    return np.mean(actual - predicted)

METRICS = {
    "mse": mse,
    "rmse": rmse,
    "nrmse": nrmse,
    "me": me,
    "mae": mae,
    "mad": mad,
    "gmae": gmae,
    "mdae": mdae,
    "mpe": mpe,
    "mape": mape,
    "mdape": mdape,
    "smape": smape,
    "smdape": smdape,
    "maape": maape,
    "mase": mase,
    "std_ae": std_ae,
    "std_ape": std_ape,
    "rmspe": rmspe,
    "rmdspe": rmdspe,
    "rmsse": rmsse,
    "inrse": inrse,
    "rrse": rrse,
    "mre": mre,
    "rae": rae,
    "mrae": mrae,
    "mdrae": mdrae,
    "gmrae": gmrae,
    "mbrae": mbrae,
    "umbrae": umbrae,
    "mda": mda,
    "bias": bias,
    "r2": r2_score,
}

def evaluate(
    actual: np.ndarray, predicted: np.ndarray,
    metrics=("mae", "rmse", "mape", "r2")
):
    results = {}
    for name in metrics:
        try:
            results[name] = METRICS[name](actual, predicted)
        except Exception as err:
            results[name] = np.nan
            print("Unable to compute metric {0}: {1}".format(name, err))
    return results

def evaluate_all(actual: np.ndarray, predicted: np.ndarray):
    return evaluate(actual, predicted, metrics=set(METRICS.keys()))
```

```python
resultsDict = {}
predictionsDict = {}

SPLIT_DATE = '2011-11-06'
train_df = df_series.loc[df_series.index <= SPLIT_DATE]
test_df = df_series.loc[df_series.index > SPLIT_DATE]
print(f"{len(train_df)} weeks of train data \n {len(test_df)} weeks of test data")
```

    102 weeks of train data
     4 weeks of test data

```python
# Baseline Model
mean = train_df.Quantity.mean()
mean = np.array([mean for u in range(len(test_df))])
resultsDict['Naive mean'] = evaluate(test_df.Quantity, mean)
predictionsDict['Naive mean'] = mean

```

## Univariate Time Series Analysis
In this section time-series forecasting methods that only look at the target variable will be used.

### Method 1: The Simple Exponential Smoothing

This method models the next time step as an exponentially weighted linear function of observations at prior time steps

```python
# Walk throught the test data, training and predicting 1 day ahead for all the test data
index = len(train_df)
yhat = list()
for t in tqdm(range(len(test_df.Quantity))):
    temp_train = df_series[:len(train_df)+t]
    model = SimpleExpSmoothing(temp_train.Quantity)
    model_fit = model.fit()
    predictions = model_fit.predict(start=len(temp_train), end=len(temp_train))
    yhat = yhat + [predictions]

yhat = pd.concat(yhat)
resultsDict['SES'] = evaluate(test_df.Quantity, yhat.values)
predictionsDict['SES'] = yhat.values
```

    100%|██████████| 4/4 [00:00<00:00, 85.48it/s]

```python
resultsDict
```

    {'Naive mean': {'mae': 64380.63725490196,
      'mape': 0.40404962183236276,
      'r2': -23.833941831061757,
      'rmse': 65717.36828150737},
     'SES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598}}

### Holt Winter’s Exponential Smoothing (HWES)

Description:

```python
# Walk throught the test data, training and predicting 1 day ahead for all the test data
index = len(train_df)
yhat = list()
for t in tqdm(range(len(test_df.Quantity))):
    temp_train = df_series[:len(train_df)+t]
    model = ExponentialSmoothing(temp_train.Quantity)
    model_fit = model.fit()
    predictions = model_fit.predict(start=len(temp_train), end=len(temp_train))
    yhat = yhat + [predictions]

yhat = pd.concat(yhat)
resultsDict['HWES'] = evaluate(test_df.Quantity, yhat.values)
predictionsDict['HWES'] = yhat.values
```

    100%|██████████| 4/4 [00:00<00:00, 121.18it/s]

### Autoregression (AR)
The autoregression (AR) method models the next step in the sequence as a linear function of the observations at prior time steps. Parameters of the model:

- __Number of AR (Auto-Regressive) terms (p):__ p is the parameter associated with the auto-regressive aspect of the model, which incorporates past values i.e lags of dependent variable. For instance if p is 5, the predictors for x(t) will be x(t-1)….x(t-5).

```python
# Walk throught the test data, training and predicting 1 day ahead for all the test data
index = len(train_df)
yhat = list()
for t in tqdm(range(len(test_df.Quantity))):
    temp_train = df_series[:len(train_df)+t]
    model = AR(temp_train.Quantity)
    model_fit = model.fit()
    predictions = model_fit.predict(start=len(temp_train), end=len(temp_train),
                                    dynamic=False)
    yhat = yhat + [predictions]

yhat = pd.concat(yhat)
resultsDict['AR'] = evaluate(test_df.Quantity, yhat.values)
predictionsDict['AR'] = yhat.values
```

    100%|██████████| 4/4 [00:00<00:00, 90.13it/s]

```python
resultsDict
```

    {'AR': {'mae': 21274.449418795593,
      'mape': 0.13224040117406527,
      'r2': -1.8812488878038707,
      'rmse': 22384.516652655242},
     'HWES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598},
     'Naive mean': {'mae': 64380.63725490196,
      'mape': 0.40404962183236276,
      'r2': -23.833941831061757,
      'rmse': 65717.36828150737},
     'SES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598}}

```python
plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat.values, color='red', label='AR predicted')
plt.legend()
```

```python
# Walk throught the test data, training and predicting 1 day ahead for all the test data
index = len(train_df)
yhat = list()
for t in tqdm(range(len(test_df.Quantity))):
  print(t)
```

    100%|██████████| 4/4 [00:00<00:00, 1292.74it/s]

    0
    1
    2
    3

## Moving Average (MA)

The Moving Average (MA) method models the next step in the sequence as the average of a window of observations at prior time steps. Parameters of the model:

Number of MA (Moving Average) terms (q): q is size of the moving average part window of the model i.e. lagged forecast errors in prediction equation. For instance if q is 5, the predictors for x(t) will be e(t-1)….e(t-5) where e(i) is the difference between the moving average at ith instant and actual value.

```python
# Walk throught the test data, training and predicting 1 day ahead for all the test data
index = len(train_df)
yhat = list()
for t in tqdm(range(len(test_df.Quantity))):
    temp_train = df_series[:len(train_df)+t]
    model = ARMA(temp_train.Quantity, order=(0,1))
    model_fit = model.fit()
    predictions = model_fit.predict(start=len(temp_train), end=len(temp_train),
                                    dynamic=False)
    yhat = yhat + [predictions]

yhat = pd.concat(yhat)
resultsDict['MA'] = evaluate(test_df.Quantity, yhat.values)
predictionsDict['MA'] = yhat.values
```

    100%|██████████| 4/4 [00:00<00:00, 21.81it/s]

```python
resultsDict
```

    {'AR': {'mae': 21274.449418795593,
      'mape': 0.13224040117406527,
      'r2': -1.8812488878038707,
      'rmse': 22384.516652655242},
     'HWES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598},
     'MA': {'mae': 39521.41047490899,
      'mape': 0.24710985372592503,
      'r2': -8.765039332636457,
      'rmse': 41209.21408696808},
     'Naive mean': {'mae': 64380.63725490196,
      'mape': 0.40404962183236276,
      'r2': -23.833941831061757,
      'rmse': 65717.36828150737},
     'SES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598}}

```python
def plot_ma_values(column_name, yhat, title):
  predicted_dates = list(range(2040, 2056,2))
  fig, ax = plt.subplots()
  plt.plot(test_df[column_name].values, label='Original')
  plt.plot(yhat.values, color='red', label='MA predicted')
  plt.title(f'Moving Average Forecast For {title}')
  plt.ylabel("Quantity")
  plt.xlabel("Week")
  ax.set_xticklabels(predicted_dates, rotation=90)
  plt.legend();
  plt.savefig(f"{FOLDER_PATH}{title}_moving_average")
```

```python
plot_ma_values("Quantity", yhat, "Quantity")
```

### Autoregressive Moving Average (ARMA)

This method will basically join the previous two `AR` and `MA`. Model parameters will be the sum of the two.

- __Number of AR (Auto-Regressive) terms (p):__ p is the parameter associated with the auto-regressive aspect of the model, which incorporates past values i.e lags of dependent variable. For instance if p is 5, the predictors for x(t) will be x(t-1)….x(t-5).
- __Number of MA (Moving Average) terms (q):__ q is size of the moving average part window of the model i.e. lagged forecast errors in prediction equation. For instance if q is 5, the predictors for x(t) will be e(t-1)….e(t-5) where e(i) is the difference between the moving average at ith instant and actual value.

```python
# Walk throught the test data, training and predicting 1 day ahead for all the test data
index = len(train_df)
yhat = list()
for t in tqdm(range(len(test_df.Quantity))):
    temp_train = df_series[:len(train_df)+t]
    model = ARMA(temp_train.Quantity, order=(1,1))
    model_fit = model.fit()
    predictions = model_fit.predict(start=len(temp_train), end=len(temp_train),
                                    dynamic=False)
    yhat = yhat + [predictions]

yhat = pd.concat(yhat)
resultsDict['ARMA'] = evaluate(test_df.Quantity, yhat.values)
predictionsDict['ARMA'] = yhat.values
```

    100%|██████████| 4/4 [00:00<00:00,  8.62it/s]

```python
resultsDict
```

    {'AR': {'mae': 21274.449418795593,
      'mape': 0.13224040117406527,
      'r2': -1.8812488878038707,
      'rmse': 22384.516652655242},
     'ARMA': {'mae': 17967.872025480618,
      'mape': 0.11018538935044533,
      'r2': -1.5848853412068404,
      'rmse': 21202.055910589734},
     'HWES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598},
     'MA': {'mae': 39521.41047490899,
      'mape': 0.24710985372592503,
      'r2': -8.765039332636457,
      'rmse': 41209.21408696808},
     'Naive mean': {'mae': 64380.63725490196,
      'mape': 0.40404962183236276,
      'r2': -23.833941831061757,
      'rmse': 65717.36828150737},
     'SES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598}}

```python
plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat.values, color='red', label='ARMA predicted')
plt.legend()
```

### Autoregressive integrated moving average (ARIMA)

In an ARIMA model there are 3 parameters that are used to help model the major aspects of a times series: seasonality, trend, and noise. These parameters are labeled p,d,and q.

* Number of AR (Auto-Regressive) terms (p): p is the parameter associated with the auto-regressive aspect of the model, which incorporates past values i.e lags of dependent variable. For instance if p is 5, the predictors for x(t) will be x(t-1)….x(t-5).
* Number of Differences (d): d is the parameter associated with the integrated part of the model, which effects the amount of differencing to apply to a time series.
* Number of MA (Moving Average) terms (q): q is size of the moving average part window of the model i.e. lagged forecast errors in prediction equation. For instance if q is 5, the predictors for x(t) will be e(t-1)….e(t-5) where e(i) is the difference between the moving average at ith instant and actual value.

**Tuning ARIMA parameters**

Non stationarity series will require level of differencing (d) >0 in ARIMA
Select the lag values for the Autoregression (AR) and Moving Average (MA) parameters, p and q respectively, using PACF, ACF plots
AUTOARIMA

Note: A problem with ARIMA is that it does not support seasonal data. That is a time series with a repeating cycle. ARIMA expects data that is either not seasonal or has the seasonal component removed, e.g. seasonally adjusted via methods such as seasonal differencing.

```python
# Walk throught the test data, training and predicting 1 day ahead for all the test data
index = len(train_df)
yhat = list()
for t in tqdm(range(len(test_df.Quantity))):
    temp_train = df_series[:len(train_df)+t]
    model = ARIMA(temp_train.Quantity, order=(1,0,0))
    model_fit = model.fit()
    predictions = model_fit.predict(start=len(temp_train), end=len(temp_train),
                                    dynamic=False)
    yhat = yhat + [predictions]

yhat = pd.concat(yhat)
resultsDict['ARIMA'] = evaluate(test_df.Quantity, yhat.values)
predictionsDict['ARIMA'] = yhat.values
```

    100%|██████████| 4/4 [00:00<00:00, 31.14it/s]

```python
plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat.values, color='red', label='ARIMA predicted')
plt.legend()
```

```python
resultsDict
```

    {'AR': {'mae': 21274.449418795593,
      'mape': 0.13224040117406527,
      'r2': -1.8812488878038707,
      'rmse': 22384.516652655242},
     'ARIMA': {'mae': 17889.69493700406,
      'mape': 0.10966924216063507,
      'r2': -1.621837191433773,
      'rmse': 21353.06359279361},
     'ARMA': {'mae': 17967.872025480618,
      'mape': 0.11018538935044533,
      'r2': -1.5848853412068404,
      'rmse': 21202.055910589734},
     'HWES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598},
     'MA': {'mae': 39521.41047490899,
      'mape': 0.24710985372592503,
      'r2': -8.765039332636457,
      'rmse': 41209.21408696808},
     'Naive mean': {'mae': 64380.63725490196,
      'mape': 0.40404962183236276,
      'r2': -23.833941831061757,
      'rmse': 65717.36828150737},
     'SES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598}}

```python
# building the model

autoModel = pm.auto_arima(train_df.Quantity, trace=True,
                          error_action='ignore', suppress_warnings=True, seasonal=False)
autoModel.fit(train_df.Quantity)
```

    Performing stepwise search to minimize aic
     ARIMA(2,0,2)(0,0,0)[0]             : AIC=2395.148, Time=0.26 sec
     ARIMA(0,0,0)(0,0,0)[0]             : AIC=2640.672, Time=0.01 sec
     ARIMA(1,0,0)(0,0,0)[0]             : AIC=2400.379, Time=0.02 sec
     ARIMA(0,0,1)(0,0,0)[0]             : AIC=2577.063, Time=0.02 sec
     ARIMA(1,0,2)(0,0,0)[0]             : AIC=2394.249, Time=0.09 sec
     ARIMA(0,0,2)(0,0,0)[0]             : AIC=2565.258, Time=0.03 sec
     ARIMA(1,0,1)(0,0,0)[0]             : AIC=2398.044, Time=0.05 sec
     ARIMA(1,0,3)(0,0,0)[0]             : AIC=2395.026, Time=0.13 sec
     ARIMA(0,0,3)(0,0,0)[0]             : AIC=2562.622, Time=0.05 sec
     ARIMA(2,0,1)(0,0,0)[0]             : AIC=inf, Time=0.22 sec
     ARIMA(2,0,3)(0,0,0)[0]             : AIC=inf, Time=0.36 sec
     ARIMA(1,0,2)(0,0,0)[0] intercept   : AIC=2388.318, Time=0.05 sec
     ARIMA(0,0,2)(0,0,0)[0] intercept   : AIC=2393.946, Time=0.04 sec
     ARIMA(1,0,1)(0,0,0)[0] intercept   : AIC=2385.955, Time=0.04 sec
     ARIMA(0,0,1)(0,0,0)[0] intercept   : AIC=2401.899, Time=0.03 sec
     ARIMA(1,0,0)(0,0,0)[0] intercept   : AIC=2383.578, Time=0.02 sec
     ARIMA(0,0,0)(0,0,0)[0] intercept   : AIC=2435.648, Time=0.01 sec
     ARIMA(2,0,0)(0,0,0)[0] intercept   : AIC=2385.581, Time=0.04 sec
     ARIMA(2,0,1)(0,0,0)[0] intercept   : AIC=2387.999, Time=0.08 sec

    Best model:  ARIMA(1,0,0)(0,0,0)[0] intercept
    Total fit time: 1.581 seconds

    ARIMA(maxiter=50, method='lbfgs', order=(1, 0, 0), out_of_sample_size=0,
          scoring='mse', scoring_args={}, seasonal_order=(0, 0, 0, 0),
          start_params=None, suppress_warnings=True, trend=None,
          with_intercept=True)

```python
order = autoModel.order
yhat = list()
for t in tqdm(range(len(test_df.Quantity))):
    temp_train = df_series[:len(train_df.Quantity)+t]
    model = ARIMA(temp_train.Quantity, order=order)
    model_fit = model.fit(disp=False)
    predictions = model_fit.predict(
        start=len(temp_train), end=len(temp_train), dynamic=False)
    yhat = yhat + [predictions]

yhat = pd.concat(yhat)
resultsDict['AutoARIMA {0}'.format(order)] = evaluate(
    test_df.Quantity, yhat)
predictionsDict['AutoARIMA {0}'.format(order)] = yhat.values
```

    100%|██████████| 4/4 [00:00<00:00, 41.13it/s]

```python
resultsDict
```

    {'AR': {'mae': 21274.449418795593,
      'mape': 0.13224040117406527,
      'r2': -1.8812488878038707,
      'rmse': 22384.516652655242},
     'ARIMA': {'mae': 17889.69493700406,
      'mape': 0.10966924216063507,
      'r2': -1.621837191433773,
      'rmse': 21353.06359279361},
     'ARMA': {'mae': 17967.872025480618,
      'mape': 0.11018538935044533,
      'r2': -1.5848853412068404,
      'rmse': 21202.055910589734},
     'AutoARIMA (1, 0, 0)': {'mae': 17889.69493700406,
      'mape': 0.10966924216063507,
      'r2': -1.621837191433773,
      'rmse': 21353.06359279361},
     'HWES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598},
     'MA': {'mae': 39521.41047490899,
      'mape': 0.24710985372592503,
      'r2': -8.765039332636457,
      'rmse': 41209.21408696808},
     'Naive mean': {'mae': 64380.63725490196,
      'mape': 0.40404962183236276,
      'r2': -23.833941831061757,
      'rmse': 65717.36828150737},
     'SES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598}}

### Seasonal Autoregressive Integrated Moving-Average (SARIMA)
Seasonal Autoregressive Integrated Moving Average, SARIMA or Seasonal ARIMA, is an extension of ARIMA that explicitly supports univariate time series data with a seasonal component.

It adds three new hyperparameters to specify the autoregression (AR), differencing (I) and moving average (MA) for the seasonal component of the series, as well as an additional parameter for the period of the seasonality.

__Trend Elements:__

There are three trend elements that require configuration. They are the same as the ARIMA model, specifically:

- p: Trend autoregression order.
- d: Trend difference order.
- q: Trend moving average order.

__Seasonal Elements:__

There are four seasonal elements that are not part of ARIMA that must be configured; they are:

- P: Seasonal autoregressive order.
- D: Seasonal difference order.
- Q: Seasonal moving average order.
- m: The number of time steps for a single seasonal period. For example, an S of 12 for monthly data suggests a yearly seasonal cycle.

__SARIMA notation:__
SARIMA(p,d,q)(P,D,Q,m)

```python
# SARIMA example

# Walk throught the test data, training and predicting 1 day ahead for all the test data
index = len(test_df)
yhat = list()
for t in tqdm(range(len(test_df.Quantity))):
    temp_train = df_series[:len(test_df)+t]
    model = SARIMAX(temp_train.Quantity, order=(
        1, 0, 0), seasonal_order=(0, 0, 0, 12)) # changed from 3 to 1
    model_fit = model.fit(disp=False)
    predictions = model_fit.predict(
        start=len(temp_train), end=len(temp_train), dynamic=False)
    yhat = yhat + [predictions]

yhat = pd.concat(yhat)
resultsDict['SARIMAX'] = evaluate(test_df.Quantity, yhat.values)
predictionsDict['SARIMAX'] = yhat.values
```

    100%|██████████| 4/4 [00:00<00:00, 48.86it/s]

```python
resultsDict
```

    {'AR': {'mae': 21274.449418795593,
      'mape': 0.13224040117406527,
      'r2': -1.8812488878038707,
      'rmse': 22384.516652655242},
     'ARIMA': {'mae': 17889.69493700406,
      'mape': 0.10966924216063507,
      'r2': -1.621837191433773,
      'rmse': 21353.06359279361},
     'ARMA': {'mae': 17967.872025480618,
      'mape': 0.11018538935044533,
      'r2': -1.5848853412068404,
      'rmse': 21202.055910589734},
     'AutoARIMA (1, 0, 0)': {'mae': 17889.69493700406,
      'mape': 0.10966924216063507,
      'r2': -1.621837191433773,
      'rmse': 21353.06359279361},
     'HWES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598},
     'MA': {'mae': 39521.41047490899,
      'mape': 0.24710985372592503,
      'r2': -8.765039332636457,
      'rmse': 41209.21408696808},
     'Naive mean': {'mae': 64380.63725490196,
      'mape': 0.40404962183236276,
      'r2': -23.833941831061757,
      'rmse': 65717.36828150737},
     'SARIMAX': {'mae': 118119.60387865943,
      'mape': 0.7410413763825117,
      'r2': -87.03955029657169,
      'rmse': 123736.04810029041},
     'SES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598}}

```python
plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat.values, color='red', label='SARIMA predicted')
plt.legend()
```

```python
# building the model
autoModel = pm.auto_arima(train_df.Quantity, trace=True, error_action='ignore',
                          suppress_warnings=True, seasonal=True, m=2, stepwise=True)
autoModel.fit(train_df.Quantity)
```

    Performing stepwise search to minimize aic
     ARIMA(1,0,1)(1,0,1)[2] intercept   : AIC=2390.260, Time=0.35 sec
     ARIMA(0,0,0)(0,0,0)[2] intercept   : AIC=2435.648, Time=0.01 sec
     ARIMA(1,0,0)(1,0,0)[2] intercept   : AIC=2385.475, Time=0.09 sec
     ARIMA(0,0,1)(0,0,1)[2] intercept   : AIC=2394.505, Time=0.05 sec
     ARIMA(0,0,0)(0,0,0)[2]             : AIC=2640.672, Time=0.01 sec
     ARIMA(1,0,0)(0,0,0)[2] intercept   : AIC=2383.578, Time=0.03 sec
     ARIMA(1,0,0)(0,0,1)[2] intercept   : AIC=2385.480, Time=0.06 sec
     ARIMA(1,0,0)(1,0,1)[2] intercept   : AIC=inf, Time=0.29 sec
     ARIMA(1,0,1)(0,0,0)[2] intercept   : AIC=2385.955, Time=0.04 sec
     ARIMA(0,0,1)(0,0,0)[2] intercept   : AIC=2401.899, Time=0.03 sec
     ARIMA(1,0,0)(0,0,0)[2]             : AIC=2400.379, Time=0.02 sec

    Best model:  ARIMA(1,0,0)(0,0,0)[2] intercept
    Total fit time: 0.993 seconds

    ARIMA(maxiter=50, method='lbfgs', order=(1, 0, 0), out_of_sample_size=0,
          scoring='mse', scoring_args={}, seasonal_order=(0, 0, 0, 2),
          start_params=None, suppress_warnings=True, trend=None,
          with_intercept=True)

```python
order = autoModel.order
seasonalOrder = autoModel.seasonal_order
yhat = list()
for t in tqdm(range(len(test_df.Quantity))):
    temp_train = df_series[:len(train_df)+t]
    model = SARIMAX(train_df.Quantity, order=order,
                    seasonal_order=seasonalOrder)
    model_fit = model.fit(disp=False)
    predictions = model_fit.predict(
        start=len(temp_train), end=len(temp_train), dynamic=False)
    yhat = yhat + [predictions]

yhat = pd.concat(yhat)
resultsDict['AutoSARIMAX {0},{1}'.format(order, seasonalOrder)] = evaluate(
    test_df.Quantity, yhat.values)
predictionsDict['AutoSARIMAX {0},{1}'.format(
    order, seasonalOrder)] = yhat.values
```

    100%|██████████| 4/4 [00:00<00:00, 37.38it/s]

```python
plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat.values, color='red', label='SARIMA predicted')
plt.legend()
```

```python
yhat.values
```

    array([169048.7729616 , 162893.16187476, 156961.69644238, 151246.21479822])

### Prophet

Prophet is a model released by [Facebook](https://github.com/facebook/prophet). Is essentially a curve fitting approach, very similar in spirit to how BSTS models trend and seasonality, except that it uses generalized additive models instead of a state-space representation to describe each component.

```python
# Prophet needs some specifics data stuff, coment it here
prophet_training = train_df.rename(
    columns={'Quantity': 'y'})  # old method
prophet_training['ds'] = prophet_training.index
prophet_training.index = pd.RangeIndex(len(prophet_training.index))

prophet_test = test_df.rename(columns={'Quantity': 'y'})  # old method
prophet_test['ds'] = prophet_test.index
prophet_test.index = pd.RangeIndex(len(prophet_test.index))
```

```python
prophet = Prophet(
    growth='linear',
    seasonality_mode='multiplicative',
    holidays_prior_scale=20,
    daily_seasonality=False,
    weekly_seasonality=False,
    yearly_seasonality=False
).add_seasonality(
    name='monthly',
    period=30.5,
    fourier_order=55
).add_seasonality(
    name='daily',
    period=1,
    fourier_order=15
).add_seasonality(
    name='weekly',
    period=7,
    fourier_order=25
).add_seasonality(
    name='yearly',
    period=365.25,
    fourier_order=20
).add_seasonality(
    name='quarterly',
    period=365.25/4,
    fourier_order=55
).add_country_holidays(country_name='UK')
```

```python
prophet.fit(prophet_training)
yhat = prophet.predict(prophet_test)
resultsDict['Prophet univariate'] = evaluate(
    test_df.Quantity, yhat.yhat.values)
predictionsDict['Prophet univariate'] = yhat.yhat.values
```

```python
plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat.yhat, color='red', label='Prophet univariate')
plt.legend()
```

## Multivariate Time Series Forecasting
A Multivariate time series has more than one time-dependent variable. Each variable depends not only on its past values but also has some dependency on other variables. This dependency is used for forecasting future values.

## Method 1: Using Time Features

```python
# ADD time features to our model
def create_time_features(df, target=None):
    """
    Creates time series features from datetime index
    """
    df['date'] = df.index
    df['hour'] = df['date'].dt.hour
    df['dayofweek'] = df['date'].dt.dayofweek
    df['quarter'] = df['date'].dt.quarter
    df['month'] = df['date'].dt.month
    df['year'] = df['date'].dt.year
    df['dayofyear'] = df['date'].dt.dayofyear
    df['sin_day'] = np.sin(df['dayofyear'])
    df['cos_day'] = np.cos(df['dayofyear'])
    df['dayofmonth'] = df['date'].dt.day
    df['weekofyear'] = df['date'].dt.weekofyear
    X = df.drop(['date'], axis=1)
    if target:
        y = df[target]
        X = X.drop([target], axis=1)
        return X, y

    return X
```

```python
train_df
```

      Quantity
      date
      hour
      dayofweek
      quarter
      month
      year
      dayofyear
      sin_day
      cos_day
      dayofmonth
      weekofyear

      Date

      2009-11-29
      125490
      2009-11-29
      0
      6
      4
      11
      2009
      333
      -0.008821
      0.999961
      29
      48

      2009-12-06
      110066
      2009-12-06
      0
      6
      4
      12
      2009
      340
      0.650311
      0.759668
      6
      49

      2009-12-13
      129763
      2009-12-13
      0
      6
      4
      12
      2009
      347
      0.989363
      0.145470
      13
      50

      2009-12-20
      27014
      2009-12-20
      0
      6
      4
      12
      2009
      354
      0.841455
      -0.540328
      20
      51

      2009-12-27
      0
      2009-12-27
      0
      6
      4
      12
      2009
      361
      0.279387
      -0.960179
      27
      52

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      2011-10-09
      114600
      2011-10-09
      0
      6
      4
      10
      2011
      282
      -0.676750
      0.736213
      9
      40

      2011-10-16
      146799
      2011-10-16
      0
      6
      4
      10
      2011
      289
      -0.026521
      0.999648
      16
      41

      2011-10-23
      134407
      2011-10-23
      0
      6
      4
      10
      2011
      296
      0.636761
      0.771061
      23
      42

      2011-10-30
      148957
      2011-10-30
      0
      6
      4
      10
      2011
      303
      0.986633
      0.162961
      30
      43

      2011-11-06
      175437
      2011-11-06
      0
      6
      4
      11
      2011
      310
      0.850888
      -0.525348
      6
      44

102 rows × 12 columns

```python
X_train_df, y_train = create_time_features(
    train_df, target='Quantity')

X_test_df, y_test = create_time_features(test_df, target='Quantity')
scaler = StandardScaler()
scaler.fit(X_train_df)  # No cheating, never scale on the training+test!
X_train = scaler.transform(X_train_df)
X_test = scaler.transform(X_test_df)

X_train_df = pd.DataFrame(X_train, columns=X_train_df.columns)
X_test_df = pd.DataFrame(X_test, columns=X_test_df.columns)
```

## Using Linear Models

### Linear Model Algorithm 1: Bayesian Regression

```python
reg = linear_model.BayesianRidge()
reg.fit(X_train, y_train)
yhat = reg.predict(X_test)
resultsDict['BayesianRidge'] = evaluate(test_df.Quantity, yhat)
predictionsDict['BayesianRidge'] = yhat
```

```python
plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat, color='red', label='BayesianRidge')
plt.legend()
```

### Linear Model Algorithm 2: Lasso

```python
reg = linear_model.Lasso(alpha=0.1)
reg.fit(X_train, y_train)
yhat = reg.predict(X_test)
resultsDict['Lasso'] = evaluate(test_df.Quantity, yhat)
predictionsDict['Lasso'] = yhat
```

```python
plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat, color='red', label='Lasso')
plt.legend()
```

## Using Tree Models

### RandomForest

```python
reg = RandomForestRegressor(max_depth=2, random_state=0)
reg.fit(X_train, y_train)
yhat = reg.predict(X_test)
resultsDict['Randomforest'] = evaluate(test_df.Quantity, yhat)
predictionsDict['Randomforest'] = yhat
```

```python
plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat, color='red', label='RandomForest')
plt.legend()
```

### XGBoost

```python
reg = xgb.XGBRegressor(objective='reg:squarederror', n_estimators=1000)
reg.fit(X_train, y_train,
        verbose=False)  # Change verbose to True if you want to see it train
yhat = reg.predict(X_test)
resultsDict['XGBoost'] = evaluate(test_df.Quantity, yhat)
predictionsDict['XGBoost'] = yhat
```

```python
plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat, color='red', label='XGboost')
plt.legend()
```

```python
reg = xgb.XGBRegressor(objective='reg:squarederror', n_estimators=1000)
reg.fit(X_train, y_train)
yhat = reg.predict(X_test)
resultsDict['XGBoost 2'] = evaluate(df_test.Amount_Spent, yhat)
predictionsDict['XGBoost 2'] = yhat

plt.plot(df_test.Amount_Spent.values, label='Original')
plt.plot(yhat, color='red', label='XGBoost 2')
plt.legend()
```

### LightGBM -

A tree gradient boosting model by [Microsoft](https://github.com/microsoft/LightGBM)

```python
def use_model(model, name):
  model.fit(X_train, y_train)
  y_hat = model.predict(X_test)
  resultsDict[name] = evaluate(test_df.Quantity, yhat)
  predictionsDict[name] = yhat

  # To visualize the results
  plt.plot(test_df.Quantity.values, label='Original')
  plt.plot(yhat, color='red', label=name)
  plt.legend()

  return resultsDict[name], predictionsDict[name]

```

```python
X_train.shape
```

    (711, 10)

```python
X_test.shape
```

    (28, 10)

```python
df_training.shape
```

    (711, 12)

```python
df_test.shape
```

    (28, 12)

```python
df_test.columns
```

    Index(['Amount_Spent', 'date', 'hour', 'dayofweek', 'quarter', 'month', 'year',
           'dayofyear', 'sin_day', 'cos_day', 'dayofmonth', 'weekofyear'],
          dtype='object')

```python
lightGBM = lgb.LGBMRegressor()
lightGBM.fit(X_train, y_train)
yhat = lightGBM.predict(X_test)
resultsDict['Lightgbm 2'] = evaluate(df_test.Amount_Spent, yhat)
predictionsDict['Lightgbm 2'] = yhat

plt.plot(df_test.Amount_Spent.values, label='Original')
plt.plot(yhat, color='red', label='Lightgbm 2')
plt.legend()
```

```python
lightGBM = lgb.LGBMRegressor()
lightGBM.fit(X_train, y_train)
yhat = lightGBM.predict(X_test)
resultsDict['Lightgbm'] = evaluate(test_df.Quantity, yhat)
predictionsDict['Lightgbm'] = yhat

plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat, color='red', label='Lightgbm')
plt.legend()

```

### Support vector machines

```python
reg = svm.SVR(kernel='rbf', C=100, gamma=0.1, epsilon=.1)
reg.fit(X_train, y_train)
yhat = reg.predict(X_test)
resultsDict['SVM RBF'] = evaluate(test_df.Quantity, yhat)
predictionsDict['SVM RBF'] = yhat

plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat, color='red', label='SVM RBF')
plt.legend()
```

## Using Nearest Neighbours

```python
reg = KNeighborsRegressor(n_neighbors=2)
reg.fit(X_train, y_train)
yhat = reg.predict(X_test)
resultsDict['Kneighbors'] = evaluate(test_df.Quantity, yhat)
predictionsDict['Kneighbors'] = yhat

plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat, color='red', label='Kneighbors')
plt.legend()
```

### Prophet Multivariate

```python
prophet = Prophet(
    growth='linear',
    seasonality_mode='multiplicative',
    daily_seasonality=True,
).add_country_holidays(country_name='UK')

for col in prophet_training.columns:
    if col not in ["ds", "y"]:
        prophet.add_regressor(col)
```

```python
prophet.fit(prophet_training)
yhat = prophet.predict(prophet_test)
resultsDict['Prophet multivariate'] = evaluate(y_test, yhat.yhat.values)
predictionsDict['Prophet multivariate'] = yhat.yhat.values
```

    INFO:fbprophet:Disabling yearly seasonality. Run prophet with yearly_seasonality=True to override this.
    INFO:fbprophet:Disabling weekly seasonality. Run prophet with weekly_seasonality=True to override this.

```python
plt.plot(test_df.Quantity.values, label='Original')
plt.plot(yhat.yhat, color='red', label='Prophet multivariate')
plt.legend()
```

```python
cleaned_data.index = cleaned_data["InvoiceDate"]
```

```python

```

## Deep Learning

```python
cleaned_data["Date"] = pd.to_datetime(cleaned_data["Date"])
cleaned_data.index = cleaned_data["Date"]

daily_sales = cleaned_data['Amount_Spent'].resample('D').sum()
```

```python
split_date = '2011-11-11' # Predict for 28 days
df_training = daily_sales.loc[daily_sales.index <= split_date]
df_test = daily_sales.loc[daily_sales.index > split_date]
print(f"{len(df_training)} days of training data \n {len(df_test)} days of testing data ")
```

    711 days of training data
     28 days of testing data

```python
df_training = pd.DataFrame(df_training)
df_test = pd.DataFrame(df_test)
```

```python
# ADD time features to our model
# ADD time features to our model
def create_time_features(df, target=None):
    """
    Creates time series features from datetime index
    """
    df['date'] = df.index
    df['hour'] = df['date'].dt.hour
    df['dayofweek'] = df['date'].dt.dayofweek
    df['quarter'] = df['date'].dt.quarter
    df['month'] = df['date'].dt.month
    df['year'] = df['date'].dt.year
    df['dayofyear'] = df['date'].dt.dayofyear
    df['sin_day'] = np.sin(df['dayofyear'])
    df['cos_day'] = np.cos(df['dayofyear'])
    df['dayofmonth'] = df['date'].dt.day
    df['weekofyear'] = df['date'].dt.weekofyear
    X = df.drop(['date'], axis=1)
    if target:
        y = df[target]
        X = X.drop([target], axis=1)
        return X, y

    return X
```

```python
train_df
```

      Quantity
      date
      hour
      dayofweek
      quarter
      month
      year
      dayofyear
      sin_day
      cos_day
      dayofmonth
      weekofyear

      Date

      2009-11-29
      125490
      2009-11-29
      0
      6
      4
      11
      2009
      333
      -0.008821
      0.999961
      29
      48

      2009-12-06
      110066
      2009-12-06
      0
      6
      4
      12
      2009
      340
      0.650311
      0.759668
      6
      49

      2009-12-13
      129763
      2009-12-13
      0
      6
      4
      12
      2009
      347
      0.989363
      0.145470
      13
      50

      2009-12-20
      27014
      2009-12-20
      0
      6
      4
      12
      2009
      354
      0.841455
      -0.540328
      20
      51

      2009-12-27
      0
      2009-12-27
      0
      6
      4
      12
      2009
      361
      0.279387
      -0.960179
      27
      52

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      2011-10-09
      114600
      2011-10-09
      0
      6
      4
      10
      2011
      282
      -0.676750
      0.736213
      9
      40

      2011-10-16
      146799
      2011-10-16
      0
      6
      4
      10
      2011
      289
      -0.026521
      0.999648
      16
      41

      2011-10-23
      134407
      2011-10-23
      0
      6
      4
      10
      2011
      296
      0.636761
      0.771061
      23
      42

      2011-10-30
      148957
      2011-10-30
      0
      6
      4
      10
      2011
      303
      0.986633
      0.162961
      30
      43

      2011-11-06
      175437
      2011-11-06
      0
      6
      4
      11
      2011
      310
      0.850888
      -0.525348
      6
      44

102 rows × 12 columns

```python
# Transforming the data for Deep Learning
# X_train_df, y_train = create_time_features(
#     df_training, target='Amount_Spent')
# X_test_df, y_test = create_time_features(df_test, target='Amount_Spent')
# scaler = StandardScaler()
# scaler.fit(X_train_df)  # No cheating, never scale on the training+test!
# X_train = scaler.transform(X_train_df)
# X_test = scaler.transform(X_test_df)

# X_train_df = pd.DataFrame(X_train, columns=X_train_df.columns)
# X_test_df = pd.DataFrame(X_test, columns=X_test_df.columns)
```

```python
# For our dl model we will create windows of data that will be feeded into the datasets, for each timestemp T we will append the data from T-7 to T to the Xdata with target Y(t)
BATCH_SIZE = 64
BUFFER_SIZE = 100
WINDOW_LENGTH = 24

def window_data(X, Y, window=7):
    '''
    The dataset length will be reduced to guarante all samples have the window, so new length will be len(dataset)-window
    '''
    x = []
    y = []
    for i in range(window-1, len(X)):
        x.append(X[i-window+1:i+1])
        y.append(Y[i])
    return np.array(x), np.array(y)

# Since we are doing sliding, we need to join the datasets again of train and test
X_w = np.concatenate((X_train, X_test))
y_w = np.concatenate((y_train, y_test))

X_w, y_w = window_data(X_w, y_w, window=WINDOW_LENGTH)
X_train_w = X_w[:-len(X_test)]
y_train_w = y_w[:-len(X_test)]
X_test_w = X_w[-len(X_test):]
y_test_w = y_w[-len(X_test):]

# Check we will have same test set as in the previous models, make sure we didnt screw up on the windowing
print(f"Test set equal: {np.array_equal(y_test_w,y_test)}")

train_data = tf.data.Dataset.from_tensor_slices((X_train_w, y_train_w))
train_data = train_data.cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat()

val_data = tf.data.Dataset.from_tensor_slices((X_test_w, y_test_w))
val_data = val_data.batch(BATCH_SIZE).repeat()
```

    Test set equal: True

```python
dropout = 0.0
simple_lstm_model = tf.keras.models.Sequential([
    tf.keras.layers.LSTM(
        128, input_shape=X_train_w.shape[-2:], dropout=dropout),
    tf.keras.layers.Dense(128),
    tf.keras.layers.Dense(128),
    tf.keras.layers.Dense(1)
])

simple_lstm_model.compile(optimizer='rmsprop', loss='mae')

# logdir = "logs/scalars/" + datetime.now().strftime("%Y%m%d-%H%M%S") #Support for tensorboard tracking!
# tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=logdir)
```

```python

```

```python
EVALUATION_INTERVAL = 200
EPOCHS = 10

model_history = simple_lstm_model.fit(train_data, epochs=EPOCHS,
                                      steps_per_epoch=EVALUATION_INTERVAL,
                                      validation_data=val_data, validation_steps=50)  # ,callbacks=[tensorboard_callback]) #Uncomment this line for tensorboard support
```

    Epoch 1/10
    200/200 [==============================] - 10s 38ms/step - loss: 82932.2812 - val_loss: 110427.6797
    Epoch 2/10
    200/200 [==============================] - 7s 35ms/step - loss: 29519.6660 - val_loss: 35150.7500
    Epoch 3/10
    200/200 [==============================] - 7s 35ms/step - loss: 15250.6123 - val_loss: 10522.5010
    Epoch 4/10
    200/200 [==============================] - 7s 35ms/step - loss: 11866.7842 - val_loss: 15533.8975
    Epoch 5/10
    200/200 [==============================] - 7s 35ms/step - loss: 10256.7734 - val_loss: 18136.9844
    Epoch 6/10
    200/200 [==============================] - 7s 35ms/step - loss: 8936.1025 - val_loss: 19302.3711
    Epoch 7/10
    200/200 [==============================] - 7s 35ms/step - loss: 7512.0142 - val_loss: 19814.7520
    Epoch 8/10
    200/200 [==============================] - 7s 34ms/step - loss: 6304.7261 - val_loss: 24064.0117
    Epoch 9/10
    200/200 [==============================] - 7s 34ms/step - loss: 5556.5308 - val_loss: 20520.6406
    Epoch 10/10
    200/200 [==============================] - 7s 34ms/step - loss: 4781.7422 - val_loss: 22374.6699

```python
yhat = simple_lstm_model.predict(X_test_w).reshape(1, -1)[0]
resultsDict['Tensorflow simple LSTM'] = evaluate(y_test, yhat)
predictionsDict['Tensorflow simple LSTM'] = yhat
```

```python
resultsDict
```

    {'AR': {'mae': 21274.449418795593,
      'mape': 0.13224040117406527,
      'r2': -1.8812488878038707,
      'rmse': 22384.516652655242},
     'ARIMA': {'mae': 17889.69493700406,
      'mape': 0.10966924216063507,
      'r2': -1.621837191433773,
      'rmse': 21353.06359279361},
     'ARMA': {'mae': 17967.872025480618,
      'mape': 0.11018538935044533,
      'r2': -1.5848853412068404,
      'rmse': 21202.055910589734},
     'AutoARIMA (1, 0, 0)': {'mae': 17889.69493700406,
      'mape': 0.10966924216063507,
      'r2': -1.621837191433773,
      'rmse': 21353.06359279361},
     'AutoSARIMAX (1, 0, 0),(0, 0, 0, 2)': {'mae': 8864.467639330236,
      'mape': 0.056112088410109284,
      'r2': 0.45758807312406724,
      'rmse': 9712.296871645192},
     'BayesianRidge': {'mae': 32083.08118414712,
      'mape': 0.1965845659934906,
      'r2': -6.65331929442584,
      'rmse': 36482.303315112265},
     'HWES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598},
     'KNeighbors': {'mae': 26703.55938142319,
      'mape': 0.16199238074064612,
      'r2': -4.948444729314532,
      'rmse': 32163.185010321733},
     'Kneighbors': {'mae': 20563.375,
      'mape': 0.12792141062143764,
      'r2': -1.7550727072789631,
      'rmse': 21888.896404627165},
     'Lasso': {'mae': 26703.55938142319,
      'mape': 0.16199238074064612,
      'r2': -4.948444729314532,
      'rmse': 32163.185010321733},
     'LightGBM': {'mae': 28783.92578125,
      'mape': 0.18607997755607655,
      'r2': -5.805708897742901,
      'rmse': 34402.81801251298},
     'Lightgbm': {'mae': 26929.42919847399,
      'mape': 0.1643673242160161,
      'r2': -5.443198832735387,
      'rmse': 33474.03765838882},
     'MA': {'mae': 39521.41047490899,
      'mape': 0.24710985372592503,
      'r2': -8.765039332636457,
      'rmse': 41209.21408696808},
     'Naive mean': {'mae': 64380.63725490196,
      'mape': 0.40404962183236276,
      'r2': -23.833941831061757,
      'rmse': 65717.36828150737},
     'Prophet multivariate': {'mae': 48318.604453567124,
      'mape': 0.3015132129668772,
      'r2': -13.462674963130201,
      'rmse': 50151.23665351092},
     'Prophet univariate': {'mae': 77298.07473579783,
      'mape': 0.49195332504175276,
      'r2': -48.0286866474169,
      'rmse': 92338.42518565872},
     'Randomforest': {'mae': 15139.478225690436,
      'mape': 0.08995804794499901,
      'r2': -1.3505487518450678,
      'rmse': 20218.175645581698},
     'SARIMAX': {'mae': 118119.60387865943,
      'mape': 0.7410413763825117,
      'r2': -87.03955029657169,
      'rmse': 123736.04810029041},
     'SES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598},
     'SVM RBF': {'mae': 69624.19191807767,
      'mape': 0.437486005678421,
      'r2': -27.874243137850005,
      'rmse': 70861.87477436503},
     'Tensorflow simple LSTM': {'mae': 9934.940446690041,
      'mape': 1565292162214.229,
      'r2': 0.5923524282887452,
      'rmse': 14030.183366088855},
     'XGB_test': {'mae': 28783.92578125,
      'mape': 0.18607997755607655,
      'r2': -5.805708897742901,
      'rmse': 34402.81801251298},
     'XGBoost': {'mae': 28783.92578125,
      'mape': 0.18607997755607655,
      'r2': -5.805708897742901,
      'rmse': 34402.81801251298}}

## Deep AR

```python
features = list(df_training.columns)
```

```python
training_data = ListDataset(
    [{"start": df_training.index[0], "target": df_training.Amount_Spent,
      'feat_dynamic_real': [df_training[feature] for feature in features]
      }],
    freq="d"
)

test_data = ListDataset(
    [{"start": df_test.index[0], "target": df_test.Amount_Spent,
      'feat_dynamic_real': [df_test[feature] for feature in features]
      }],
    freq="d"
)
```

```python
estimator = DeepAREstimator(freq="d",
                            prediction_length=1, context_length=30,
                            trainer=Trainer(epochs=5))

predictor = estimator.train(training_data=training_data)

forecast_it, ts_it = make_evaluation_predictions(
    test_data, predictor=predictor, num_samples=len(df_test))

forecasts = list(forecast_it)
tss = list(ts_it)
```

    INFO:gluonts.trainer:Start model training
    INFO:gluonts.trainer:Epoch[0] Learning rate is 0.001
      0%|          | 0/50 [00:00<?, ?it/s]INFO:gluonts.trainer:Number of parameters in DeepARTrainingNetwork: 25884
    100%|██████████| 50/50 [00:04<00:00, 12.04it/s, epoch=1/5, avg_epoch_loss=11.1]
    INFO:gluonts.trainer:Epoch[0] Elapsed time 4.165 seconds
    INFO:gluonts.trainer:Epoch[0] Evaluation metric 'epoch_loss'=11.114265
    INFO:gluonts.trainer:Epoch[1] Learning rate is 0.001
    100%|██████████| 50/50 [00:03<00:00, 13.32it/s, epoch=2/5, avg_epoch_loss=10.7]
    INFO:gluonts.trainer:Epoch[1] Elapsed time 3.765 seconds
    INFO:gluonts.trainer:Epoch[1] Evaluation metric 'epoch_loss'=10.738511
    INFO:gluonts.trainer:Epoch[2] Learning rate is 0.001
    100%|██████████| 50/50 [00:03<00:00, 13.54it/s, epoch=3/5, avg_epoch_loss=10.5]
    INFO:gluonts.trainer:Epoch[2] Elapsed time 3.699 seconds
    INFO:gluonts.trainer:Epoch[2] Evaluation metric 'epoch_loss'=10.530960
    INFO:gluonts.trainer:Epoch[3] Learning rate is 0.001
    100%|██████████| 50/50 [00:03<00:00, 13.45it/s, epoch=4/5, avg_epoch_loss=10.4]
    INFO:gluonts.trainer:Epoch[3] Elapsed time 3.729 seconds
    INFO:gluonts.trainer:Epoch[3] Evaluation metric 'epoch_loss'=10.402856
    INFO:gluonts.trainer:Epoch[4] Learning rate is 0.001
    100%|██████████| 50/50 [00:03<00:00, 13.52it/s, epoch=5/5, avg_epoch_loss=10.3]
    INFO:gluonts.trainer:Epoch[4] Elapsed time 3.703 seconds
    INFO:gluonts.trainer:Epoch[4] Evaluation metric 'epoch_loss'=10.348450
    INFO:root:Computing averaged parameters.
    INFO:root:Loading averaged parameters.
    INFO:gluonts.trainer:End model training

```python
yhat = forecasts[0].samples.reshape(1, -1)[0]
resultsDict['DeepAR'] = evaluate(y_test, yhat)
predictionsDict['DeepAR'] = yhat
```

```python
resultsDict['DeepAR']
```

    {'mae': 24734.603479962934,
     'mape': 37334046456473.664,
     'r2': -0.8237353721114968,
     'rmse': 29675.758487058385}

Some Hyper Parameter Optimization Methods

```python
reg = GridSearchCV(svm.SVR(kernel='rbf', gamma=0.1),
                   param_grid={"C": [1e0, 1e1, 1e2, 1e3],
                               "gamma": np.logspace(-2, 2, 5)})
reg.fit(X_train, y_train)
yhat = reg.predict(X_test)
resultsDict['SVM RBF GRID SEARCH 2'] = evaluate(test_df.Quantity, yhat)
predictionsDict['SVM RBF GRID SEARCH 2'] = yhat
```

```python
resultsDict['SVM RBF GRID SEARCH']
```

    {'mae': 19062.47413918481,
     'mape': 36833528735135.02,
     'r2': 0.0006016830906163673,
     'rmse': 21967.985036110087}

```python
resultsDict['SVM RBF GRID SEARCH 2']
```

    {'mae': 59246.42986667021,
     'mape': 0.3712058353534466,
     'r2': -20.21925587949882,
     'rmse': 60746.66058607654}

```python
increase = 1 - (resultsDict['SVM RBF GRID SEARCH']
                ['rmse']/resultsDict['SVM RBF']['rmse'])
print(
    f"Grid search Tunned SVM is {increase*100}% better than the SVM with default parameters")
```

    Grid search Tunned SVM is 68.99886560148248% better than the SVM with default parameters

```python
def rms(y_actual, y_predicted):
    return sqrt(mean_squared_error(y_actual, y_predicted))

my_scorer = make_scorer(rms, greater_is_better=False)
pbounds = {
    'n_estimators': (100, 10000),
    'max_depth': (3, 15),
    'min_samples_leaf': (1, 4),
    'min_samples_split': (2, 10),
}

def rf_hyper_param(n_estimators,
                   max_depth,
                   min_samples_leaf,
                   min_samples_split):

    max_depth = int(max_depth)
    n_estimators = int(n_estimators)

    clf = RandomForestRegressor(n_estimators=n_estimators,
                                max_depth=int(max_depth),
                                min_samples_leaf=int(min_samples_leaf),
                                min_samples_split=int(min_samples_split),
                                n_jobs=1)

    return -np.mean(cross_val_score(clf, X_train, y_train, cv=3))

optimizer = BayesianOptimization(
    f=rf_hyper_param,
    pbounds=pbounds,
    random_state=1,
)
```

```python
optimizer.maximize(
    init_points=3,
    n_iter=20,
    acq='ei'
)
```

    |   iter    |  target   | max_depth | min_sa... | min_sa... | n_esti... |
    -------------------------------------------------------------------------
    |  1        | -0.5254   |  8.004    |  3.161    |  2.001    |  3.093e+0 |
    |  2        | -0.5567   |  4.761    |  1.277    |  3.49     |  3.521e+0 |
    |  3        | -0.5282   |  7.761    |  2.616    |  5.354    |  6.884e+0 |
    |  4        | -0.5276   |  15.0     |  4.0      |  2.0      |  100.0    |
    |  5        | -0.5287   |  15.0     |  4.0      |  2.0      |  1e+04    |
    |  6        | -0.5586   |  4.895    |  3.298    |  3.9      |  3.095e+0 |
    |  7        | -0.5166   |  14.68    |  2.053    |  2.634    |  2.879e+0 |
    |  8        | -0.5162   |  13.37    |  1.758    |  5.552    |  4.134e+0 |
    |  9        | -0.5473   |  5.942    |  3.086    |  4.383    |  4.693e+0 |
    |  10       | -0.5246   |  11.13    |  2.336    |  6.991    |  751.4    |
    |  11       | -0.5271   |  14.15    |  3.383    |  8.37     |  1.376e+0 |
    |  12       | -0.524    |  13.09    |  3.658    |  3.791    |  7.577e+0 |
    |  13       | -0.547    |  5.88     |  2.338    |  3.249    |  8.211e+0 |
    |  14       | -0.5525   |  4.107    |  1.984    |  9.133    |  6.199e+0 |
    |  15       | -0.5243   |  10.19    |  3.364    |  5.423    |  9.335e+0 |
    |  16       | -0.5142   |  12.71    |  2.09     |  4.531    |  1.966e+0 |
    |  17       | -0.5001   |  13.42    |  1.385    |  2.367    |  5.436e+0 |
    |  18       | -0.524    |  13.54    |  3.406    |  4.938    |  8.804e+0 |
    |  19       | -0.5002   |  15.0     |  1.0      |  2.0      |  2.365e+0 |
    |  20       | -0.5248   |  14.56    |  2.477    |  7.319    |  5.802e+0 |
    |  21       | -0.5592   |  4.474    |  3.472    |  4.636    |  5.076e+0 |
    |  22       | -0.5213   |  13.97    |  3.068    |  3.807    |  2.367e+0 |
    |  23       | -0.5175   |  14.2     |  2.229    |  2.976    |  2.196e+0 |
    =========================================================================

```python
params = optimizer.max['params']

# Converting the max_depth and n_estimator values from float to int
params['max_depth'] = int(params['max_depth'])
params['n_estimators'] = int(params['n_estimators'])
params['min_samples_leaf'] = int(params['min_samples_leaf'])
params['min_samples_split'] = int(params['min_samples_split'])

# Initialize an XGBRegressor with the tuned parameters and fit the training data
tunned_rf = RandomForestRegressor(**params)
# Change verbose to True if you want to see it train
tunned_rf.fit(X_train, y_train)

yhat = tunned_rf.predict(X_test)
resultsDict['Randomforest tunned'] = evaluate(y_test, yhat)
```

```python
increase = 1 - (resultsDict['Randomforest tunned']
                ['rmse']/resultsDict['Randomforest']['rmse'])
print(
    f"Bayesian optimized Randomforest is {increase*100}% better than the Randomforest with default parameters")
```

    Bayesian optimized Randomforest is 37.28287358299437% better than the Randomforest with default parameters

```python
resultsDict
```

    {'AR': {'mae': 21274.449418795593,
      'mape': 0.13224040117406527,
      'r2': -1.8812488878038707,
      'rmse': 22384.516652655242},
     'ARIMA': {'mae': 17889.69493700406,
      'mape': 0.10966924216063507,
      'r2': -1.621837191433773,
      'rmse': 21353.06359279361},
     'ARMA': {'mae': 17967.872025480618,
      'mape': 0.11018538935044533,
      'r2': -1.5848853412068404,
      'rmse': 21202.055910589734},
     'AutoARIMA (1, 0, 0)': {'mae': 17889.69493700406,
      'mape': 0.10966924216063507,
      'r2': -1.621837191433773,
      'rmse': 21353.06359279361},
     'AutoSARIMAX (1, 0, 0),(0, 0, 0, 2)': {'mae': 8864.467639330236,
      'mape': 0.056112088410109284,
      'r2': 0.45758807312406724,
      'rmse': 9712.296871645192},
     'BayesianRidge': {'mae': 32083.08118414712,
      'mape': 0.1965845659934906,
      'r2': -6.65331929442584,
      'rmse': 36482.303315112265},
     'DeepAR': {'mae': 24734.603479962934,
      'mape': 37334046456473.664,
      'r2': -0.8237353721114968,
      'rmse': 29675.758487058385},
     'HWES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598},
     'KNeighbors': {'mae': 26703.55938142319,
      'mape': 0.16199238074064612,
      'r2': -4.948444729314532,
      'rmse': 32163.185010321733},
     'Kneighbors': {'mae': 20563.375,
      'mape': 0.12792141062143764,
      'r2': -1.7550727072789631,
      'rmse': 21888.896404627165},
     'Lasso': {'mae': 26703.55938142319,
      'mape': 0.16199238074064612,
      'r2': -4.948444729314532,
      'rmse': 32163.185010321733},
     'LightGBM': {'mae': 28783.92578125,
      'mape': 0.18607997755607655,
      'r2': -5.805708897742901,
      'rmse': 34402.81801251298},
     'Lightgbm': {'mae': 26929.42919847399,
      'mape': 0.1643673242160161,
      'r2': -5.443198832735387,
      'rmse': 33474.03765838882},
     'Lightgbm 2': {'mae': 8663.044135515223,
      'mape': 6385109908287.111,
      'r2': 0.7446250694607179,
      'rmse': 11104.782273939358},
     'MA': {'mae': 39521.41047490899,
      'mape': 0.24710985372592503,
      'r2': -8.765039332636457,
      'rmse': 41209.21408696808},
     'Naive mean': {'mae': 64380.63725490196,
      'mape': 0.40404962183236276,
      'r2': -23.833941831061757,
      'rmse': 65717.36828150737},
     'Prophet multivariate': {'mae': 48318.604453567124,
      'mape': 0.3015132129668772,
      'r2': -13.462674963130201,
      'rmse': 50151.23665351092},
     'Prophet univariate': {'mae': 77298.07473579783,
      'mape': 0.49195332504175276,
      'r2': -48.0286866474169,
      'rmse': 92338.42518565872},
     'Randomforest': {'mae': 15139.478225690436,
      'mape': 0.08995804794499901,
      'r2': -1.3505487518450678,
      'rmse': 20218.175645581698},
     'Randomforest tunned': {'mae': 8636.969898021141,
      'mape': 21818951242.1698,
      'r2': 0.6670228751265097,
      'rmse': 12680.258778851718},
     'SARIMAX': {'mae': 118119.60387865943,
      'mape': 0.7410413763825117,
      'r2': -87.03955029657169,
      'rmse': 123736.04810029041},
     'SES': {'mae': 13697.959852637745,
      'mape': 0.08706640386845278,
      'r2': -0.4372433819644075,
      'rmse': 15809.658623520598},
     'SVM RBF': {'mae': 69624.19191807767,
      'mape': 0.437486005678421,
      'r2': -27.874243137850005,
      'rmse': 70861.87477436503},
     'SVM RBF GRID SEARCH': {'mae': 19062.47413918481,
      'mape': 36833528735135.02,
      'r2': 0.0006016830906163673,
      'rmse': 21967.985036110087},
     'SVM RBF GRID SEARCH 2': {'mae': 59246.42986667021,
      'mape': 0.3712058353534466,
      'r2': -20.21925587949882,
      'rmse': 60746.66058607654},
     'Tensorflow simple LSTM': {'mae': 9934.940446690041,
      'mape': 1565292162214.229,
      'r2': 0.5923524282887452,
      'rmse': 14030.183366088855},
     'XGB_test': {'mae': 28783.92578125,
      'mape': 0.18607997755607655,
      'r2': -5.805708897742901,
      'rmse': 34402.81801251298},
     'XGBoost': {'mae': 28783.92578125,
      'mape': 0.18607997755607655,
      'r2': -5.805708897742901,
      'rmse': 34402.81801251298},
     'XGBoost 2': {'mae': 8663.044135515223,
      'mape': 6385109908287.111,
      'r2': 0.7446250694607179,
      'rmse': 11104.782273939358}}

```python
len(df_test.Amount_Spent.values)
```

    28

```python
models = ['Tensorflow simple LSTM',
          'Lightgbm 2',
          'XGBoost 2']

my_predictions = {}

for model in models:
  my_predictions[model] = predictionsDict[model]

resis = pd.DataFrame(data={k: df_test.Amount_Spent.values -
                     v for k, v in my_predictions.items()})[models]
corr = resis.corr()
print("Residuals correlation")
corr.style.background_gradient(cmap='coolwarm')
```

    Residuals correlation

        	        Tensorflow simple LSTM	        Lightgbm 2	        XGBoost 2

                        Tensorflow simple LSTM
                        1.000000
                        0.560352
                        0.171792

                        Lightgbm 2
                        0.560352
                        1.000000
                        0.774586

                        XGBoost 2
                        0.171792
                        0.774586
                        1.000000

```python
for k, v in predictionsDict.items():
  df_test.Amount_Spent.value
```

    Naive mean [93461.3627451 93461.3627451 93461.3627451 93461.3627451]
    SES [164754.4773981  175437.00967016 159167.49373743 150956.18659894]
    HWES [164754.4773981  175437.00967016 159167.49373743 150956.18659894]
    AR [147227.67729148 169115.57995311 131655.02493588 134529.08005057]
    MA [122619.23539821 125847.03166244 108547.56840332 116268.5226364 ]
    ARMA [147264.89865265 153499.73955671 133082.8798034  130674.47299875]
    ARIMA [147241.32593775 152392.29291125 132707.77177755 130278.41544794]
    AutoARIMA (1, 0, 0) [147241.32593775 152392.29291125 132707.77177755 130278.41544794]
    SARIMAX [22793.60869581     0.         74488.19911044 61607.77667911]
    AutoSARIMAX (1, 0, 0),(0, 0, 0, 2) [169048.7729616  162893.16187476 156961.69644238 151246.21479822]
    Prophet univariate [251437.79576848 167476.85368878  81926.80534318 310076.45482912]
    BayesianRidge [119005.55162671 122503.0289195  125140.13087208 136386.96384512]
    Lasso [123408.89283916 128005.55696666 131185.15733591 141954.15533257]
    Randomforest [142546.79479217 142546.79479217 143721.56854217 141994.92897074]
    XGBoost [154930.1  159020.89 206132.73 175648.17]
    LightGBM [154930.1  159020.89 206132.73 175648.17]
    SVM RBF [88208.58317484 88230.18085573 88166.32494252 88266.14335461]
    KNeighbors [123408.89283916 128005.55696666 131185.15733591 141954.15533257]
    XGB_test [154930.1  159020.89 206132.73 175648.17]
    Lightgbm [126096.38481247 114503.30490538 132475.6820312  156059.08854294]
    Kneighbors [147369.5 136188.  126612.  138945. ]
    Prophet multivariate [109070.14822032 109372.3131044  109674.47798847 109976.64287254]
    Tensorflow simple LSTM [  -96.47632 30348.607   42192.234   42536.863   42652.27    43354.3
     40065.152     854.7033  29984.857   40705.016   41517.83    41985.203
     42131.23    42323.133     547.0996  33377.332   40232.457   40745.477
     40772.406   43043.84    43319.504    2884.5388  32664.97    42422.35
     43366.324   43557.91    44395.17    45196.707  ]
    DeepAR [36066.188   32061.176   20070.365   28751.225   33500.19    15257.655
     32442.996   25156.676   81840.766   28988.422   38598.484   40878.438
     29921.098   28457.1     25356.764   42476.95    15079.401   28816.219
     22115.645   25807.264   29866.152   17955.703   33081.316     631.51373
     32758.283   32016.246   20218.332   24902.43   ]
    SVM RBF GRID SEARCH [25506.89841649 23164.52323904 34277.38384266 34138.82669843
     32649.44418987 30662.91887811 28164.43643198 25654.60733186
     23513.63041541 32688.62050806 32222.48951186 30998.3150707
     29324.81337287 27206.00614403 25109.84444897 22719.90753359
     29951.78957216 29566.36620846 28728.57948897 30929.39592892
     29037.51318681 26862.53026106 23667.80172095 34223.73402253
     34996.63660773 34024.50801046 32208.4221714  30202.39968471]
    SVM RBF GRID SEARCH 2 [98118.15713762 98698.93978364 98413.07870488 99152.10490718]
    Lightgbm 2 [ 2562.65521538 26027.10954604 57639.3728347  61576.5085816
     60299.74433186 54442.97652012 38909.69053675  3397.43752882
     25572.78997286 52425.00660533 50510.27685727 48305.25108834
     48597.0006058  39221.67426012  -865.45709249 22975.51172581
     55391.17324788 62363.6961076  52467.98190052 75623.70171055
     57525.33383008 11052.75790651 29668.94684571 68223.01872042
     68657.15493885 70536.66626345 66199.68214908 43188.9235504 ]
    XGBoost 2 [ 2562.65521538 26027.10954604 57639.3728347  61576.5085816
     60299.74433186 54442.97652012 38909.69053675  3397.43752882
     25572.78997286 52425.00660533 50510.27685727 48305.25108834
     48597.0006058  39221.67426012  -865.45709249 22975.51172581
     55391.17324788 62363.6961076  52467.98190052 75623.70171055
     57525.33383008 11052.75790651 29668.94684571 68223.01872042
     68657.15493885 70536.66626345 66199.68214908 43188.9235504 ]

```python
predictionsDict['EnsembleXG+LIGHT'] = (
    predictionsDict['XGBoost 2'] + predictionsDict['Lightgbm 2'])/2
resultsDict['EnsembleXG+LIGHT'] = evaluate(
    df_test.Amount_Spent.values, predictionsDict['EnsembleXG+LIGHT'])
```

```python
def full_extent(ax, pad=0.0):
    """Get the full extent of an axes, including axes labels, tick labels, and
    titles."""
    # For text objects, we need to draw the figure first, otherwise the extents
    # are undefined.
    ax.figure.canvas.draw()
    items = ax.get_xticklabels() + ax.get_yticklabels()
#    items += [ax, ax.title, ax.xaxis.label, ax.yaxis.label]
    items += [ax, ax.title]
    bbox = Bbox.union([item.get_window_extent() for item in items])

    return bbox.expanded(1.0 + pad, 1.0 + pad)
```

```python
import matplotlib.pyplot as plt
import pandas as pd

def bar_metrics(resultsDict):
    df = pd.DataFrame.from_dict(resultsDict)
    colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
    pallette = plt.cm.get_cmap("tab20c", len(df.columns))
    colors = [pallette(x) for x in range(len(df.columns))]
    color_dict = dict(zip(df.columns, colors))
    fig = plt.figure(figsize=(20, 15))

    # MAE plot
    fig.add_subplot(2, 2, 1)
    df.loc["mae"].sort_values().plot(
        kind="bar",
        colormap="Paired",
        color=[color_dict.get(x, "#333333") for x in df.loc["mae"].sort_values().index],
    )
    plt.legend()
    plt.title("MAE Metric, lower is better")
    fig.add_subplot(2, 2, 2)
    df.loc["rmse"].sort_values().plot(
        kind="bar",
        colormap="Paired",
        color=[
            color_dict.get(x, "#333333") for x in df.loc["rmse"].sort_values().index
        ],
    )
    plt.legend()
    plt.title("RMSE Metric, lower is better")
    fig.add_subplot(2, 2, 3)
    df.loc["mape"].sort_values().plot(
        kind="bar",
        colormap="Paired",
        color=[
            color_dict.get(x, "#333333") for x in df.loc["mape"].sort_values().index
        ],
    )
    plt.legend()
    plt.title("MAPE Metric, lower is better")
    fig.add_subplot(2, 2, 4)
    df.loc["r2"].sort_values(ascending=False).plot(
        kind="bar",
        colormap="Paired",
        color=[
            color_dict.get(x, "#333333")
            for x in df.loc["r2"].sort_values(ascending=False).index
        ],
    )
    plt.legend()
    plt.title("R2 Metric, higher is better")
    plt.tight_layout()
    plt.savefig(f"{FOLDER_PATH}metrics.png")
    plt.show()

```

```python
FOLDER_PATH
```

    '/content/drive/MyDrive/Colab_Notebooks/online_retail/'

```python
bar_metrics(resultsDict)
```

```python
my_predictions
```

    {'Lightgbm 2': array([ 2562.65521538, 26027.10954604, 57639.3728347 , 61576.5085816 ,
            60299.74433186, 54442.97652012, 38909.69053675,  3397.43752882,
            25572.78997286, 52425.00660533, 50510.27685727, 48305.25108834,
            48597.0006058 , 39221.67426012,  -865.45709249, 22975.51172581,
            55391.17324788, 62363.6961076 , 52467.98190052, 75623.70171055,
            57525.33383008, 11052.75790651, 29668.94684571, 68223.01872042,
            68657.15493885, 70536.66626345, 66199.68214908, 43188.9235504 ]),
     'Tensorflow simple LSTM': array([  -96.47632, 30348.607  , 42192.234  , 42536.863  , 42652.27   ,
            43354.3    , 40065.152  ,   854.7033 , 29984.857  , 40705.016  ,
            41517.83   , 41985.203  , 42131.23   , 42323.133  ,   547.0996 ,
            33377.332  , 40232.457  , 40745.477  , 40772.406  , 43043.84   ,
            43319.504  ,  2884.5388 , 32664.97   , 42422.35   , 43366.324  ,
            43557.91   , 44395.17   , 45196.707  ], dtype=float32),
     'XGBoost 2': array([  6395.383 ,  26448.268 ,  59023.19  ,  66680.125 ,  59674.598 ,
             58064.605 ,  27729.172 ,   8699.61  ,  31062.46  ,  49335.43  ,
             42020.566 ,  46011.633 ,  48526.746 ,  52400.723 ,  -3073.0815,
             26684.748 ,  43252.11  ,  64654.562 ,  49271.87  , 104955.59  ,
             59908.45  ,  21954.717 ,  30290.111 ,  94024.33  ,  79815.18  ,
             94258.49  ,  87185.07  ,  42724.89  ], dtype=float32)}

```python
test_df
```

      Quantity
      date
      hour
      dayofweek
      quarter
      month
      year
      dayofyear
      sin_day
      cos_day
      dayofmonth
      weekofyear

      Date

      2011-11-13
      180316
      2011-11-13
      0
      6
      4
      11
      2011
      317
      0.296340
      -0.955083
      13
      45

      2011-11-20
      150987
      2011-11-20
      0
      6
      4
      11
      2011
      324
      -0.404065
      -0.914730
      20
      46

      2011-11-27
      146748
      2011-11-27
      0
      6
      4
      11
      2011
      331
      -0.905591
      -0.424152
      27
      47

      2011-12-04
      153317
      2011-12-04
      0
      6
      4
      12
      2011
      338
      -0.961389
      0.275192
      4
      48

```python
df_test
```

      Amount_Spent
      date
      hour
      dayofweek
      quarter
      month
      year
      dayofyear
      sin_day
      cos_day
      dayofmonth
      weekofyear

      Date

      2011-11-12
      0.00
      2011-11-12
      0
      5
      4
      11
      2011
      316
      0.963787
      -0.266672
      12
      45

      2011-11-13
      29192.66
      2011-11-13
      0
      6
      4
      11
      2011
      317
      0.296340
      -0.955083
      13
      45

      2011-11-14
      74546.77
      2011-11-14
      0
      0
      4
      11
      2011
      318
      -0.643561
      -0.765395
      14
      46

      2011-11-15
      53090.36
      2011-11-15
      0
      1
      4
      11
      2011
      319
      -0.991775
      0.127994
      15
      46

      2011-11-16
      47897.82
      2011-11-16
      0
      2
      4
      11
      2011
      320
      -0.428155
      0.903705
      16
      46

      2011-11-17
      54839.12
      2011-11-17
      0
      3
      4
      11
      2011
      321
      0.529108
      0.848554
      17
      46

      2011-11-18
      40542.11
      2011-11-18
      0
      4
      4
      11
      2011
      322
      0.999912
      0.013247
      18
      46

      2011-11-19
      0.00
      2011-11-19
      0
      5
      4
      11
      2011
      323
      0.551402
      -0.834240
      19
      46

      2011-11-20
      30236.18
      2011-11-20
      0
      6
      4
      11
      2011
      324
      -0.404065
      -0.914730
      20
      46

      2011-11-21
      42699.18
      2011-11-21
      0
      0
      4
      11
      2011
      325
      -0.988036
      -0.154222
      21
      47

      2011-11-22
      51618.89
      2011-11-22
      0
      1
      4
      11
      2011
      326
      -0.663611
      0.748078
      22
      47

      2011-11-23
      68007.83
      2011-11-23
      0
      2
      4
      11
      2011
      327
      0.270935
      0.962598
      23
      47

      2011-11-24
      40197.87
      2011-11-24
      0
      3
      4
      11
      2011
      328
      0.956385
      0.292110
      24
      47

      2011-11-25
      31659.94
      2011-11-25
      0
      4
      4
      11
      2011
      329
      0.762539
      -0.646942
      25
      47

      2011-11-26
      0.00
      2011-11-26
      0
      5
      4
      11
      2011
      330
      -0.132382
      -0.991199
      26
      47

      2011-11-27
      16692.97
      2011-11-27
      0
      6
      4
      11
      2011
      331
      -0.905591
      -0.424152
      27
      47

      2011-11-28
      46887.24
      2011-11-28
      0
      0
      4
      11
      2011
      332
      -0.846204
      0.532859
      28
      48

      2011-11-29
      50015.18
      2011-11-29
      0
      1
      4
      11
      2011
      333
      -0.008821
      0.999961
      29
      48

      2011-11-30
      45494.72
      2011-11-30
      0
      2
      4
      11
      2011
      334
      0.836672
      0.547704
      30
      48

      2011-12-01
      44725.16
      2011-12-01
      0
      3
      4
      12
      2011
      335
      0.912933
      -0.408110
      1
      48

      2011-12-02
      44524.11
      2011-12-02
      0
      4
      4
      12
      2011
      336
      0.149847
      -0.988709
      2
      48

      2011-12-03
      0.00
      2011-12-03
      0
      5
      4
      12
      2011
      337
      -0.751007
      -0.660294
      3
      48

      2011-12-04
      22109.25
      2011-12-04
      0
      6
      4
      12
      2011
      338
      -0.961389
      0.275192
      4
      48

      2011-12-05
      71461.87
      2011-12-05
      0
      0
      4
      12
      2011
      339
      -0.287874
      0.957668
      5
      49

      2011-12-06
      53339.40
      2011-12-06
      0
      1
      4
      12
      2011
      340
      0.650311
      0.759668
      6
      49

      2011-12-07
      74424.09
      2011-12-07
      0
      2
      4
      12
      2011
      341
      0.990603
      -0.136767
      7
      49

      2011-12-08
      63484.82
      2011-12-08
      0
      3
      4
      12
      2011
      342
      0.420140
      -0.907459
      8
      49

      2011-12-09
      23379.46
      2011-12-09
      0
      4
      4
      12
      2011
      343
      -0.536598
      -0.843838
      9
      49

```python
# Generate the aggregated weekly sales
# dated_retail_data = cleaned_data.copy()
df_test["Date"] = pd.to_datetime(df_test["date"]) - pd.to_timedelta(7, unit="d")
df_test.index = df_test["Date"]
#resampling to get the weekly sales and returns
weekly_sale_test = df_test[df_test['Amount_Spent']>0].Amount_Spent.resample('W').sum()
# weekly_retail = dated_retail_data[dated_retail_data['Quantity']<0].Quantity.resample('W').sum().abs()
```

```python
weekly_sale_test
```

    Date
    2011-11-06     29192.66
    2011-11-13    301152.36
    2011-11-20    250876.68
    2011-11-27    253755.66
    2011-12-04    286089.64
    Freq: W-SUN, Name: Amount_Spent, dtype: float64

```python
xgboost_pred = predictionsDict["XGBoost 2"]

xg_boost = pd.DataFrame(xgboost_pred,columns=["Amount_Spent"])
xg_boost.index = df_test.index
```

```python
xg_boost
```

      Amount_Spent

      Date

      2011-11-05
      6395.382812

      2011-11-06
      26448.267578

      2011-11-07
      59023.191406

      2011-11-08
      66680.125000

      2011-11-09
      59674.597656

      2011-11-10
      58064.605469

      2011-11-11
      27729.171875

      2011-11-12
      8699.610352

      2011-11-13
      31062.460938

      2011-11-14
      49335.429688

      2011-11-15
      42020.566406

      2011-11-16
      46011.632812

      2011-11-17
      48526.746094

      2011-11-18
      52400.722656

      2011-11-19
      -3073.081543

      2011-11-20
      26684.748047

      2011-11-21
      43252.109375

      2011-11-22
      64654.562500

      2011-11-23
      49271.871094

      2011-11-24
      104955.593750

      2011-11-25
      59908.449219

      2011-11-26
      21954.716797

      2011-11-27
      30290.111328

      2011-11-28
      94024.328125

      2011-11-29
      79815.179688

      2011-11-30
      94258.492188

      2011-12-01
      87185.070312

      2011-12-02
      42724.890625

```python
weekly_xg_boost = xg_boost[xg_boost['Amount_Spent']>0].Amount_Spent.resample('W').sum()
```

```python
weekly_xg_boost
```

    Date
    2011-11-06     32843.648438
    2011-11-13    310933.750000
    2011-11-20    264979.843750
    2011-11-27    374287.406250
    2011-12-04    398007.968750
    Freq: W-SUN, Name: Amount_Spent, dtype: float32

```python
evaluate(weekly_sale_test.values,
         weekly_xg_boost.values)
```

    {'mae': 51997.12343750037,
     'mape': 0.2159904517667238,
     'r2': 0.4457308209189358,
     'rmse': 73975.0210529609}