Forecasting with Prophet
This tutorial will guide you through using Facebook’s Prophet forecasting model with the WebAssembly-based Stan backend in augurs. Prophet is particularly well-suited for time series that have strong seasonal effects and multiple seasons.
Prerequisites
First, add the necessary features to your Cargo.toml:
[dependencies]
augurs = { version = "0.6.0", features = ["prophet", "prophet-wasmstan"] }
Basic Prophet Forecasting
Let’s start with a simple example:
extern crate augurs;
use augurs::prophet::{Prophet, TrainingData, wasmstan::WasmstanOptimizer};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create timestamps (as Unix timestamps)
let timestamps = vec![
1704067200, // 2024-01-01
1704153600, // 2024-01-02
1704240000, // 2024-01-03
1704326400, // 2024-01-04
1704412800, // 2024-01-05
// ... more dates
];
// Your observations
let values = vec![1.1, 2.1, 3.2, 4.3, 5.5];
// Create training data
let data = TrainingData::new(timestamps, values)?;
// Initialize Prophet with WASMSTAN optimizer
let optimizer = WasmstanOptimizer::new();
let mut prophet = Prophet::new(Default::default(), optimizer);
// Fit the model
prophet.fit(data, Default::default())?;
// Make in-sample predictions
let predictions = prophet.predict(None)?;
println!("Predictions: {:?}", predictions.yhat.point);
println!("Lower bounds: {:?}", predictions.yhat.lower.unwrap());
println!("Upper bounds: {:?}", predictions.yhat.upper.unwrap());
Ok(())
}
Adding Regressors
Prophet allows you to include additional regressors to improve your forecasts:
extern crate augurs;
use std::collections::HashMap;
use augurs::prophet::{Prophet, TrainingData, Regressor, wasmstan::WasmstanOptimizer};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create timestamps and values as before
let timestamps = vec![
1704067200, // 2024-01-01
1704153600, // 2024-01-02
1704240000, // 2024-01-03
1704326400, // 2024-01-04
1704412800, // 2024-01-05
];
let values = vec![1.1, 2.1, 3.2, 4.3, 5.5];
// Create regressors
let regressors = HashMap::from([
(
"temperature".to_string(),
vec![20.0, 22.0, 21.0, 21.5, 22.5], // temperature values
),
]);
// Create training data with regressors
let data = TrainingData::new(timestamps, values)?
.with_regressors(regressors)?;
// Initialize Prophet
let optimizer = WasmstanOptimizer::new();
let mut prophet = Prophet::new(Default::default(), optimizer);
// Add regressors with their modes
prophet.add_regressor("temperature".to_string(), Regressor::additive());
// Fit and predict as before
prophet.fit(data, Default::default())?;
let predictions = prophet.predict(None)?;
Ok(())
}
Customizing the Model
Prophet offers several customization options:
extern crate augurs;
use augurs::prophet::{
Prophet, TrainingData, ProphetOptions, FeatureMode,
GrowthType, SeasonalityOption,
wasmstan::WasmstanOptimizer,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Configure Prophet with custom settings
let options = ProphetOptions {
// Set growth model
growth: GrowthType::Linear,
// Configure seasonality
seasonality_mode: FeatureMode::Multiplicative,
yearly_seasonality: SeasonalityOption::Manual(true),
weekly_seasonality: SeasonalityOption::Manual(true),
daily_seasonality: SeasonalityOption::Manual(false),
..Default::default()
};
let optimizer = WasmstanOptimizer::new();
let mut prophet = Prophet::new(options, optimizer);
// Proceed with fitting and prediction...
Ok(())
}
Working with Future Dates
To forecast into the future, you’ll need to create a PredictionData object with the timestamps you want
to predict. It must also contain the same regressors as the training data:
extern crate augurs;
use augurs::prophet::{Prophet, PredictionData, wasmstan::WasmstanOptimizer};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Setup and fit model as before...
let optimizer = WasmstanOptimizer::new();
let mut prophet = Prophet::new(Default::default(), optimizer);
let prediction_data = PredictionData::new(vec![
1704499200, // 2024-01-06
1704585600, // 2024-01-07
]);
let predictions = prophet.predict(Some(prediction_data))?;
// Access the forecasted values, and their bounds.
println!("Predictions: {:?}", predictions.yhat.point);
println!("Lower bounds: {:?}", predictions.yhat.lower.as_ref().unwrap());
println!("Upper bounds: {:?}", predictions.yhat.upper.as_ref().unwrap());
Ok(())
}
Best Practices
-
Data Preparation
- Ensure your timestamps are Unix timestamps
- Handle missing values before passing to Prophet
- Consider scaling your target variable if values are very large
-
Model Configuration
- Start with default settings and adjust based on your needs
- Use additive seasonality for constant seasonal variations
- Use multiplicative seasonality when variations scale with the trend
-
Performance Considerations
- WASMSTAN runs inside a WASM runtime and may be slower than native code
- For server-side applications, consider using the
prophet-cmdstanfeature instead - Large datasets may require more computation time
Troubleshooting
Common issues and their solutions:
- Invalid timestamps: Ensure timestamps are Unix timestamps in seconds
- Missing values: Prophet can handle some missing values, but it’s better to preprocess them
- Convergence issues: Try adjusting the number of iterations or sampling parameters
Next Steps
- Learn about changepoint detection
- Explore seasonal decomposition
- Understand cross-validation