Forecasting with Prophet | ||
---|---|---|
Resources consulted: YouTube Tutorial & Kaggle Notebook |
Evan Marie online: | EvanMarie.com| EvanMarie@Proton.me | Linked In | GitHub | Hugging Face | Mastadon | Jovian.ai | TikTok | CodeWars | Discord ⇨ ✨ EvanMarie ✨#6114 | |
from helpers_elegant import *
import_all()
plt.style.use('elegant.mplstyle')
import seaborn as sns
from prophet import Prophet
from sklearn.metrics import mean_squared_error as mse
from sklearn.metrics import mean_absolute_error as mae
from sklearn.metrics import mean_absolute_percentage_error as mape
import warnings
from tqdm import tqdm
warnings.filterwarnings("ignore")
%matplotlib inline
colors = sns.color_palette("husl", 20)
clr = list(map(mpl.colors.rgb2hex, colors))
from random import choice as rc
/Users/evancarr/opt/anaconda3/envs/sktime_project_02_05_23/lib/python3.10/site-packages/tqdm/auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html from .autonotebook import tqdm as notebook_tqdm
%%html
<style>
a:link {color: pink !important; font-weight: 600 !important;}
a:visited {color: pink !important; font-weight: 600 !important;}
</style>
data = pd.read_csv('pjme.csv',
index_col=[0],
parse_dates=[0])
df_overview(data, 'imported data')
PJME_MW | |
---|---|
datatype | float64 |
missing values | 0 |
count | 145,366.00 |
mean | 32,080.22 |
std | 6,464.01 |
min | 14,544.00 |
25% | 27,573.00 |
50% | 31,421.00 |
75% | 35,650.00 |
max | 62,009.00 |
total rows | 145,366 |
---|---|
total columns | 1 |
column names | PJME_MW |
index start | 2002-12-31 01:00:00 |
index end | 2018-01-02 00:00:00 |
total missing values | 0 |
imported data Head and Tail |
PJME_MW | |
---|---|
Datetime | |
2002-12-31 | 26,498.00 |
2002-12-31 | 25,147.00 |
2002-12-31 | 24,574.00 |
PJME_MW | |
---|---|
Datetime | |
2018-01-01 | 42,402.00 |
2018-01-01 | 40,164.00 |
2018-01-02 | 38,608.00 |
Initial Data Visualization |
---|
fix, axes = plt.subplots(5, 1, figsize = (12, 17))
data.plot(ax = axes[0], title = 'PJME: Energy Consumption Hourly', ms=.5, c = rc(clr))
data.resample('D').mean().plot(ax = axes[1], title = 'PJME: Daily Avg', c = rc(clr))
data.resample('W').mean().plot(ax = axes[2], title = 'PJME: Weekly Avg', c = rc(clr))
data.resample('M').mean().plot(ax = axes[3], title = 'PJME: Monthly Avg', c = rc(clr))
data.resample('A').mean().plot(ax = axes[4], title = 'PJME: Yearly Avg', c = rc(clr))
axes[0].legend().remove(); axes[1].legend().remove();
axes[2].legend().remove(); axes[3].legend().remove();
axes[4].legend().remove();
plt.tight_layout()
Featurized Data |
---|
featurized = featurize_dt_index(data)
df_overview(featurized, 'featurized_data')
PJME_MW | hour | weekday | weekday_name | month | month_name | quarter | year | week_of_year | day_of_year | date_offset | season | time_of_day | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
datatype | float64 | int64 | int64 | category | int64 | category | int64 | int64 | float64 | int64 | int64 | category | category |
missing values | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 408 | 0 |
count | 145,366.00 | 145,366.00 | 145,366.00 | nan | 145,366.00 | nan | 145,366.00 | 145,366.00 | 145,366.00 | 145,366.00 | 145,366.00 | nan | nan |
mean | 32,080.22 | 11.50 | 3.00 | nan | 6.44 | nan | 2.48 | 2,009.80 | 26.22 | 180.46 | 624.66 | nan | nan |
std | 6,464.01 | 6.92 | 2.00 | nan | 3.44 | nan | 1.11 | 4.79 | 15.02 | 105.14 | 380.84 | nan | nan |
min | 14,544.00 | 0.00 | 0.00 | nan | 1.00 | nan | 1.00 | 2,002.00 | 1.00 | 1.00 | 0.00 | nan | nan |
25% | 27,573.00 | 6.00 | 1.00 | nan | 3.00 | nan | 1.00 | 2,006.00 | 13.00 | 90.00 | 297.00 | nan | nan |
50% | 31,421.00 | 12.00 | 3.00 | nan | 6.00 | nan | 2.00 | 2,010.00 | 26.00 | 179.00 | 596.00 | nan | nan |
75% | 35,650.00 | 18.00 | 5.00 | nan | 9.00 | nan | 3.00 | 2,014.00 | 39.00 | 271.00 | 900.00 | nan | nan |
max | 62,009.00 | 23.00 | 6.00 | nan | 12.00 | nan | 4.00 | 2,018.00 | 53.00 | 366.00 | 1,299.00 | nan | nan |
total rows | 145,366 |
---|---|
total columns | 13 |
column names | PJME_MW, hour, weekday, weekday_name, month, month_name, quarter, year, week_of_year, day_of_year, date_offset, season, time_of_day |
index start | 2002-12-31 01:00:00 |
index end | 2018-01-02 00:00:00 |
total missing values | 408 |
featurized_data Head and Tail |
PJME_MW | hour | weekday | weekday_name | month | month_name | quarter | year | week_of_year | day_of_year | date_offset | season | time_of_day | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Datetime | |||||||||||||
2002-12-31 | 26,498.00 | 1 | 1 | Tuesday | 12 | December | 4 | 2,002 | 1.00 | 365 | 911 | Winter | midnight |
2002-12-31 | 25,147.00 | 2 | 1 | Tuesday | 12 | December | 4 | 2,002 | 1.00 | 365 | 911 | Winter | midnight |
2002-12-31 | 24,574.00 | 3 | 1 | Tuesday | 12 | December | 4 | 2,002 | 1.00 | 365 | 911 | Winter | midnight |
PJME_MW | hour | weekday | weekday_name | month | month_name | quarter | year | week_of_year | day_of_year | date_offset | season | time_of_day | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Datetime | |||||||||||||
2018-01-01 | 42,402.00 | 22 | 0 | Monday | 1 | January | 1 | 2,018 | 1.00 | 1 | 1,081 | Winter | night |
2018-01-01 | 40,164.00 | 23 | 0 | Monday | 1 | January | 1 | 2,018 | 1.00 | 1 | 1,081 | Winter | night |
2018-01-02 | 38,608.00 | 0 | 1 | Tuesday | 1 | January | 1 | 2,018 | 1.00 | 2 | 1,082 | Winter | midnight |
Feature Visualization |
---|
def box_plot_me(df, column, target, hue, palette = 'cool'):
fig, ax = plt.subplots(figsize=(10, 5))
sns.boxplot(data = featurized.dropna(),
x = column,
y = target,
hue = hue,
ax = ax,
linewidth = 1,
palette = palette)
ax.set_title(f'Energy Consumption by {column}')
ax.set_xlabel(f'{column}')
ax.set_ylabel(f'{target}')
ax.legend(bbox_to_anchor=(1, 1))
box_plot_me(featurized, 'weekday', 'PJME_MW', 'season')
box_plot_me(featurized, 'month', 'PJME_MW', 'weekday', palette = 'bright')
Train-Test Split |
---|
split_date = '1-Jan-2015'
train_data = data.loc[data.index <= split_date].copy()
test_data = data.loc[data.index > split_date].copy()
fig, ax = plt.subplots()
train_data.plot(ax = ax, c = rc(clr), ms = 4, style='.');
test_data.plot(ax = ax, c = rc(clr), ms = 4, style='.');
plt.title('Train-Test Split');
plt.legend(labels = ['training', 'testing']);
Prophet Model | ||
---|---|---|
- Prophet requires the datetime index to be a column named ds and the metric or target column to be named y |
prophet_data = data.reset_index()\
.rename(columns = {'Datetime': 'ds', 'PJME_MW': 'y'})
head_tail_horz(prophet_data, 5, 'Data Prepped for Prophet')
Data Prepped for Prophet |
ds | y | |
---|---|---|
0 | 2002-12-31 | 26,498.00 |
1 | 2002-12-31 | 25,147.00 |
2 | 2002-12-31 | 24,574.00 |
3 | 2002-12-31 | 24,393.00 |
4 | 2002-12-31 | 24,860.00 |
ds | y | |
---|---|---|
145361 | 2018-01-01 | 44,284.00 |
145362 | 2018-01-01 | 43,751.00 |
145363 | 2018-01-01 | 42,402.00 |
145364 | 2018-01-01 | 40,164.00 |
145365 | 2018-01-02 | 38,608.00 |
Documentation on Prophet |
---|
# help(Prophet)
Prepping the testing and training data |
---|
prophet_train = train_data.reset_index()\
.rename(columns = {'Datetime': 'ds', 'PJME_MW': 'y'})
prophet_test = test_data.reset_index()\
.rename(columns = {'Datetime': 'ds', 'PJME_MW': 'y'})
Fitting the model |
---|
%%time
model = Prophet()
model.fit(prophet_train)
15:32:20 - cmdstanpy - INFO - Chain [1] start processing 15:33:05 - cmdstanpy - INFO - Chain [1] done processing
CPU times: user 5.1 s, sys: 276 ms, total: 5.37 s Wall time: 50 s
<prophet.forecaster.Prophet at 0x7f8069cc4ca0>
Forecasting |
---|
prophet_forecast = model.predict(prophet_test)
head_tail_vert(prophet_forecast, 5, title = "Prophet Forecast")
Prophet Forecast: head(5) |
ds | trend | yhat_lower | yhat_upper | trend_lower | trend_upper | additive_terms | additive_terms_lower | additive_terms_upper | daily | daily_lower | daily_upper | weekly | weekly_lower | weekly_upper | yearly | yearly_lower | yearly_upper | multiplicative_terms | multiplicative_terms_lower | multiplicative_terms_upper | yhat | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2015-01-01 | 31,210.66 | 23,657.45 | 32,593.63 | 31,210.66 | 31,210.66 | -2,836.55 | -2,836.55 | -2,836.55 | -4,430.26 | -4,430.26 | -4,430.26 | 1,281.38 | 1,281.38 | 1,281.38 | 312.33 | 312.33 | 312.33 | 0.00 | 0.00 | 0.00 | 28,374.11 |
1 | 2015-01-01 | 31,210.62 | 22,226.58 | 31,323.26 | 31,210.62 | 31,210.62 | -4,340.88 | -4,340.88 | -4,340.88 | -5,927.14 | -5,927.14 | -5,927.14 | 1,272.62 | 1,272.62 | 1,272.62 | 313.64 | 313.64 | 313.64 | 0.00 | 0.00 | 0.00 | 26,869.74 |
2 | 2015-01-01 | 31,210.58 | 21,536.55 | 30,689.50 | 31,210.58 | 31,210.58 | -5,212.57 | -5,212.57 | -5,212.57 | -6,790.21 | -6,790.21 | -6,790.21 | 1,262.66 | 1,262.66 | 1,262.66 | 314.99 | 314.99 | 314.99 | 0.00 | 0.00 | 0.00 | 25,998.01 |
3 | 2015-01-01 | 31,210.54 | 21,080.78 | 30,011.51 | 31,210.54 | 31,210.54 | -5,354.14 | -5,354.14 | -5,354.14 | -6,922.12 | -6,922.12 | -6,922.12 | 1,251.61 | 1,251.61 | 1,251.61 | 316.38 | 316.38 | 316.38 | 0.00 | 0.00 | 0.00 | 25,856.40 |
4 | 2015-01-01 | 31,210.51 | 21,753.91 | 30,900.70 | 31,210.51 | 31,210.51 | -4,679.83 | -4,679.83 | -4,679.83 | -6,237.25 | -6,237.25 | -6,237.25 | 1,239.62 | 1,239.62 | 1,239.62 | 317.81 | 317.81 | 317.81 | 0.00 | 0.00 | 0.00 | 26,530.68 |
Prophet Forecast: tail(5) |
ds | trend | yhat_lower | yhat_upper | trend_lower | trend_upper | additive_terms | additive_terms_lower | additive_terms_upper | daily | daily_lower | daily_upper | weekly | weekly_lower | weekly_upper | yearly | yearly_lower | yearly_upper | multiplicative_terms | multiplicative_terms_lower | multiplicative_terms_upper | yhat | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
31434 | 2018-08-02 | 30,036.72 | -8,445.77 | 88,610.31 | -18,755.46 | 76,102.03 | 11,263.15 | 11,263.15 | 11,263.15 | 4,451.04 | 4,451.04 | 4,451.04 | 1,033.85 | 1,033.85 | 1,033.85 | 5,778.26 | 5,778.26 | 5,778.26 | 0.00 | 0.00 | 0.00 | 41,299.88 |
31435 | 2018-08-02 | 30,036.69 | -9,223.74 | 86,786.42 | -18,758.70 | 76,105.59 | 10,490.38 | 10,490.38 | 10,490.38 | 3,695.84 | 3,695.84 | 3,695.84 | 1,021.23 | 1,021.23 | 1,021.23 | 5,773.32 | 5,773.32 | 5,773.32 | 0.00 | 0.00 | 0.00 | 40,527.07 |
31436 | 2018-08-02 | 30,036.65 | -10,586.11 | 85,253.92 | -18,761.94 | 76,109.15 | 8,861.20 | 8,861.20 | 8,861.20 | 2,084.37 | 2,084.37 | 2,084.37 | 1,008.45 | 1,008.45 | 1,008.45 | 5,768.37 | 5,768.37 | 5,768.37 | 0.00 | 0.00 | 0.00 | 38,897.84 |
31437 | 2018-08-02 | 30,036.61 | -12,486.18 | 84,076.65 | -18,765.17 | 76,112.70 | 6,653.49 | 6,653.49 | 6,653.49 | -105.25 | -105.25 | -105.25 | 995.32 | 995.32 | 995.32 | 5,763.42 | 5,763.42 | 5,763.42 | 0.00 | 0.00 | 0.00 | 36,690.10 |
31438 | 2018-08-03 | 30,036.57 | -15,782.19 | 80,735.46 | -18,768.41 | 76,116.26 | 4,327.99 | 4,327.99 | 4,327.99 | -2,412.10 | -2,412.10 | -2,412.10 | 981.61 | 981.61 | 981.61 | 5,758.47 | 5,758.47 | 5,758.47 | 0.00 | 0.00 | 0.00 | 34,364.56 |
Customized Prophet Plot Function (adapted from Prophet documentation) |
---|
def customized_plot(model,
forecast,
ax=None,
uncertainty=True,
plot_cap=True,
xlabel='datetime',
ylabel='target metric',
):
if ax is None:
fig = plt.figure()
ax = fig.add_subplot(111)
else:
fig = ax.get_figure()
fcst_t = forecast['ds'].dt.to_pydatetime()
ax.plot(model.history['ds'].dt.to_pydatetime(), model.history['y'], c=rc(clr))
ax.plot(fcst_t, forecast['yhat'], ls='-', c=rc(clr))
if 'cap' in forecast and plot_cap:
ax.plot(fcst_t, forecast['cap'], ls='--', c=rc(clr))
if model.logistic_floor and 'floor' in forecast and plot_cap:
ax.plot(fcst_t, forecast['floor'], ls='--', c=rc(clr))
if uncertainty:
ax.fill_between(fcst_t, forecast['yhat_lower'], forecast['yhat_upper'],
color=rc(clr), alpha=0.4)
ax.grid(True, which='major', c=rc(clr), ls='-', lw=1, alpha=0.4)
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
fig.tight_layout()
return fig
fig, ax = plt.subplots(figsize=(10, 5))
fig = customized_plot(model = model, forecast = prophet_forecast, ax=ax);
ax.set_title('Prophet Forecast');
Confidence interval becomes wider the farther a prediction is into the future | ||
---|---|---|
(Source) Confidence Intervals are estimates that are calculated from sample data to determine ranges likely to contain the population parameter(mean, standard deviation)of interest. For example, if our population is (2,6), a confidence interval of the mean suggests that the population mean is likely between 2 and 6. And how confidently can we say this? Obviously 100%, right? Because we know all the values and we can calculate it very easily. But in real-life problems, this is not the case. It is not always feasible or possible to study the whole population. So what do we do? We take sample data. But can we rely on one sample? No, because different samples from the same data will produce different mean. So we take numerous random samples (from the same population) and calculate confidence intervals for each sample and a certain percentage of these ranges will contain the true population parameter. This certain percentage is called the confidence level. A 95% confidence level means that out of 100 random samples taken, I expect 95 of the confidence intervals to contain the true population parameter. |
Visualizing Components |
---|
import matplotlib
matplotlib.rcParams.update(matplotlib.rcParamsDefault)
fig = model.plot_components(prophet_forecast)
plt.show()
Model Evaluation |
---|
fig, ax = plt.subplots()
ax.scatter(test_data.index, test_data['PJME_MW'], color='yellow')
customized_plot(model = model, forecast = prophet_forecast, ax=ax)
plt.show()
Zoom in to date range within predictions and actual values |
---|
def zoom_in_prophet(forecast,
test_data,
start_date,
end_date,
title,
legend_loc = 1
):
forecast_segment = forecast.set_index('ds')
forecast_segment = forecast_segment.loc[start_date : end_date]
test_segment = test_data.loc[start_date : end_date]
plt.style.use('elegant.mplstyle')
fig, ax = plt.subplots(figsize = (15, 4))
ax.scatter(test_segment.index,
test_segment['PJME_MW'],
color = 'yellow',
label = "Actual Values",
s = 20)
ax.scatter(forecast_segment.index,
forecast_segment['yhat'],
color = 'deeppink',
label = 'Predicted Values',
s = 20)
plt.title(title + ' Actual Values vs Predicted');
plt.xticks(rotation = 40)
plt.legend(loc = legend_loc)
plt.show()
Monthly Comparison: Earliest Predictions |
---|
zoom_in_prophet(prophet_forecast, test_data,
'2015-01-01', '2015-02-01', 'January 2015:')
zoom_in_prophet(prophet_forecast, test_data,
'2015-02-01', '2015-03-01', 'February 2015:', legend_loc = 2)
zoom_in_prophet(prophet_forecast, test_data,
'2015-03-01', '2015-04-01', 'March 2015:')
zoom_in_prophet(prophet_forecast, test_data,
'2015-04-01', '2015-05-01', 'April 2015:', legend_loc = 4)
Weekly Comparison: Earliest Predictions |
---|
zoom_in_prophet(prophet_forecast, test_data,
'2015-01-01', '2015-01-08', 'January 2015 Week 1:', legend_loc = 2)
zoom_in_prophet(prophet_forecast, test_data,
'2015-01-08', '2015-01-15', 'January 2015 Week 2:', legend_loc = 4)
zoom_in_prophet(prophet_forecast, test_data,
'2015-01-15', '2015-01-22', 'January 2015 Week 3:', legend_loc = 4)
zoom_in_prophet(prophet_forecast, test_data,
'2015-01-22', '2015-01-29', 'January 2015 Week 4:', legend_loc = 4)
Monthly Comparison: Latest Predictions |
---|
zoom_in_prophet(prophet_forecast, test_data,
'2018-04-01', '2018-05-01', 'April 2018:')
zoom_in_prophet(prophet_forecast, test_data,
'2018-05-01', '2018-06-01', 'May 2018:', legend_loc = 2)
zoom_in_prophet(prophet_forecast, test_data,
'2018-06-01', '2018-07-01', 'June 2018:')
zoom_in_prophet(prophet_forecast, test_data,
'2018-07-01', '2018-08-01', 'July 2018:', legend_loc = 4)
Weekly Comparison: Latest Predictions |
---|
zoom_in_prophet(prophet_forecast, test_data,
'2018-07-01', '2018-07-08', 'July 2018 Week 1:', legend_loc = 2)
zoom_in_prophet(prophet_forecast, test_data,
'2018-07-08', '2018-07-15', 'July 2018 Week 2:', legend_loc = 4)
zoom_in_prophet(prophet_forecast, test_data,
'2018-07-15', '2018-07-22', 'July 2018 Week 3:', legend_loc = 4)
zoom_in_prophet(prophet_forecast, test_data,
'2018-07-22', '2018-07-29', 'July 2018 Week 4:', legend_loc = 4)
Error Metric Evaluation |
---|
actual_values = test_data.PJME_MW
predictions = prophet_forecast.yhat
pretty(np.sqrt(mse(actual_values, predictions)), 'Mean Squared Error Score:')
pretty(mae(actual_values, predictions), 'Mean Absolute Error Score:')
pretty(f'{mape(actual_values, predictions)*100:.3f}%',
'Mean Absolute Percenage Error:')
Mean Squared Error Score: |
6615.820557 |
Mean Absolute Error Score: |
5182.108151 |
Mean Absolute Percenage Error: |
16.509% |
Incorporating Holidays |
---|
# Rob used another library, but I am just using the Prophet method
# to add holidays
holiday_model = Prophet()
holiday_model.add_country_holidays(country_name='US')
holiday_model = holiday_model.fit(prophet_train)
15:37:44 - cmdstanpy - INFO - Chain [1] start processing 15:38:32 - cmdstanpy - INFO - Chain [1] done processing
pretty('US Holidays from Prophet')
pd.DataFrame(holiday_model.train_holiday_names).style\
.hide(axis = 'index')\
.hide(axis = 'columns')
US Holidays from Prophet |
New Year's Day |
Martin Luther King Jr. Day |
Washington's Birthday |
Memorial Day |
Independence Day |
Labor Day |
Columbus Day |
Veterans Day |
Thanksgiving |
Christmas Day |
New Year's Day (Observed) |
Independence Day (Observed) |
Christmas Day (Observed) |
Veterans Day (Observed) |
Predictions: Accounting for holidays |
---|
holidays_forecast = holiday_model.predict(df = prophet_test)
head_tail_vert(holidays_forecast, 5, 'Holidays Forecast')
Holidays Forecast: head(5) |
ds | trend | yhat_lower | yhat_upper | trend_lower | trend_upper | Christmas Day | Christmas Day_lower | Christmas Day_upper | Christmas Day (Observed) | Christmas Day (Observed)_lower | Christmas Day (Observed)_upper | Columbus Day | Columbus Day_lower | Columbus Day_upper | Independence Day | Independence Day_lower | Independence Day_upper | Independence Day (Observed) | Independence Day (Observed)_lower | Independence Day (Observed)_upper | Labor Day | Labor Day_lower | Labor Day_upper | Martin Luther King Jr. Day | Martin Luther King Jr. Day_lower | Martin Luther King Jr. Day_upper | Memorial Day | Memorial Day_lower | Memorial Day_upper | New Year's Day | New Year's Day_lower | New Year's Day_upper | New Year's Day (Observed) | New Year's Day (Observed)_lower | New Year's Day (Observed)_upper | Thanksgiving | Thanksgiving_lower | Thanksgiving_upper | Veterans Day | Veterans Day_lower | Veterans Day_upper | Veterans Day (Observed) | Veterans Day (Observed)_lower | Veterans Day (Observed)_upper | Washington's Birthday | Washington's Birthday_lower | Washington's Birthday_upper | additive_terms | additive_terms_lower | additive_terms_upper | daily | daily_lower | daily_upper | holidays | holidays_lower | holidays_upper | weekly | weekly_lower | weekly_upper | yearly | yearly_lower | yearly_upper | multiplicative_terms | multiplicative_terms_lower | multiplicative_terms_upper | yhat | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2015-01-01 | 31,266.52 | 21,382.75 | 30,266.37 | 31,266.52 | 31,266.52 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | -3,057.33 | -3,057.33 | -3,057.33 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | -5,514.97 | -5,514.97 | -5,514.97 | -4,430.34 | -4,430.34 | -4,430.34 | -3,057.33 | -3,057.33 | -3,057.33 | 1,317.81 | 1,317.81 | 1,317.81 | 654.90 | 654.90 | 654.90 | 0.00 | 0.00 | 0.00 | 25,751.55 |
1 | 2015-01-01 | 31,266.48 | 19,692.07 | 28,486.71 | 31,266.48 | 31,266.48 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | -3,057.33 | -3,057.33 | -3,057.33 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | -7,016.75 | -7,016.75 | -7,016.75 | -5,927.22 | -5,927.22 | -5,927.22 | -3,057.33 | -3,057.33 | -3,057.33 | 1,312.01 | 1,312.01 | 1,312.01 | 655.80 | 655.80 | 655.80 | 0.00 | 0.00 | 0.00 | 24,249.74 |
2 | 2015-01-01 | 31,266.45 | 19,271.15 | 27,719.36 | 31,266.45 | 31,266.45 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | -3,057.33 | -3,057.33 | -3,057.33 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | -7,886.21 | -7,886.21 | -7,886.21 | -6,790.28 | -6,790.28 | -6,790.28 | -3,057.33 | -3,057.33 | -3,057.33 | 1,304.67 | 1,304.67 | 1,304.67 | 656.73 | 656.73 | 656.73 | 0.00 | 0.00 | 0.00 | 23,380.24 |
3 | 2015-01-01 | 31,266.41 | 18,873.50 | 27,643.08 | 31,266.41 | 31,266.41 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | -3,057.33 | -3,057.33 | -3,057.33 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | -8,025.93 | -8,025.93 | -8,025.93 | -6,922.19 | -6,922.19 | -6,922.19 | -3,057.33 | -3,057.33 | -3,057.33 | 1,295.91 | 1,295.91 | 1,295.91 | 657.69 | 657.69 | 657.69 | 0.00 | 0.00 | 0.00 | 23,240.49 |
4 | 2015-01-01 | 31,266.38 | 19,529.33 | 28,463.87 | 31,266.38 | 31,266.38 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | -3,057.33 | -3,057.33 | -3,057.33 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | -7,350.14 | -7,350.14 | -7,350.14 | -6,237.33 | -6,237.33 | -6,237.33 | -3,057.33 | -3,057.33 | -3,057.33 | 1,285.83 | 1,285.83 | 1,285.83 | 658.68 | 658.68 | 658.68 | 0.00 | 0.00 | 0.00 | 23,916.23 |
Holidays Forecast: tail(5) |
ds | trend | yhat_lower | yhat_upper | trend_lower | trend_upper | Christmas Day | Christmas Day_lower | Christmas Day_upper | Christmas Day (Observed) | Christmas Day (Observed)_lower | Christmas Day (Observed)_upper | Columbus Day | Columbus Day_lower | Columbus Day_upper | Independence Day | Independence Day_lower | Independence Day_upper | Independence Day (Observed) | Independence Day (Observed)_lower | Independence Day (Observed)_upper | Labor Day | Labor Day_lower | Labor Day_upper | Martin Luther King Jr. Day | Martin Luther King Jr. Day_lower | Martin Luther King Jr. Day_upper | Memorial Day | Memorial Day_lower | Memorial Day_upper | New Year's Day | New Year's Day_lower | New Year's Day_upper | New Year's Day (Observed) | New Year's Day (Observed)_lower | New Year's Day (Observed)_upper | Thanksgiving | Thanksgiving_lower | Thanksgiving_upper | Veterans Day | Veterans Day_lower | Veterans Day_upper | Veterans Day (Observed) | Veterans Day (Observed)_lower | Veterans Day (Observed)_upper | Washington's Birthday | Washington's Birthday_lower | Washington's Birthday_upper | additive_terms | additive_terms_lower | additive_terms_upper | daily | daily_lower | daily_upper | holidays | holidays_lower | holidays_upper | weekly | weekly_lower | weekly_upper | yearly | yearly_lower | yearly_upper | multiplicative_terms | multiplicative_terms_lower | multiplicative_terms_upper | yhat | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
31434 | 2018-08-02 | 30,132.09 | 1,910.44 | 85,032.05 | -8,671.05 | 73,917.88 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 11,265.73 | 11,265.73 | 11,265.73 | 4,451.12 | 4,451.12 | 4,451.12 | 0.00 | 0.00 | 0.00 | 1,067.63 | 1,067.63 | 1,067.63 | 5,746.99 | 5,746.99 | 5,746.99 | 0.00 | 0.00 | 0.00 | 41,397.82 |
31435 | 2018-08-02 | 30,132.05 | 1,379.49 | 84,542.92 | -8,671.33 | 73,920.14 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 10,490.17 | 10,490.17 | 10,490.17 | 3,695.91 | 3,695.91 | 3,695.91 | 0.00 | 0.00 | 0.00 | 1,052.16 | 1,052.16 | 1,052.16 | 5,742.09 | 5,742.09 | 5,742.09 | 0.00 | 0.00 | 0.00 | 40,622.22 |
31436 | 2018-08-02 | 30,132.01 | -795.26 | 82,982.56 | -8,671.61 | 73,922.40 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 8,858.04 | 8,858.04 | 8,858.04 | 2,084.42 | 2,084.42 | 2,084.42 | 0.00 | 0.00 | 0.00 | 1,036.43 | 1,036.43 | 1,036.43 | 5,737.19 | 5,737.19 | 5,737.19 | 0.00 | 0.00 | 0.00 | 38,990.06 |
31437 | 2018-08-02 | 30,131.98 | -4,276.75 | 81,395.76 | -8,671.89 | 73,924.66 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 6,647.30 | 6,647.30 | 6,647.30 | -105.26 | -105.26 | -105.26 | 0.00 | 0.00 | 0.00 | 1,020.27 | 1,020.27 | 1,020.27 | 5,732.29 | 5,732.29 | 5,732.29 | 0.00 | 0.00 | 0.00 | 36,779.28 |
31438 | 2018-08-03 | 30,131.94 | -4,482.38 | 78,794.76 | -8,672.17 | 73,926.92 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 4,318.70 | 4,318.70 | 4,318.70 | -2,412.16 | -2,412.16 | -2,412.16 | 0.00 | 0.00 | 0.00 | 1,003.47 | 1,003.47 | 1,003.47 | 5,727.39 | 5,727.39 | 5,727.39 | 0.00 | 0.00 | 0.00 | 34,450.64 |
header_text('Forecast: Incorporating Holidays')
customized_plot(model = holiday_model,
forecast = holidays_forecast)
Forecast: Incorporating Holidays |
Visualizing Components: Incorporating Holidays |
---|
import matplotlib
matplotlib.rcParams.update(matplotlib.rcParamsDefault)
fig = holiday_model.plot_components(holidays_forecast)
plt.show()
Monthly Comparison (incorporating holidays): Earliest Predictions |
---|
zoom_in_prophet(holidays_forecast, test_data,
'2015-01-01', '2015-02-01', 'January 2015:')
zoom_in_prophet(holidays_forecast, test_data,
'2015-02-01', '2015-03-01', 'February 2015:', legend_loc = 2)
zoom_in_prophet(holidays_forecast, test_data,
'2015-03-01', '2015-04-01', 'March 2015:')
zoom_in_prophet(holidays_forecast, test_data,
'2015-04-01', '2015-05-01', 'April 2015:', legend_loc = 4)
Weekly Comparison (incorporating holidays): Earliest Predictions |
---|
zoom_in_prophet(holidays_forecast, test_data,
'2015-01-01', '2015-01-08', 'January 2015 Week 1:', legend_loc = 2)
zoom_in_prophet(holidays_forecast, test_data,
'2015-01-08', '2015-01-15', 'January 2015 Week 2:', legend_loc = 4)
zoom_in_prophet(holidays_forecast, test_data,
'2015-01-15', '2015-01-22', 'January 2015 Week 3:', legend_loc = 4)
zoom_in_prophet(holidays_forecast, test_data,
'2015-01-22', '2015-01-29', 'January 2015 Week 4:', legend_loc = 4)
Monthly Comparison (incorporating holidays): Latest Predictions |
---|
zoom_in_prophet(holidays_forecast, test_data,
'2018-04-01', '2018-05-01', 'April 2018:')
zoom_in_prophet(holidays_forecast, test_data,
'2018-05-01', '2018-06-01', 'May 2018:', legend_loc = 2)
zoom_in_prophet(holidays_forecast, test_data,
'2018-06-01', '2018-07-01', 'June 2018:')
zoom_in_prophet(holidays_forecast, test_data,
'2018-07-01', '2018-08-01', 'July 2018:', legend_loc = 4)
Weekly Comparison (incorporating holidays): Latest Predictions |
---|
zoom_in_prophet(holidays_forecast, test_data,
'2018-07-01', '2018-07-08', 'July 2018 Week 1:', legend_loc = 2)
zoom_in_prophet(holidays_forecast, test_data,
'2018-07-08', '2018-07-15', 'July 2018 Week 2:', legend_loc = 4)
zoom_in_prophet(holidays_forecast, test_data,
'2018-07-15', '2018-07-22', 'July 2018 Week 3:', legend_loc = 4)
zoom_in_prophet(holidays_forecast, test_data,
'2018-07-22', '2018-07-29', 'July 2018 Week 4:', legend_loc = 4)
actual_values = test_data.PJME_MW
predictions = holidays_forecast.yhat
pretty(np.sqrt(mse(actual_values, predictions)), 'Mean Squared Error Score:')
pretty(mae(actual_values, predictions), 'Mean Absolute Error Score:')
pretty(f'{mape(actual_values, predictions)*100:.3f}%',
'Mean Absolute Percenage Error:')
Mean Squared Error Score: |
6634.361847 |
Mean Absolute Error Score: |
5197.493614 |
Mean Absolute Percenage Error: |
16.566% |
Future Predictions |
---|
# retraining model on entirety of the data # Rob didn't do this, but I do not see a reason not to |
---|
future_model = Prophet()
future_model = future_model.fit(prophet_data)
15:57:59 - cmdstanpy - INFO - Chain [1] start processing 15:58:40 - cmdstanpy - INFO - Chain [1] done processing
future = future_model.make_future_dataframe(periods = 365*24,
freq='h',
include_history=False)
future_forecast = future_model.predict(future)
head_tail_vert(future_forecast, 5, 'Future Prediction Forecast')
Future Prediction Forecast: head(5) |
ds | trend | yhat_lower | yhat_upper | trend_lower | trend_upper | additive_terms | additive_terms_lower | additive_terms_upper | daily | daily_lower | daily_upper | weekly | weekly_lower | weekly_upper | yearly | yearly_lower | yearly_upper | multiplicative_terms | multiplicative_terms_lower | multiplicative_terms_upper | yhat | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2018-08-03 | 30,824.25 | 28,762.81 | 38,351.27 | 30,824.25 | 30,824.25 | 2,392.84 | 2,392.84 | 2,392.84 | -4,336.67 | -4,336.67 | -4,336.67 | 944.13 | 944.13 | 944.13 | 5,785.38 | 5,785.38 | 5,785.38 | 0.00 | 0.00 | 0.00 | 33,217.09 |
1 | 2018-08-03 | 30,824.22 | 27,377.33 | 36,294.35 | 30,824.22 | 30,824.22 | 912.37 | 912.37 | 912.37 | -5,801.94 | -5,801.94 | -5,801.94 | 933.11 | 933.11 | 933.11 | 5,781.20 | 5,781.20 | 5,781.20 | 0.00 | 0.00 | 0.00 | 31,736.59 |
2 | 2018-08-03 | 30,824.20 | 25,963.22 | 35,700.36 | 30,824.20 | 30,824.20 | 47.62 | 47.62 | 47.62 | -6,649.93 | -6,649.93 | -6,649.93 | 920.52 | 920.52 | 920.52 | 5,777.02 | 5,777.02 | 5,777.02 | 0.00 | 0.00 | 0.00 | 30,871.81 |
3 | 2018-08-03 | 30,824.17 | 26,051.66 | 34,994.86 | 30,824.17 | 30,824.17 | -96.59 | -96.59 | -96.59 | -6,775.47 | -6,775.47 | -6,775.47 | 906.04 | 906.04 | 906.04 | 5,772.84 | 5,772.84 | 5,772.84 | 0.00 | 0.00 | 0.00 | 30,727.58 |
4 | 2018-08-03 | 30,824.15 | 26,540.61 | 35,902.12 | 30,824.15 | 30,824.15 | 567.80 | 567.80 | 567.80 | -6,090.15 | -6,090.15 | -6,090.15 | 889.29 | 889.29 | 889.29 | 5,768.66 | 5,768.66 | 5,768.66 | 0.00 | 0.00 | 0.00 | 31,391.95 |
Future Prediction Forecast: tail(5) |
ds | trend | yhat_lower | yhat_upper | trend_lower | trend_upper | additive_terms | additive_terms_lower | additive_terms_upper | daily | daily_lower | daily_upper | weekly | weekly_lower | weekly_upper | yearly | yearly_lower | yearly_upper | multiplicative_terms | multiplicative_terms_lower | multiplicative_terms_upper | yhat | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
8755 | 2019-08-02 | 30,604.14 | 35,590.82 | 46,949.20 | 27,292.16 | 33,586.28 | 10,391.84 | 10,391.84 | 10,391.84 | 4,425.20 | 4,425.20 | 4,425.20 | 135.17 | 135.17 | 135.17 | 5,831.48 | 5,831.48 | 5,831.48 | 0.00 | 0.00 | 0.00 | 40,995.98 |
8756 | 2019-08-02 | 30,604.12 | 34,210.36 | 45,933.98 | 27,290.99 | 33,586.67 | 9,517.51 | 9,517.51 | 9,517.51 | 3,649.72 | 3,649.72 | 3,649.72 | 40.51 | 40.51 | 40.51 | 5,827.28 | 5,827.28 | 5,827.28 | 0.00 | 0.00 | 0.00 | 40,121.62 |
8757 | 2019-08-02 | 30,604.09 | 32,466.65 | 43,588.31 | 27,289.83 | 33,587.06 | 7,805.73 | 7,805.73 | 7,805.73 | 2,042.67 | 2,042.67 | 2,042.67 | -60.02 | -60.02 | -60.02 | 5,823.09 | 5,823.09 | 5,823.09 | 0.00 | 0.00 | 0.00 | 38,409.83 |
8758 | 2019-08-02 | 30,604.07 | 30,087.02 | 41,862.48 | 27,288.66 | 33,587.45 | 5,540.35 | 5,540.35 | 5,540.35 | -112.31 | -112.31 | -112.31 | -166.23 | -166.23 | -166.23 | 5,818.89 | 5,818.89 | 5,818.89 | 0.00 | 0.00 | 0.00 | 36,144.42 |
8759 | 2019-08-03 | 30,604.04 | 28,144.76 | 39,567.14 | 27,287.49 | 33,587.84 | 3,169.70 | 3,169.70 | 3,169.70 | -2,367.12 | -2,367.12 | -2,367.12 | -277.88 | -277.88 | -277.88 | 5,814.70 | 5,814.70 | 5,814.70 | 0.00 | 0.00 | 0.00 | 33,773.74 |
future_forecast.yhat.plot(c='cyan', title = 'Future Predictions')
plt.show()