From 5d453c1d05d356e08b5dee19dcce89cd57b53f4a Mon Sep 17 00:00:00 2001 From: bletham Date: Thu, 9 Nov 2017 21:19:51 -0800 Subject: [PATCH] Fix missing columns in SHF with extra regressor --- R/DESCRIPTION | 2 +- R/R/diagnostics.R | 2 ++ R/tests/testthat/test_diagnostics.R | 19 +++++++++++++++++++ python/fbprophet/__init__.py | 2 +- python/fbprophet/diagnostics.py | 2 ++ python/fbprophet/tests/test_diagnostics.py | 19 +++++++++++++++++++ 6 files changed, 44 insertions(+), 2 deletions(-) diff --git a/R/DESCRIPTION b/R/DESCRIPTION index 63752f1..d689bb2 100644 --- a/R/DESCRIPTION +++ b/R/DESCRIPTION @@ -1,6 +1,6 @@ Package: prophet Title: Automatic Forecasting Procedure -Version: 0.2.1 +Version: 0.2.1.9000 Date: 2017-11-08 Authors@R: c( person("Sean", "Taylor", email = "sjt@fb.com", role = c("cre", "aut")), diff --git a/R/R/diagnostics.R b/R/R/diagnostics.R index c35db40..4723298 100644 --- a/R/R/diagnostics.R +++ b/R/R/diagnostics.R @@ -83,6 +83,7 @@ simulated_historical_forecasts <- function(model, horizon, units, k, m <- fit.prophet(m, history.c) # Calculate yhat df.predict <- dplyr::filter(df, ds > cutoff, ds <= cutoff + horizon) + # Get the columns for the future dataframe columns <- c('ds') if (m$growth == 'logistic') { columns <- c(columns, 'cap') @@ -90,6 +91,7 @@ simulated_historical_forecasts <- function(model, horizon, units, k, columns <- c(columns, 'floor') } } + columns <- c(columns, names(m$extra_regressors)) future <- df[columns] yhat <- stats::predict(m, future) # Merge yhat, y, and cutoff. diff --git a/R/tests/testthat/test_diagnostics.R b/R/tests/testthat/test_diagnostics.R index fbe0fec..287743f 100644 --- a/R/tests/testthat/test_diagnostics.R +++ b/R/tests/testthat/test_diagnostics.R @@ -47,6 +47,25 @@ test_that("simulated_historical_forecasts_logistic", { expect_equal(sum((df.merged$y.x - df.merged$y.y) ** 2), 0) }) +test_that("simulated_historical_forecasts_extra_regressors", { + skip_if_not(Sys.getenv('R_ARCH') != '/i386') + df <- DATA + df$extra <- seq(0, nrow(df) - 1) + m <- prophet() + m <- add_seasonality(m, name = 'monthly', period = 30.5, fourier.order = 5) + m <- add_regressor(m, 'extra') + m <- fit.prophet(m, df) + df.shf <- simulated_historical_forecasts( + m, horizon = 3, units = 'days', k = 2, period = 3) + # All cutoff dates should be less than ds dates + expect_true(all(df.shf$cutoff < df.shf$ds)) + # The unique size of output cutoff should be equal to 'k' + expect_equal(length(unique(df.shf$cutoff)), 2) + # Each y in df_shf and DATA with same ds should be equal + df.merged <- dplyr::left_join(df.shf, m$history, by="ds") + expect_equal(sum((df.merged$y.x - df.merged$y.y) ** 2), 0) +}) + test_that("simulated_historical_forecasts_default_value_check", { skip_if_not(Sys.getenv('R_ARCH') != '/i386') m <- prophet(DATA) diff --git a/python/fbprophet/__init__.py b/python/fbprophet/__init__.py index 684eeb0..a2a7a0a 100644 --- a/python/fbprophet/__init__.py +++ b/python/fbprophet/__init__.py @@ -7,4 +7,4 @@ from fbprophet.forecaster import Prophet -__version__ = '0.2.1' +__version__ = '0.2.1.dev' diff --git a/python/fbprophet/diagnostics.py b/python/fbprophet/diagnostics.py index d591d69..6254971 100644 --- a/python/fbprophet/diagnostics.py +++ b/python/fbprophet/diagnostics.py @@ -93,11 +93,13 @@ def simulated_historical_forecasts(model, horizon, k, period=None): m.fit(df[df['ds'] <= cutoff]) # Calculate yhat index_predicted = (df['ds'] > cutoff) & (df['ds'] <= cutoff + horizon) + # Get the columns for the future dataframe columns = ['ds'] if m.growth == 'logistic': columns.append('cap') if m.logistic_floor: columns.append('floor') + columns.extend(m.extra_regressors.keys()) yhat = m.predict(df[index_predicted][columns]) # Merge yhat(predicts), y(df, original data) and cutoff predicts.append(pd.concat([ diff --git a/python/fbprophet/tests/test_diagnostics.py b/python/fbprophet/tests/test_diagnostics.py index 3dee544..02e480c 100644 --- a/python/fbprophet/tests/test_diagnostics.py +++ b/python/fbprophet/tests/test_diagnostics.py @@ -75,6 +75,25 @@ class TestDiagnostics(TestCase): self.assertAlmostEqual( np.sum((df_merged['y_x'] - df_merged['y_y']) ** 2), 0.0) + def test_simulated_historical_forecasts_extra_regressors(self): + m = Prophet() + m.add_seasonality(name='monthly', period=30.5, fourier_order=5) + m.add_regressor('extra') + df = self.__df.copy() + df['cap'] = 40 + df['extra'] = range(df.shape[0]) + m.fit(df) + df_shf = diagnostics.simulated_historical_forecasts( + m, horizon='3 days', k=2, period='3 days') + # All cutoff dates should be less than ds dates + self.assertTrue((df_shf['cutoff'] < df_shf['ds']).all()) + # The unique size of output cutoff should be equal to 'k' + self.assertEqual(len(np.unique(df_shf['cutoff'])), 2) + # Each y in df_shf and self.__df with same ds should be equal + df_merged = pd.merge(df_shf, df, 'left', on='ds') + self.assertAlmostEqual( + np.sum((df_merged['y_x'] - df_merged['y_y']) ** 2), 0.0) + def test_simulated_historical_forecasts_default_value_check(self): m = Prophet() m.fit(self.__df)