import pandas as pd
import numpy as np
from fbprophet import Prophet
import matplotlib.pyplot as plt
 
%matplotlib inline
 
plt.rcParams['figure.figsize']=(20,10)
plt.style.use('ggplot')

"""
Explanation: This notebook covers the use of 'holidays' in the Prophet forecasting library. In this notebook, we will extend the previous example (http://pythondata.com/forecasting-time-series-data-prophet-jupyter-notebook/) to use holidays in the forecasting.
Import necessary libraries
End of explanation
"""


sales_df = pd.read_csv('../examples/retail_sales.csv', index_col='date', parse_dates=True)

sales_df.head()

"""
Explanation: Read in the data
Read the data in from the retail sales CSV file in the examples folder then set the index to the 'date' column. We are also parsing dates in the data file.
End of explanation
"""


df = sales_df.reset_index()

df.head()

"""
Explanation: Prepare for Prophet
As explained in previous prophet posts, for prophet to work, we need to change the names of these columns to 'ds' and 'y'.
End of explanation
"""


df=df.rename(columns={'date':'ds', 'sales':'y'})

df.head()

"""
Explanation: Let's rename the columns as required by fbprophet. Additioinally, fbprophet doesn't like the index to be a datetime...it wants to see 'ds' as a non-index column, so we won't set an index differnetly than the integer index.
End of explanation
"""


df.set_index('ds').y.plot()

"""
Explanation: Now's a good time to take a look at your data.  Plot the data using pandas' plot function
End of explanation
"""


promotions = pd.DataFrame({
  'holiday': 'december_promotion',
  'ds': pd.to_datetime(['2009-12-01', '2010-12-01', '2011-12-01', '2012-12-01',
                        '2013-12-01', '2014-12-01', '2015-12-01']),
  'lower_window': 0,
  'upper_window': 0,
})

promotions

"""
Explanation: Reviewing the Data
We can see from this data that there is a spike in the same month each year. While spike could be due to many different reasons, let's assume its because there's a major promotion that this company runs every year at that time, which is in December for this dataset.
Because we know this promotion occurs every december, we want to use this knowledge to help prophet better forecast those months, so we'll use prohpet's holiday construct (explained here https://facebookincubator.github.io/prophet/docs/holiday_effects.html).
The holiday constrict is a pandas dataframe with the holiday and date of the holiday. For this example, the construct would look like this:
promotions = pd.DataFrame({
  'holiday': 'december_promotion',
  'ds': pd.to_datetime(['2009-12-01', '2010-12-01', '2011-12-01', '2012-12-01',
                        '2013-12-01', '2014-12-01', '2015-12-01']),
  'lower_window': 0,
  'upper_window': 0,
})
This promotions dataframe consisists of promotion dates for Dec in 2009 through 2015,  The lower_window and upper_window values are set to zero to indicate that we don't want prophet to consider any other months than the ones listed.
End of explanation
"""


df['y'] = np.log(df['y'])

df.tail()

"""
Explanation: To continue, we need to log-transform our data:
End of explanation
"""


model = Prophet(holidays=promotions)
model.fit(df);

"""
Explanation: Running Prophet
Now, let's set prophet up to begin modeling our data using our promotions dataframe as part of the forecast
Note: Since we are using monthly data, you'll see a message from Prophet saying Disabling weekly seasonality. Run prophet with weekly_seasonality=True to override this.  This is OK since we are workign with monthly data but you can disable it by using weekly_seasonality=True in the instantiation of Prophet.
End of explanation
"""


future = model.make_future_dataframe(periods=24, freq = 'm')
future.tail()

"""
Explanation: We've instantiated the model, now we need to build some future dates to forecast into.
End of explanation
"""


forecast = model.predict(future)

"""
Explanation: To forecast this future data, we need to run it through Prophet's model.
End of explanation
"""


forecast.tail()

"""
Explanation: The resulting forecast dataframe contains quite a bit of data, but we really only care about a few columns.  First, let's look at the full dataframe:
End of explanation
"""


forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()

"""
Explanation: We really only want to look at yhat, yhat_lower and yhat_upper, so we can do that with:
End of explanation
"""


model.plot(forecast);

"""
Explanation: Plotting Prophet results
Prophet has a plotting mechanism called plot.  This plot functionality draws the original data (black dots), the model (blue line) and the error of the forecast (shaded blue area).
End of explanation
"""


model.plot_components(forecast);

"""
Explanation: Personally, I'm not a fan of this visualization but I'm not going to build my own...you can see how I do that here:
https://github.com/urgedata/pythondata/blob/master/fbprophet/fbprophet_part_one.ipynb.
Additionally, prophet let's us take a at the components of our model, including the holidays. This component plot is an important plot as it lets you see the components of your model including the trend and seasonality (identified in the yearly pane).
End of explanation
"""


model_no_holiday = Prophet()
model_no_holiday.fit(df);

future_no_holiday = model_no_holiday.make_future_dataframe(periods=24, freq = 'm')
future_no_holiday.tail()

forecast_no_holiday = model_no_holiday.predict(future)

"""
Explanation: Comparing holidays vs no-holidays forecasts
Let's re-run our prophet model without our promotions/holidays for comparison.
End of explanation
"""


forecast.set_index('ds', inplace=True)
forecast_no_holiday.set_index('ds', inplace=True)
compared_df = forecast.join(forecast_no_holiday, rsuffix="_no_holiday")

"""
Explanation: Let's compare the two forecasts now. Note: I doubt there will be much difference in these models due to the small amount of data, but its a good example to see the process. We'll set the indexes and then join the forecast dataframes into a new dataframe called 'compared_df'.
End of explanation
"""


compared_df= np.exp(compared_df[['yhat', 'yhat_no_holiday']])


"""
Explanation: We are only really insterested in the yhat values, so let's remove all the rest and convert the logged values back to their original scale.
End of explanation
"""


compared_df['diff_per'] = 100*(compared_df['yhat'] - compared_df['yhat_no_holiday']) / compared_df['yhat_no_holiday']
compared_df.tail()

compared_df['diff_per'].mean()

"""
Explanation: Now, let's take the percentage difference and the average difference for the model with holidays vs that without.
End of explanation
"""


