{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 158,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from datetime import datetime\n",
    "from datetime import timedelta\n",
    "from pandas.plotting import register_matplotlib_converters\n",
    "from statsmodels.tsa.stattools import acf, pacf\n",
    "from statsmodels.tsa.statespace.sarimax import SARIMAX\n",
    "register_matplotlib_converters()\n",
    "from time import time"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Catfish Sales Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 159,
   "metadata": {},
   "outputs": [],
   "source": [
    "def parser(s):\n",
    "    return datetime.strptime(s, '%Y-%m-%d')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 160,
   "metadata": {},
   "outputs": [],
   "source": [
    "#read data\n",
    "catfish_sales = pd.read_csv('catfish.csv', parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 161,
   "metadata": {},
   "outputs": [],
   "source": [
    "#infer the frequency of the data\n",
    "catfish_sales = catfish_sales.asfreq(pd.infer_freq(catfish_sales.index))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 162,
   "metadata": {},
   "outputs": [],
   "source": [
    "start_date = datetime(1996,1,1)\n",
    "end_date = datetime(2000,1,1)\n",
    "lim_catfish_sales = catfish_sales[start_date:end_date]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 163,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 38716 characters]\n",
      "text/plain": [
       "<Figure size 720x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10,4))\n",
    "plt.plot(lim_catfish_sales)\n",
    "plt.title('Catfish Sales in 1000s of Pounds', fontsize=20)\n",
    "plt.ylabel('Sales', fontsize=16)\n",
    "for year in range(start_date.year,end_date.year):\n",
    "    plt.axvline(pd.to_datetime(str(year)+'-01-01'), color='k', linestyle='--', alpha=0.2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Remove the trend"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 164,
   "metadata": {},
   "outputs": [],
   "source": [
    "first_diff = lim_catfish_sales.diff()[1:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 165,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.lines.Line2D at 0x26731b2af48>"
      ]
     },
     "execution_count": 165,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 44376 characters]\n",
      "text/plain": [
       "<Figure size 720x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10,4))\n",
    "plt.plot(first_diff)\n",
    "plt.title('Catfish Sales in 1000s of Pounds', fontsize=20)\n",
    "plt.ylabel('Sales', fontsize=16)\n",
    "for year in range(start_date.year,end_date.year):\n",
    "    plt.axvline(pd.to_datetime(str(year)+'-01-01'), color='k', linestyle='--', alpha=0.2)\n",
    "plt.axhline(0, color='k', linestyle='--', alpha=0.2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# ACF"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 166,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<BarContainer object of 20 artists>"
      ]
     },
     "execution_count": 166,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 6204 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "acf_vals = acf(first_diff)\n",
    "num_lags = 20\n",
    "plt.bar(range(num_lags), acf_vals[:num_lags])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Based on ACF, we should start with a seasonal MA process"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PACF"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 236,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<BarContainer object of 15 artists>"
      ]
     },
     "execution_count": 236,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 5532 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "pacf_vals = pacf(first_diff)\n",
    "num_lags = 15\n",
    "plt.bar(range(num_lags), pacf_vals[:num_lags])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Based on PACF, we should start with a seasonal AR process"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Get training and testing sets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 194,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_end = datetime(1999,7,1)\n",
    "test_end = datetime(2000,1,1)\n",
    "\n",
    "train_data = lim_catfish_sales[:train_end]\n",
    "test_data = lim_catfish_sales[train_end + timedelta(days=1):test_end]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Fit the SARIMA Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 196,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_order = (0,1,0)\n",
    "my_seasonal_order = (1, 0, 1, 12)\n",
    "# define model\n",
    "model = SARIMAX(train_data, order=my_order, seasonal_order=my_seasonal_order)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 197,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model Fitting Time: 0.4917430877685547\n"
     ]
    }
   ],
   "source": [
    "#fit the model\n",
    "start = time()\n",
    "model_fit = model.fit()\n",
    "end = time()\n",
    "print('Model Fitting Time:', end - start)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 198,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                                 Statespace Model Results                                 \n",
      "==========================================================================================\n",
      "Dep. Variable:                              Total   No. Observations:                   43\n",
      "Model:             SARIMAX(0, 1, 0)x(1, 0, 1, 12)   Log Likelihood                -365.635\n",
      "Date:                            Fri, 03 Apr 2020   AIC                            737.269\n",
      "Time:                                    16:43:30   BIC                            742.482\n",
      "Sample:                                01-01-1996   HQIC                           739.180\n",
      "                                     - 07-01-1999                                         \n",
      "Covariance Type:                              opg                                         \n",
      "==============================================================================\n",
      "                 coef    std err          z      P>|z|      [0.025      0.975]\n",
      "------------------------------------------------------------------------------\n",
      "ar.S.L12       0.8250      0.120      6.848      0.000       0.589       1.061\n",
      "ma.S.L12      -0.5187      0.197     -2.632      0.008      -0.905      -0.132\n",
      "sigma2       1.78e+06    4.7e+05      3.791      0.000     8.6e+05     2.7e+06\n",
      "===================================================================================\n",
      "Ljung-Box (Q):                       55.29   Jarque-Bera (JB):                 1.13\n",
      "Prob(Q):                              0.05   Prob(JB):                         0.57\n",
      "Heteroskedasticity (H):               0.81   Skew:                            -0.30\n",
      "Prob(H) (two-sided):                  0.70   Kurtosis:                         2.46\n",
      "===================================================================================\n",
      "\n",
      "Warnings:\n",
      "[1] Covariance matrix calculated using the outer product of gradients (complex-step).\n"
     ]
    }
   ],
   "source": [
    "#summary of the model\n",
    "print(model_fit.summary())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 215,
   "metadata": {},
   "outputs": [],
   "source": [
    "#get the predictions and residuals\n",
    "predictions = model_fit.forecast(len(test_data))\n",
    "predictions = pd.Series(predictions, index=test_data.index)\n",
    "residuals = test_data - predictions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 223,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0, 0.5, 'Error')"
      ]
     },
     "execution_count": 223,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 25400 characters]\n",
      "text/plain": [
       "<Figure size 720x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10,4))\n",
    "plt.plot(residuals)\n",
    "plt.axhline(0, linestyle='--', color='k')\n",
    "plt.title('Residuals from SARIMA Model', fontsize=20)\n",
    "plt.ylabel('Error', fontsize=16)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 217,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 44904 characters]\n",
      "text/plain": [
       "<Figure size 720x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10,4))\n",
    "\n",
    "plt.plot(lim_catfish_sales)\n",
    "plt.plot(predictions)\n",
    "\n",
    "plt.legend(('Data', 'Predictions'), fontsize=16)\n",
    "\n",
    "plt.title('Catfish Sales in 1000s of Pounds', fontsize=20)\n",
    "plt.ylabel('Production', fontsize=16)\n",
    "for year in range(start_date.year,end_date.year):\n",
    "    plt.axvline(pd.to_datetime(str(year)+'-01-01'), color='k', linestyle='--', alpha=0.2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 218,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Mean Absolute Percent Error: 0.0433\n"
     ]
    }
   ],
   "source": [
    "print('Mean Absolute Percent Error:', round(np.mean(abs(residuals/test_data)),4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 219,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Root Mean Squared Error: 1122.3009354240558\n"
     ]
    }
   ],
   "source": [
    "print('Root Mean Squared Error:', np.sqrt(np.mean(residuals**2)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Using the Rolling Forecast Origin"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 229,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\ritvik\\Anaconda3\\lib\\site-packages\\statsmodels\\tsa\\statespace\\sarimax.py:993: UserWarning: Non-invertible starting seasonal moving average Using zeros as starting parameters.\n",
      "  warn('Non-invertible starting seasonal moving average'\n",
      "C:\\Users\\ritvik\\Anaconda3\\lib\\site-packages\\statsmodels\\tsa\\statespace\\sarimax.py:981: UserWarning: Non-stationary starting seasonal autoregressive Using zeros as starting parameters.\n",
      "  warn('Non-stationary starting seasonal autoregressive'\n"
     ]
    }
   ],
   "source": [
    "rolling_predictions = test_data.copy()\n",
    "for train_end in test_data.index:\n",
    "    train_data = lim_catfish_sales[:train_end-timedelta(days=1)]\n",
    "    model = SARIMAX(train_data, order=my_order, seasonal_order=my_seasonal_order)\n",
    "    model_fit = model.fit()\n",
    "\n",
    "    pred = model_fit.forecast()\n",
    "    rolling_predictions[train_end] = pred"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 230,
   "metadata": {},
   "outputs": [],
   "source": [
    "rolling_residuals = test_data - rolling_predictions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 231,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0, 0.5, 'Error')"
      ]
     },
     "execution_count": 231,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 31008 characters]\n",
      "text/plain": [
       "<Figure size 720x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10,4))\n",
    "plt.plot(rolling_residuals)\n",
    "plt.axhline(0, linestyle='--', color='k')\n",
    "plt.title('Rolling Forecast Residuals from SARIMA Model', fontsize=20)\n",
    "plt.ylabel('Error', fontsize=16)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 232,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 45112 characters]\n",
      "text/plain": [
       "<Figure size 720x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10,4))\n",
    "\n",
    "plt.plot(lim_catfish_sales)\n",
    "plt.plot(rolling_predictions)\n",
    "\n",
    "plt.legend(('Data', 'Predictions'), fontsize=16)\n",
    "\n",
    "plt.title('Catfish Sales in 1000s of Pounds', fontsize=20)\n",
    "plt.ylabel('Production', fontsize=16)\n",
    "for year in range(start_date.year,end_date.year):\n",
    "    plt.axvline(pd.to_datetime(str(year)+'-01-01'), color='k', linestyle='--', alpha=0.2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 233,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Mean Absolute Percent Error: 0.0288\n"
     ]
    }
   ],
   "source": [
    "print('Mean Absolute Percent Error:', round(np.mean(abs(rolling_residuals/test_data)),4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 234,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Root Mean Squared Error: 803.9733619136064\n"
     ]
    }
   ],
   "source": [
    "print('Root Mean Squared Error:', np.sqrt(np.mean(rolling_residuals**2)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
