1 Installation
Install Farseer using pip or build from source:
From PyPI (Recommended)
pip install farseer
From Source
# Clone the repository
git clone https://github.com/ryanbieber/seer
cd seer
# For Python 3.13+, set compatibility flag
export PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1
# Build and install with maturin
pip install maturin
maturin develop --release
Prerequisites
- Python 3.11 or higher
- CmdStan (automatically installed with cmdstanpy)
- Rust toolchain (only for building from source)
2 Your First Forecast
Create a simple forecast in just a few lines of code:
import polars as pl
from datetime import datetime, timedelta
from farseer import Farseer
# Create sample data
dates = [datetime(2020, 1, 1) + timedelta(days=i) for i in range(100)]
df = pl.DataFrame({
'ds': dates,
'y': list(range(100)) # Simple linear trend
})
# Create and fit model
m = Farseer()
m.fit(df)
# Make future predictions
future = m.make_future_dataframe(periods=30)
forecast = m.predict(future)
# View results
print(forecast.select(['ds', 'yhat', 'yhat_lower', 'yhat_upper']).tail())
Output:
shape: (5, 4)
┌─────────────────────┬────────────┬─────────────┬─────────────┐
│ ds ┆ yhat ┆ yhat_lower ┆ yhat_upper │
│ --- ┆ --- ┆ --- ┆ --- │
│ datetime[μs] ┆ f64 ┆ f64 ┆ f64 │
╞═════════════════════╪════════════╪═════════════╪═════════════╡
│ 2020-04-06 00:00:00 ┆ 126.234 ┆ 123.891 ┆ 128.577 │
│ 2020-04-07 00:00:00 ┆ 127.234 ┆ 124.891 ┆ 129.577 │
│ 2020-04-08 00:00:00 ┆ 128.234 ┆ 125.891 ┆ 130.577 │
│ 2020-04-09 00:00:00 ┆ 129.234 ┆ 126.891 ┆ 131.577 │
│ 2020-04-10 00:00:00 ┆ 130.234 ┆ 127.891 ┆ 132.577 │
└─────────────────────┴────────────┴─────────────┴─────────────┘
3 Understanding the Model
Farseer's forecast includes several components:
- yhat: The predicted value
- yhat_lower, yhat_upper: Uncertainty intervals (default 80%)
- trend: The overall trend component
- yearly: Yearly seasonal component (if enabled)
- weekly: Weekly seasonal component (if enabled)
- daily: Daily seasonal component (if enabled)
# View all components
print(forecast.select([
'ds', 'yhat', 'trend', 'yearly', 'weekly'
]).tail())
4 Customizing Your Model
Farseer provides many options to customize your forecast:
Seasonality Settings
m = Farseer(
yearly_seasonality=True, # Enable yearly patterns
weekly_seasonality=True, # Enable weekly patterns
daily_seasonality=False, # Disable daily patterns
seasonality_mode='additive' # or 'multiplicative'
)
Growth Settings
# Linear growth (default)
m = Farseer(growth='linear')
# Logistic growth with capacity
df['cap'] = 200 # Maximum capacity
m = Farseer(growth='logistic')
m.fit(df)
# Flat (no trend)
m = Farseer(growth='flat')
Changepoint Detection
m = Farseer(
n_changepoints=25, # Number of potential changepoints
changepoint_range=0.8, # Use first 80% of data
changepoint_prior_scale=0.05 # Flexibility (higher = more flexible)
)
Uncertainty Intervals
m = Farseer(interval_width=0.95) # 95% confidence intervals
m.fit(df)
forecast = m.predict(future)
5 Working with Real Data
Here's a complete example with realistic data including trend and seasonality:
import polars as pl
import numpy as np
from datetime import datetime, timedelta
from farseer import Farseer
# Generate realistic data
np.random.seed(42)
n = 365 * 2 # 2 years of daily data
dates = [datetime(2020, 1, 1) + timedelta(days=i) for i in range(n)]
# Create components
t = np.arange(n)
trend = t * 0.5 + 100 # Linear growth
yearly = 10 * np.sin(2 * np.pi * t / 365.25) # Yearly seasonality
weekly = 5 * np.sin(2 * np.pi * t / 7) # Weekly seasonality
noise = np.random.normal(0, 2, n)
y = trend + yearly + weekly + noise
df = pl.DataFrame({'ds': dates, 'y': y})
# Split into train/test
train_size = int(n * 0.8)
train = df[:train_size]
test = df[train_size:]
# Fit model
m = Farseer(
yearly_seasonality=True,
weekly_seasonality=True,
daily_seasonality=False
)
m.fit(train)
# Forecast on test set
forecast = m.predict(test)
# Evaluate performance
from sklearn.metrics import mean_absolute_error, mean_squared_error
mae = mean_absolute_error(test['y'], forecast['yhat'][:len(test)])
rmse = np.sqrt(mean_squared_error(test['y'], forecast['yhat'][:len(test)]))
print(f"Test MAE: {mae:.2f}")
print(f"Test RMSE: {rmse:.2f}")
6 Advanced Features
Weighted Observations
Give more importance to recent or reliable data by adding a 'weight' column:
import polars as pl
import numpy as np
from datetime import datetime, timedelta
from farseer import Farseer
dates = [datetime(2020, 1, 1) + timedelta(days=i) for i in range(100)]
df = pl.DataFrame({
'ds': dates,
'y': np.random.randn(100).cumsum() + 50,
'weight': [2.0 if i >= 80 else 1.0 for i in range(100)] # Emphasize recent data
})
m = Farseer()
m.fit(df) # Weights automatically detected and used
Conditional Seasonality
Apply seasonal patterns only when certain conditions are met:
from farseer import Farseer
m = Farseer()
# Add seasonality that only applies on weekdays
m.add_seasonality(
name='weekly_on_weekday',
period=7,
fourier_order=3,
condition_name='is_weekday'
)
# Add condition column to your data
df = df.with_columns((pl.col('ds').dt.weekday() < 5).alias('is_weekday'))
m.fit(df)
# Remember to add condition to future dataframe too
future = m.make_future_dataframe(periods=30)
future = future.with_columns((pl.col('ds').dt.weekday() < 5).alias('is_weekday'))
forecast = m.predict(future)
Floor & Cap (Logistic Growth)
Model data with both upper and lower bounds:
import polars as pl
from datetime import datetime, timedelta
from farseer import Farseer
# Logistic growth with floor (minimum) and cap (maximum)
dates = [datetime(2020, 1, 1) + timedelta(days=i) for i in range(100)]
df = pl.DataFrame({
'ds': dates,
'y': [...], # Your data
'floor': 1.5, # Minimum value
'cap': 10.0 # Maximum value
})
m = Farseer(growth='logistic')
m.fit(df)
# Add floor and cap to future dataframe
future = m.make_future_dataframe(periods=30)
future = future.with_columns([
pl.lit(1.5).alias('floor'),
pl.lit(10.0).alias('cap')
])
forecast = m.predict(future)
Smart Regressor Standardization
Farseer automatically detects binary vs continuous regressors:
from farseer import Farseer
m = Farseer()
# Binary regressor (0/1) - won't be standardized in 'auto' mode
m.add_regressor('is_weekend', standardize='auto')
# Continuous regressor - will be standardized in 'auto' mode
m.add_regressor('temperature', standardize='auto')
# Force standardization
m.add_regressor('custom_feature', standardize='true')
# Force no standardization
m.add_regressor('manual_feature', standardize='false')
m.fit(df)
Holiday Priors
Different prior scales for different holidays:
from farseer import Farseer
m = Farseer()
# Major holiday with strong prior
m.add_holidays(
'christmas',
dates=['2020-12-25', '2021-12-25', '2022-12-25'],
prior_scale=20.0, # Strong effect
lower_window=-1, # Day before
upper_window=1 # Day after
)
# Minor event with weak prior
m.add_holidays(
'minor_event',
dates=['2020-03-17', '2021-03-17'],
prior_scale=5.0 # Weak effect
)
m.fit(df)
7 Next Steps
Now that you've got the basics and advanced features, explore more:
- Weighted observations examples - Advanced weighting strategies
- Conditional seasonality examples - Complex seasonal patterns
- Custom regressors - Add additional variables
- Floor and cap examples - Saturating forecasts
- Manual changepoints - Specify known trend changes
- Holiday effects - Model special events
- Performance optimization - Maximize speed
- Migrating from Prophet - Switch from Prophet
💡 Pro Tips
- Use Polars DataFrames instead of Pandas for 5-10x better performance
- Farseer automatically uses all CPU cores for parallel optimization
- The 'auto' standardize mode intelligently detects binary regressors
- Conditional seasonality is powerful for modeling weekday/weekend differences
- Save trained models with
m.save('model.json')for deployment