What could cause facebook's Prophet model to do so poorly on these procedurally generated datasets, where one is a continuation of the other Recently I've been looking into some easy out of the box modeling using Facebook's Prophet -- potentially to use in some projects at work. So far, I have been super impressed with everything that I've seen people use it online. Their first demo using the Peyton Manning Wikipedia page views is pretty impressive  see here
I started playing with it and I had a hard time getting the package fbprophet installed in python. I'm on windows and there's some CPP compiling going on which apparently can cause issues. So, anyway I switched over to R. I thought I would throw Prophet a softball, and see how it did on a dataset that I generated programmatically. Here is some R code that you can run so you can see what I mean:
install.packages('prophet')
library(prophet)

f = function(numDays){
  set.seed(10000)
  
  startDate = as.Date('2000-01-01')
  endDate = startDate + numDays - 1
  
  history <- data.frame(ds = seq(startDate, endDate, by='d'), y = sin(1:numDays/100 ) + rnorm(numDays)/4)
  m = prophet(history)
  future <- make_future_dataframe(m, periods = 365)
  forecast <- predict(m, future)
  plot(m, forecast)
}

Here are some results of this simple plotting function that I made.
First with 6000 days of data:
f(6000)


Second with 1000 days of data:
f(1000)


Some comments on the plots:
Clearly, something went wrong here!?
Note that the two datasets are the exact same for the first 1000 points. Yet the forecast with fewer points is more accurate. This is counterintuitive to what I would expect.
Also, the data is clearly seasonal -- though the seasonality does not have a period of exactly 1 year.
So, my questions are, what tweaks do I need to make to this model to get it to fit the data somewhat well? It does well fitting the data historically for the second model but does poorly on the forecast projection considering the 'obvious' sine curve. I mean, it seems like this is supposed to work well right out of the box (and it seems to have for everything I have seen online), but this is the first case that I've seen it do poorly -- and this seems like a much simpler dataset to fit than some of the other demos online. Or maybe it's the randomly generated data that threw it off?
I am looking for some insight from those with a bit more experience than me when it comes to time series modeling.
 A: What is happening is that prophet is unable to detect the seasonality of the data automatically. This is not unexpected. Usually we define the seasonality manually or indirectly (e.g. when we see argument like yearly.seasonality=TRUE). In both of the fitted models presented the algorithm assumes as linear evolution of the existing trend. Just in the case of 6000 samples prophet can detect the mean trend more effectively that in the case of having 1000 samples. In both cases, we fail to detect a relevant periodicity.
In order to make our model fit our data more explicitly we need to define the periodicity terms ourselves. There are a number of ways to find the periodicity of signal.
The most common way is through a signal's Fourier transform. We can examine the periodogram of our signal, find the frequency with the maximal spectral density and then inverse that frequency to get the corresponding period $T$. A second simple way would be to calculate the average difference solution between successive local maxima assuming our signal is smooth enough. A third "funky" way, if we already know we have a single periodic component would be to try to fit an linear model with a periodic term of period $T$ and then pick the $T$ corresponding to the maximum likelihood. So code-wise:
numDays = 6000
set.seed(10000)
t =  (1:numDays)
y =  sin( t/100 ) + rnorm(numDays)/4
# Using spectral decomposition / Fourier
y_spec = stats::spectrum(y)
T_F  = 1/y_spec$freq[which.max(y_spec$spec)] # 600
# Using a custom LM to test different periods
T_LM = optim(par=c(30), # some starting value
             fn=function(x){-logLik( 
                              # Define an linear model with a periodic term of T=x[1]
                              lm( y ~ sin(2*pi*t/x[1]) + cos(2*pi*t/x[1]))
                             )
                           }, 
             method="Brent", lower=2, upper=1000)$par # 628
print(paste0("Fourier Decomp. suggests seasonality of: ", T_F,
         " days and LM seasonality of: ", round(T_LM,1), " days."))
# "Fourier Decomp. suggests seasonality of: 600 days and LM seasonality of: 628.6 days."

Based on the above can see that we have a periodicity of ~628 days. prophet allows us to define customer seasonality terms using the add_seasonlity function. As such we can redefine our modelling procedure f like:
f = function(numDays){
  set.seed(10000)
  
  startDate = as.Date('2000-01-01')
  endDate = startDate + numDays - 1
  
  history <- data.frame(ds = seq(startDate, endDate, by='d'), 
                        y = sin(1:numDays/100 ) + rnorm(numDays)/4)
  m = prophet(fit = FALSE)
  m <- add_seasonality(m, name='my_seasonality', period=628, fourier.order=3)
  m <- fit.prophet(m, history)
  future <- make_future_dataframe(m, periods = 365)
  forecast <- predict(m, future)
  plot(m, forecast)
}

In which case prophet is directly able to fit the periodicity in our data. Both for our 6000 samples:

and our 1000 samples:

As we see there was nothing wrong with our "randomly generated data" in any way. The data just did not fit the rather specific periodic cycles prophet accounts for automatically. In some cases, prophet is presented as a forecasting silver bullet; it is not. It is an excellent procedure and it has main automated goodies (e.g. weekly and monthly seasonality checks). We need though to be aware of what we try to predict/forecast and the underlying nature of our problem.
