{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "block_hidden": true
   },
   "outputs": [],
   "source": [
    "%load_ext rpy2.ipython\n",
    "%matplotlib inline\n",
    "\n",
    "from prophet import Prophet\n",
    "from matplotlib import pyplot as plt\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import logging\n",
    "import warnings\n",
    "\n",
    "logging.getLogger('prophet').setLevel(logging.ERROR)\n",
    "logging.getLogger('numexpr').setLevel(logging.ERROR)\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "\n",
    "df = pd.read_csv('https://raw.githubusercontent.com/facebook/prophet/main/examples/example_wp_log_peyton_manning.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "block_hidden": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "R[write to console]: Loading required package: Rcpp\n",
      "\n",
      "R[write to console]: Loading required package: rlang\n",
      "\n",
      "R[write to console]: Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.\n",
      "\n",
      "R[write to console]: Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%R\n",
    "library(prophet)\n",
    "df <- read.csv('https://raw.githubusercontent.com/facebook/prophet/main/examples/example_wp_log_peyton_manning.csv')\n",
    "m <- prophet(df)\n",
    "future <- make_future_dataframe(m, periods=366)\n",
    "m <- prophet(df)\n",
    "forecast <- predict(m, future)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You may have noticed in the earlier examples in this documentation that real time series frequently have abrupt changes in their trajectories. By default, Prophet will automatically detect these changepoints and will allow the trend to adapt appropriately. However, if you wish to have finer control over this process (e.g., Prophet missed a rate change, or is overfitting rate changes in the history), then there are several input arguments you can use."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Automatic changepoint detection in Prophet\n",
    "Prophet detects changepoints by first specifying a large number of *potential changepoints* at which the rate is allowed to change. It then puts a sparse prior on the magnitudes of the rate changes (equivalent to L1 regularization) - this essentially means that Prophet has a large number of *possible* places where the rate can change, but will use as few of them as possible. Consider the Peyton Manning forecast from the Quickstart. By default, Prophet specifies 25 potential changepoints which are uniformly placed in the first 80% of the time series. The vertical lines in this figure indicate where the potential changepoints were placed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "input_hidden": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 147296 characters]\n",
      "text/plain": [
       "<Figure size 720x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "m = Prophet()\n",
    "m.fit(df)\n",
    "future = m.make_future_dataframe(periods=366)\n",
    "forecast = m.predict(future)\n",
    "fig = m.plot(forecast)\n",
    "for cp in m.changepoints:\n",
    "    plt.axvline(cp, c='gray', ls='--', lw=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Even though we have a lot of places where the rate can possibly change, because of the sparse prior, most of these changepoints go unused. We can see this by plotting the magnitude of the rate change at each changepoint:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "input_hidden": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 14000 characters]\n",
      "text/plain": [
       "<Figure size 720x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "deltas = m.params['delta'].mean(0)\n",
    "fig = plt.figure(facecolor='w', figsize=(10, 6))\n",
    "ax = fig.add_subplot(111)\n",
    "ax.bar(range(len(deltas)), deltas, facecolor='#0072B2', edgecolor='#0072B2')\n",
    "ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)\n",
    "ax.set_ylabel('Rate change')\n",
    "ax.set_xlabel('Potential changepoint')\n",
    "fig.tight_layout()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The number of potential changepoints can be set using the argument `n_changepoints`, but this is better tuned by adjusting the regularization. The locations of the signification changepoints can be visualized with:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "output_hidden": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 135116 characters]\n"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%R -w 10 -h 6 -u in\n",
    "plot(m, forecast) + add_changepoints_to_plot(m)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 155900 characters]\n",
      "text/plain": [
       "<Figure size 720x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from prophet.plot import add_changepoints_to_plot\n",
    "fig = m.plot(forecast)\n",
    "a = add_changepoints_to_plot(fig.gca(), m, forecast)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By default changepoints are only inferred for the first 80% of the time series in order to have plenty of runway for projecting the trend forward and to avoid overfitting fluctuations at the end of the time series. This default works in many situations but not all, and can be changed using the `changepoint_range` argument. For example, `m = Prophet(changepoint_range=0.9)` in Python or `m <- prophet(changepoint.range = 0.9)` in R will place potential changepoints in the first 90% of the time series."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Adjusting trend flexibility\n",
    "If the trend changes are being overfit (too much flexibility) or underfit (not enough flexibility), you can adjust the strength of the sparse prior using the input argument `changepoint_prior_scale`. By default, this parameter is set to 0.05. Increasing it will make the trend *more* flexible:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "output_hidden": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "R[write to console]: Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 111732 characters]\n"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%R -w 10 -h 6 -u in\n",
    "m <- prophet(df, changepoint.prior.scale = 0.5)\n",
    "forecast <- predict(m, future)\n",
    "plot(m, forecast)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 136204 characters]\n",
      "text/plain": [
       "<Figure size 720x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "m = Prophet(changepoint_prior_scale=0.5)\n",
    "forecast = m.fit(df).predict(future)\n",
    "fig = m.plot(forecast)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Decreasing it will make the trend *less* flexible:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "output_hidden": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "R[write to console]: Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 114876 characters]\n"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%R -w 10 -h 6 -u in\n",
    "m <- prophet(df, changepoint.prior.scale = 0.001)\n",
    "forecast <- predict(m, future)\n",
    "plot(m, forecast)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 145740 characters]\n",
      "text/plain": [
       "<Figure size 720x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "m = Prophet(changepoint_prior_scale=0.001)\n",
    "forecast = m.fit(df).predict(future)\n",
    "fig = m.plot(forecast)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When visualizing the forecast, this parameter can be adjusted as needed if the trend seems to be over- or under-fit. In the fully-automated setting, see the documentation on cross validation for recommendations on how this parameter can be tuned."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Specifying the locations of the changepoints"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you wish, rather than using automatic changepoint detection you can manually specify the locations of potential changepoints with the `changepoints` argument. Slope changes will then be allowed only at these points, with the same sparse regularization as before. One could, for instance, create a grid of points as is done automatically, but then augment that grid with some specific dates that are known to be likely to have changes. As another example, the changepoints could be entirely limited to a small set of dates, as is done here:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "output_hidden": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "R[write to console]: Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 116060 characters]\n"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%R -w 10 -h 6 -u in\n",
    "m <- prophet(df, changepoints = c('2014-01-01'))\n",
    "forecast <- predict(m, future)\n",
    "plot(m, forecast)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 146304 characters]\n",
      "text/plain": [
       "<Figure size 720x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "m = Prophet(changepoints=['2014-01-01'])\n",
    "forecast = m.fit(df).predict(future)\n",
    "fig = m.plot(forecast)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
