This document provides a comprehensive reference for all public APIs in the anofox-forecast crate. The crate provides 35+ forecasting models, 76+ time series features, and extensive utilities for time series analysis.
- Core Types
- Missing Value Imputation
- Forecaster Trait
- Baseline Models
- Exponential Smoothing
- ARIMA Models
- Theta Models
- Intermittent Demand Models
- Advanced Models
- Ensemble
- Decomposition
- Spectral Analysis
- Feature Extraction
- Transformations
- Validation
- Changepoint Detection
- Utilities
- Probabilistic Postprocessing
Container for time series data with timestamps and multivariate values.
pub struct TimeSeries {
// Fields are private, use methods to access
}| Method | Description |
|---|---|
TimeSeries::new(timestamps, values, labels) |
Create with full configuration |
TimeSeries::univariate(values) |
Create simple single-dimension series |
| Method | Returns | Description |
|---|---|---|
len() |
usize |
Number of observations |
dimensions() |
usize |
Number of dimensions |
is_empty() |
bool |
Check if series is empty |
is_multivariate() |
bool |
Check if multi-dimensional |
timestamps() |
&[DateTime<Utc>] |
Get timestamps |
values(dimension) |
Result<&[f64]> |
Get values for dimension |
primary_values() |
&[f64] |
Get first dimension values |
slice(start, end) |
Result<TimeSeries> |
Extract subsequence |
has_missing_values() |
bool |
Check for NaN/Inf |
missing_mask() |
Vec<bool> |
Boolean mask: true where NaN/Inf (primary dimension) |
missing_count() |
Vec<usize> |
Count of missing values per dimension |
interpolated(fill_edges) |
TimeSeries |
Linear interpolation for NaN values |
sanitized(policy) |
Result<TimeSeries> |
Apply missing value policy |
imputed_forward_backward() |
TimeSeries |
Forward-fill then backward-fill |
imputed_moving_average(window) |
Result<TimeSeries> |
Centered moving average imputation |
imputed_seasonal(period) |
Result<TimeSeries> |
Seasonal median imputation |
with_imputed_regressors(policy) |
Result<TimeSeries> |
Impute NaN in regressor vectors |
Builder pattern for constructing TimeSeries.
let ts = TimeSeriesBuilder::new()
.timestamps(timestamps)
.values(values)
.frequency(Duration::days(1))
.build()?;| Method | Description |
|---|---|
new() |
Create new builder |
timestamps(Vec<DateTime<Utc>>) |
Set timestamps |
values(Vec<f64>) |
Set univariate values |
multivariate_values(Vec<Vec<f64>>, ValueLayout) |
Set multivariate values |
labels(Vec<String>) |
Set dimension labels |
frequency(Duration) |
Set time frequency |
build() |
Build the TimeSeries |
Prediction output containing point forecasts and optional confidence intervals.
pub struct Forecast {
// Fields are private, use methods to access
}| Method | Description |
|---|---|
Forecast::new() |
Create empty forecast |
Forecast::from_values(values) |
Create from point forecasts |
Forecast::from_values_with_intervals(values, lower, upper) |
Create with intervals |
| Method | Returns | Description |
|---|---|---|
horizon() |
usize |
Number of forecast steps |
primary() |
&[f64] |
Get point forecasts |
lower() |
Option<&[Vec<f64>]> |
Get lower bounds |
upper() |
Option<&[Vec<f64>]> |
Get upper bounds |
has_lower() |
bool |
Check if lower bounds exist |
has_upper() |
bool |
Check if upper bounds exist |
Error types for forecasting operations.
pub enum ForecastError {
EmptyData,
InsufficientData { needed: usize, got: usize, hint: Option<String> },
InvalidParameter(String),
DimensionMismatch { expected: usize, got: usize },
FitRequired,
MissingValues,
ComputationError(String),
// ... other variants
}| Variant | Description |
|---|---|
EmptyData |
Input data is empty |
InsufficientData |
Not enough data points |
InvalidParameter |
Invalid parameter value |
DimensionMismatch |
Dimension mismatch in data |
FitRequired |
Model not fitted before prediction |
MissingValues |
Missing values detected |
ComputationError |
Numerical computation error |
Tools for handling NaN/Inf values before model fitting. All models reject missing values at fit() time, so imputation must be applied beforehand.
pub enum MissingValuePolicy {
Drop, // Remove observations with NaN/Inf
Fill(f64), // Replace with specific value
ForwardFill, // Carry last valid value forward
BackwardFill, // Carry next valid value backward
FillMean, // Replace with mean of finite values
FillMedian, // Replace with median of finite values
Interpolate, // Linear interpolation (edges filled)
Error, // Return error if any missing
}Usage:
use anofox_forecast::core::{TimeSeries, MissingValuePolicy};
// Apply policy via sanitized()
let clean = ts.sanitized(MissingValuePolicy::FillMean)?;
let clean = ts.sanitized(MissingValuePolicy::BackwardFill)?;
let clean = ts.sanitized(MissingValuePolicy::Interpolate)?;| Method | Description |
|---|---|
sanitized(policy) |
Apply any MissingValuePolicy variant |
imputed_forward_backward() |
Forward-fill then backward-fill — handles both leading and trailing NaN |
imputed_moving_average(window) |
Centered window mean with multi-pass for adjacent gaps. Window must be odd. Remaining NaN filled with global mean. |
imputed_seasonal(period) |
Fill NaN with median of same seasonal position across cycles. Requires at least 1 full cycle. Errors if >50% missing in any bucket. |
with_imputed_regressors(policy) |
Apply fill policy to each regressor vector independently. Supports Fill, ForwardFill, BackwardFill, FillMean, FillMedian, Interpolate. |
| Method | Returns | Description |
|---|---|---|
has_missing_values() |
bool |
True if any NaN/Inf in any dimension |
missing_mask() |
Vec<bool> |
Per-observation mask for primary dimension |
missing_count() |
Vec<usize> |
Count of NaN/Inf per dimension |
Example — seasonal imputation:
// Weekly data with gaps
let clean = ts.imputed_seasonal(7)?; // Fill using same-weekday medianExample — regressor imputation:
let clean = ts.with_imputed_regressors(MissingValuePolicy::FillMean)?;Main trait interface that all forecasting models implement.
pub trait Forecaster {
fn fit(&mut self, series: &TimeSeries) -> Result<()>;
fn predict(&self, horizon: usize) -> Result<Forecast>;
fn predict_with_intervals(&self, horizon: usize, level: f64) -> Result<Forecast>;
fn fitted_values(&self) -> Option<&[f64]>;
fn residuals(&self) -> Option<&[f64]>;
fn name(&self) -> &str;
fn is_fitted(&self) -> bool;
}| Method | Parameters | Returns | Description |
|---|---|---|---|
fit |
series: &TimeSeries |
Result<()> |
Fit model to data |
predict |
horizon: usize |
Result<Forecast> |
Generate point forecasts |
predict_with_intervals |
horizon: usize, level: f64 |
Result<Forecast> |
Forecasts with confidence intervals |
fitted_values |
- | Option<&[f64]> |
Get in-sample fitted values |
residuals |
- | Option<&[f64]> |
Get residuals (actual - fitted) |
name |
- | &str |
Model name |
is_fitted |
- | bool |
Check if model is fitted |
Repeats the last observed value for all forecast horizons.
pub struct Naive;
impl Naive {
pub fn new() -> Self;
}Example:
let mut model = Naive::new();
model.fit(&ts)?;
let forecast = model.predict(12)?;Repeats values from the same season in the previous cycle.
pub struct SeasonalNaive {
period: usize,
}
impl SeasonalNaive {
pub fn new(period: usize) -> Self;
}| Parameter | Type | Description |
|---|---|---|
period |
usize |
Seasonal period (e.g., 12 for monthly data) |
Random walk model with trend drift component.
pub struct RandomWalkWithDrift;
impl RandomWalkWithDrift {
pub fn new() -> Self;
}The drift is estimated as the average change between consecutive observations.
Forecasts the mean of all historical observations.
pub struct HistoricAverage;
impl HistoricAverage {
pub fn new() -> Self;
}Moving window average forecaster.
pub struct WindowAverage {
window: usize,
}
impl WindowAverage {
pub fn new(window: usize) -> Self;
}| Parameter | Type | Description |
|---|---|---|
window |
usize |
Number of observations to average |
Seasonal window-based averaging.
pub struct SeasonalWindowAverage {
period: usize,
window: usize,
}
impl SeasonalWindowAverage {
pub fn new(period: usize, window: usize) -> Self;
}| Parameter | Type | Description |
|---|---|---|
period |
usize |
Seasonal period |
window |
usize |
Number of seasonal cycles to average |
Simple exponential smoothing for non-seasonal, non-trending data.
pub struct SimpleExponentialSmoothing {
alpha: Option<f64>,
}
impl SimpleExponentialSmoothing {
pub fn new(alpha: f64) -> Self;
pub fn auto() -> Self;
pub fn alpha(&self) -> Option<f64>;
pub fn level(&self) -> Option<f64>;
}| Parameter | Type | Description |
|---|---|---|
alpha |
f64 |
Smoothing parameter (0 < alpha < 1) |
| Method | Returns | Description |
|---|---|---|
auto() |
Self |
Create with auto-optimized alpha |
alpha() |
Option<f64> |
Get fitted alpha value |
level() |
Option<f64> |
Get final level |
Holt's linear trend method (double exponential smoothing).
pub struct Holt {
alpha: Option<f64>,
beta: Option<f64>,
}
impl Holt {
pub fn new(alpha: f64, beta: f64) -> Self;
pub fn auto() -> Self;
}| Parameter | Type | Description |
|---|---|---|
alpha |
f64 |
Level smoothing (0 < alpha < 1) |
beta |
f64 |
Trend smoothing (0 < beta < 1) |
Holt-Winters seasonal exponential smoothing.
pub struct HoltWinters {
period: usize,
seasonal_type: SeasonalType,
alpha: Option<f64>,
beta: Option<f64>,
gamma: Option<f64>,
}
impl HoltWinters {
pub fn new(period: usize, seasonal_type: SeasonalType) -> Self;
pub fn with_params(period: usize, seasonal_type: SeasonalType,
alpha: f64, beta: f64, gamma: f64) -> Self;
pub fn auto(period: usize, seasonal_type: SeasonalType) -> Self;
}
pub enum SeasonalType {
Additive,
Multiplicative,
}| Parameter | Type | Description |
|---|---|---|
period |
usize |
Seasonal period |
seasonal_type |
SeasonalType |
Additive or Multiplicative |
alpha |
f64 |
Level smoothing |
beta |
f64 |
Trend smoothing |
gamma |
f64 |
Seasonal smoothing |
Error-Trend-Seasonal state-space model following the FPP3 taxonomy.
pub struct ETS {
spec: ETSSpec,
}
impl ETS {
pub fn new(spec: ETSSpec, period: usize) -> Self;
}
pub struct ETSSpec {
pub error: ErrorType,
pub trend: TrendType,
pub seasonal: SeasonalType,
}
pub enum ErrorType { Additive, Multiplicative }
pub enum TrendType { None, Additive, AdditiveDamped }
pub enum SeasonalType { None, Additive, Multiplicative }This implementation follows the ETS taxonomy from Forecasting: Principles and Practice (FPP3).
Valid ETS Specifications (16 of 18 combinations):
| Code | Name | Constructor |
|---|---|---|
| ANN | Simple exponential smoothing | ETSSpec::ann() |
| AAN | Holt's linear method | ETSSpec::aan() |
| AAdN | Additive damped trend | ETSSpec::aadn() |
| ANA | Seasonal (no trend, additive) | ETSSpec::ana() |
| ANM | Seasonal (no trend, multiplicative) | ETSSpec::anm() |
| AAA | Holt-Winters additive | ETSSpec::aaa() |
| AAM | Holt-Winters multiplicative seasonal | ETSSpec::aam() |
| AAdA | Damped Holt-Winters additive | ETSSpec::aada() |
| AAdM | Damped Holt-Winters multiplicative | ETSSpec::aadm() |
| MNN | Multiplicative error simple smoothing | ETSSpec::mnn() |
| MAN | Multiplicative error with trend | ETSSpec::man() |
| MAdN | Multiplicative error damped trend | ETSSpec::madn() |
| MNM | Multiplicative error and seasonal | ETSSpec::mnm() |
| MAM | Multiplicative Holt-Winters | ETSSpec::mam() |
| MAdM | Damped multiplicative Holt-Winters | ETSSpec::madm() |
Invalid/Unstable (rejected):
| Code | Reason |
|---|---|
| MAA | Multiplicative error + additive trend + additive seasonal |
| MAdA | Multiplicative error + damped trend + additive seasonal |
impl ETSSpec {
/// Parse from notation string like "AAA", "MAM", "AAdM"
pub fn from_notation(notation: &str) -> Result<Self>;
/// Check if this combination is valid
pub fn is_valid(&self) -> bool;
}Example:
use anofox_forecast::models::exponential::ETSSpec;
// Parse notation
let spec = ETSSpec::from_notation("AAA")?; // Holt-Winters additive
let spec = ETSSpec::from_notation("MAdM")?; // Damped multiplicative
// Invalid combinations return error
assert!(ETSSpec::from_notation("MAA").is_err());Automatic ETS model selection using information criteria.
pub struct AutoETS {
config: AutoETSConfig,
}
impl AutoETS {
pub fn new() -> Self;
pub fn with_config(config: AutoETSConfig) -> Self;
pub fn with_period(period: usize) -> Self;
}
pub struct AutoETSConfig {
pub criterion: SelectionCriterion,
pub seasonal_period: Option<usize>,
pub allow_multiplicative: bool,
}
pub enum SelectionCriterion { AIC, BIC, AICc }Multiplicative seasonal exponential smoothing.
pub struct SeasonalES {
period: usize,
}
impl SeasonalES {
pub fn new(period: usize) -> Self;
}Non-seasonal ARIMA(p,d,q) model.
pub struct ARIMA {
spec: ARIMASpec,
}
impl ARIMA {
pub fn new(p: usize, d: usize, q: usize) -> Self;
pub fn spec(&self) -> &ARIMASpec;
pub fn ar_coefficients(&self) -> &[f64];
pub fn ma_coefficients(&self) -> &[f64];
pub fn intercept(&self) -> f64;
pub fn aic(&self) -> Option<f64>;
pub fn bic(&self) -> Option<f64>;
}
pub struct ARIMASpec {
pub p: usize, // AR order
pub d: usize, // Differencing order
pub q: usize, // MA order
}| Parameter | Type | Description |
|---|---|---|
p |
usize |
Autoregressive order |
d |
usize |
Differencing order |
q |
usize |
Moving average order |
Seasonal ARIMA(p,d,q)(P,D,Q)[s] model.
pub struct SARIMA {
spec: SARIMASpec,
}
impl SARIMA {
pub fn new(p: usize, d: usize, q: usize,
cap_p: usize, cap_d: usize, cap_q: usize, s: usize) -> Self;
}
pub struct SARIMASpec {
pub p: usize, pub d: usize, pub q: usize, // Non-seasonal
pub cap_p: usize, pub cap_d: usize, pub cap_q: usize, // Seasonal
pub s: usize, // Seasonal period
}| Parameter | Type | Description |
|---|---|---|
p, d, q |
usize |
Non-seasonal orders |
P, D, Q |
usize |
Seasonal orders |
s |
usize |
Seasonal period |
Automatic ARIMA order selection.
pub struct AutoARIMA {
config: AutoARIMAConfig,
}
impl AutoARIMA {
pub fn new() -> Self;
pub fn with_config(config: AutoARIMAConfig) -> Self;
}
pub struct AutoARIMAConfig {
pub max_p: usize,
pub max_d: usize,
pub max_q: usize,
pub seasonal_period: Option<usize>,
pub criterion: SelectionCriterion,
pub stepwise: bool,
pub true_stepwise: bool, // Neighbor-based hill climbing
}
impl AutoARIMAConfig {
pub fn with_true_stepwise(self, enabled: bool) -> Self;
pub fn exhaustive(self) -> Self;
}| Parameter | Description |
|---|---|
stepwise |
Use stepwise search (faster, fewer models) |
true_stepwise |
Use neighbor-based hill climbing (60-70% fewer evaluations) |
Parallel Execution:
Enable with --features parallel for 4-8x speedup on multi-core systems:
[dependencies]
anofox-forecast = { version = "0.3", features = ["parallel"] }Standard Theta Model (STM) for forecasting.
pub struct Theta {
theta: f64,
seasonal_period: usize,
decomposition: DecompositionType,
}
impl Theta {
pub fn new() -> Self;
pub fn with_theta(theta: f64) -> Self;
pub fn seasonal(period: usize) -> Self;
pub fn seasonal_with_type(period: usize, decomposition: DecompositionType) -> Self;
}
pub enum DecompositionType {
Additive,
Multiplicative,
}| Parameter | Type | Description |
|---|---|---|
theta |
f64 |
Theta parameter (default: 2.0) |
period |
usize |
Seasonal period (0 for non-seasonal) |
decomposition |
DecompositionType |
Seasonal decomposition type |
Theta model with optimized alpha and theta parameters.
pub struct OptimizedTheta;
impl OptimizedTheta {
pub fn new() -> Self;
}Parameters are optimized using Nelder-Mead minimization of MSE.
Theta with dynamic linear coefficient updates.
pub struct DynamicTheta {
period: Option<usize>,
}
impl DynamicTheta {
pub fn new(period: Option<usize>) -> Self;
}Combines dynamic updates with parameter optimization.
pub struct DynamicOptimizedTheta {
period: Option<usize>,
}
impl DynamicOptimizedTheta {
pub fn new(period: Option<usize>) -> Self;
}Automatic Theta model selection.
pub struct AutoTheta;
impl AutoTheta {
pub fn new() -> Self;
}Selects the best Theta variant based on cross-validation performance.
Croston's method for intermittent demand forecasting.
pub struct Croston {
variant: CrostonVariant,
alpha: f64,
}
impl Croston {
pub fn new() -> Self; // Classic variant
pub fn classic() -> Self;
pub fn sba() -> Self; // Syntetos-Babai adjusted
pub fn with_alpha(alpha: f64) -> Self;
}
pub enum CrostonVariant {
Classic,
SBA, // Syntetos-Babai Approximation
}| Variant | Description |
|---|---|
Classic |
Original Croston method |
SBA |
Bias-corrected Syntetos-Babai variant |
Teunter-Syntetos-Babai method for intermittent demand.
pub struct TSB {
alpha: f64,
beta: f64,
}
impl TSB {
pub fn new() -> Self;
pub fn with_params(alpha: f64, beta: f64) -> Self;
}Aggregate-Disaggregate Intermittent Demand Approach.
pub struct ADIDA {
aggregation_level: usize,
}
impl ADIDA {
pub fn new() -> Self;
pub fn with_aggregation(level: usize) -> Self;
}Intermittent Multiple Aggregation Prediction Algorithm.
pub struct IMAPA;
impl IMAPA {
pub fn new() -> Self;
}Combines forecasts from multiple aggregation levels.
Multiple Fourier Linear Exponential Smoothing - gradient boosted decomposition.
pub struct MFLES {
seasonal_periods: Vec<usize>,
}
impl MFLES {
pub fn new(seasonal_periods: Vec<usize>) -> Self;
pub fn with_max_rounds(rounds: usize) -> Self;
}| Parameter | Type | Description |
|---|---|---|
seasonal_periods |
Vec<usize> |
Seasonal periods to model |
max_rounds |
usize |
Maximum boosting iterations |
MSTL decomposition-based forecaster for multiple seasonalities.
pub struct MSTLForecaster {
seasonal_periods: Vec<usize>,
}
impl MSTLForecaster {
pub fn new(seasonal_periods: Vec<usize>) -> Self;
}Trigonometric seasonality, Box-Cox transformation, ARMA errors, Trend, Seasonal components.
pub struct TBATS {
seasonal_periods: Vec<usize>,
}
impl TBATS {
pub fn new(seasonal_periods: Vec<usize>) -> Self;
}Handles complex seasonal patterns with trigonometric representation.
Automatic TBATS configuration selection.
pub struct AutoTBATS;
impl AutoTBATS {
pub fn new(seasonal_periods: Vec<usize>) -> Self;
}Generalized Autoregressive Conditional Heteroskedasticity for volatility modeling.
pub struct GARCH {
p: usize,
q: usize,
}
impl GARCH {
pub fn new(p: usize, q: usize) -> Self;
}| Parameter | Type | Description |
|---|---|---|
p |
usize |
GARCH order (variance lags) |
q |
usize |
ARCH order (squared residual lags) |
Combines multiple forecasting models.
pub struct Ensemble {
models: Vec<Box<dyn Forecaster>>,
method: CombinationMethod,
}
impl Ensemble {
pub fn new(models: Vec<Box<dyn Forecaster>>) -> Self;
pub fn with_method(method: CombinationMethod) -> Self;
pub fn with_weights(weights: Vec<f64>) -> Self;
}
pub enum CombinationMethod {
Mean,
Median,
WeightedMSE,
Custom,
}| Method | Description |
|---|---|
Mean |
Simple average of forecasts |
Median |
Median of forecasts |
WeightedMSE |
Inverse MSE weighting |
Custom |
User-provided weights |
Seasonal-Trend decomposition using LOESS.
pub struct STL {
period: usize,
}
impl STL {
pub fn new(period: usize) -> Self;
pub fn decompose(&self, series: &[f64]) -> Result<STLResult>;
}
pub struct STLResult {
pub seasonal: Vec<f64>,
pub trend: Vec<f64>,
pub remainder: Vec<f64>,
}Multiple Seasonal-Trend decomposition using LOESS.
pub struct MSTL {
periods: Vec<usize>,
}
impl MSTL {
pub fn new(periods: Vec<usize>) -> Self;
pub fn decompose(&self, series: &[f64]) -> Result<MSTLResult>;
}
pub struct MSTLResult {
pub seasonal_components: Vec<Vec<f64>>,
pub trend: Vec<f64>,
pub remainder: Vec<f64>,
}Welch's method for reduced variance spectral estimation using overlapping windowed segments.
/// Welch's periodogram for reduced variance spectral estimation
pub fn welch_periodogram(
signal: &[f64],
window_size: usize, // Segment size (power of 2 recommended)
overlap: f64, // Overlap ratio (0.0-0.9, typically 0.5)
) -> Vec<(usize, f64)>;| Parameter | Type | Description |
|---|---|---|
signal |
&[f64] |
Input time series |
window_size |
usize |
Segment size (power of 2 for efficiency) |
overlap |
f64 |
Overlap ratio between segments (0.0-0.9) |
Returns: Vector of (period, power) tuples sorted by period (largest first).
Example:
use anofox_forecast::detection::welch_periodogram;
let signal: Vec<f64> = (0..256)
.map(|i| (2.0 * std::f64::consts::PI * i as f64 / 12.0).sin())
.collect();
let psd = welch_periodogram(&signal, 64, 0.5);
if let Some((period, _)) = psd.iter().max_by(|a, b| a.1.partial_cmp(&b.1).unwrap()) {
println!("Dominant period: {}", period);
}Note: For comprehensive periodicity detection (ACF, FFT, Autoperiod, CFD-Autoperiod, SAZED), see the fdars crate.
The crate provides 76+ time series features organized by category.
pub fn mean(series: &[f64]) -> f64;
pub fn median(series: &[f64]) -> f64;
pub fn variance(series: &[f64]) -> f64;
pub fn standard_deviation(series: &[f64]) -> f64;
pub fn minimum(series: &[f64]) -> f64;
pub fn maximum(series: &[f64]) -> f64;
pub fn sum_values(series: &[f64]) -> f64;
pub fn abs_energy(series: &[f64]) -> f64;
pub fn mean_abs_change(series: &[f64]) -> f64;
pub fn mean_change(series: &[f64]) -> f64;pub fn skewness(series: &[f64]) -> f64;
pub fn kurtosis(series: &[f64]) -> f64;
pub fn quantile(series: &[f64], q: f64) -> f64;
pub fn variation_coefficient(series: &[f64]) -> f64;pub fn autocorrelation(series: &[f64], lag: usize) -> f64;
pub fn partial_autocorrelation(series: &[f64], lag: usize) -> f64;pub fn approximate_entropy(series: &[f64], m: usize, r: f64) -> f64;
pub fn sample_entropy(series: &[f64], m: usize, r: f64) -> f64;
pub fn permutation_entropy(series: &[f64], order: usize) -> f64;pub fn cid_ce(series: &[f64], normalize: bool) -> f64;
pub fn lempel_ziv_complexity(series: &[f64], threshold: Option<f64>) -> f64;pub fn linear_trend(series: &[f64]) -> LinearTrendResult;
pub fn ar_coefficient(series: &[f64], k: usize) -> f64;pub fn boxcox(series: &[f64], lambda: f64) -> Result<BoxCoxResult>;
pub fn boxcox_auto(series: &[f64]) -> Result<BoxCoxResult>;
pub fn inv_boxcox(transformed: &[f64], lambda: f64) -> Result<Vec<f64>>;
pub fn boxcox_lambda(series: &[f64]) -> Result<f64>;
pub struct BoxCoxResult {
pub transformed: Vec<f64>,
pub lambda: f64,
}pub fn standardize(series: &[f64]) -> ScaleResult;
pub fn normalize(series: &[f64]) -> ScaleResult;
pub fn robust_scale(series: &[f64]) -> ScaleResult;
pub struct ScaleResult {
pub scaled: Vec<f64>,
pub mean: f64,
pub std: f64,
}pub fn rolling_mean(series: &[f64], window: usize, center: bool) -> Vec<f64>;
pub fn rolling_std(series: &[f64], window: usize, center: bool) -> Vec<f64>;
pub fn rolling_min(series: &[f64], window: usize, center: bool) -> Vec<f64>;
pub fn rolling_max(series: &[f64], window: usize, center: bool) -> Vec<f64>;
pub fn expanding_mean(series: &[f64]) -> Vec<f64>;
pub fn ewm_mean(series: &[f64], span: f64) -> Vec<f64>;pub fn ljung_box(residuals: &[f64], nlags: Option<usize>, seasonal: usize) -> LjungBoxResult;
pub fn box_pierce(residuals: &[f64], nlags: Option<usize>, seasonal: usize) -> LjungBoxResult;
pub fn durbin_watson(residuals: &[f64]) -> DurbinWatsonResult;
pub struct LjungBoxResult {
pub statistic: f64,
pub p_value: f64,
pub degrees_of_freedom: usize,
}
pub struct DurbinWatsonResult {
pub statistic: f64, // Values near 2 indicate no autocorrelation
}pub fn adf_test(series: &[f64], nlags: Option<usize>) -> StationarityResult;
pub fn kpss_test(series: &[f64], nlags: Option<usize>) -> StationarityResult;
pub struct StationarityResult {
pub test_statistic: f64,
pub p_value: f64,
pub is_stationary: bool,
}PELT (Pruned Exact Linear Time) algorithm for changepoint detection.
pub fn pelt_detect(series: &[f64], config: &PeltConfig) -> PeltResult;
pub struct PeltConfig {
pub penalty: f64,
pub min_segment_length: usize,
pub cost_fn: CostFunction,
}
impl PeltConfig {
pub fn default() -> Self;
pub fn penalty(penalty: f64) -> Self;
pub fn with_bic_penalty(n: usize) -> Self;
}
pub enum CostFunction {
L2, // Squared cost (default)
L1, // Absolute cost, robust to outliers
Normal, // Normal likelihood
Poisson, // Poisson likelihood for count data
LinearTrend, // Detects slope/trend changes
MeanVariance, // Joint mean and variance changes
Cusum, // Sustained mean shifts
}
pub struct PeltResult {
pub changepoints: Vec<usize>,
pub n_changepoints: usize,
}pub fn calculate_metrics(actual: &[f64], predicted: &[f64],
seasonal_period: Option<usize>) -> Result<AccuracyMetrics>;
pub struct AccuracyMetrics {
pub mae: f64, // Mean Absolute Error
pub mse: f64, // Mean Squared Error
pub rmse: f64, // Root Mean Squared Error
pub mape: f64, // Mean Absolute Percentage Error
pub smape: f64, // Symmetric MAPE
pub mase: f64, // Mean Absolute Scaled Error
pub r_squared: f64,
}pub fn cross_validate<F>(config: &CVConfig, series: &TimeSeries,
model_factory: F) -> Result<CVResults>;
pub struct CVConfig {
pub horizon: usize,
pub initial_window: usize,
pub step_size: usize,
pub strategy: CVStrategy,
}
pub enum CVStrategy {
Rolling, // Fixed window slides forward
Expanding, // Window grows over time
}
pub struct CVResults {
pub n_folds: usize,
pub aggregated: AggregatedMetrics,
pub fold_metrics: Vec<AccuracyMetrics>,
}pub fn nelder_mead(objective: fn(&[f64]) -> f64, initial: Vec<f64>,
config: &NelderMeadConfig) -> Result<NelderMeadResult>;
pub struct NelderMeadConfig {
pub max_iter: usize,
pub tolerance: f64,
}
pub struct NelderMeadResult {
pub parameters: Vec<f64>,
pub value: f64,
pub iterations: usize,
}Bootstrap methods for empirical confidence intervals.
pub struct BootstrapConfig {
pub n_samples: usize, // Number of bootstrap samples (default: 1000)
pub block_size: Option<usize>, // Block size for block bootstrap
pub seed: Option<u64>, // Random seed for reproducibility
}
impl BootstrapConfig {
pub fn new(n_samples: usize) -> Self;
pub fn with_block_size(self, block_size: usize) -> Self;
pub fn with_seed(self, seed: u64) -> Self;
}
pub struct BootstrapResult {
pub lower: Vec<f64>, // Lower bounds per horizon step
pub upper: Vec<f64>, // Upper bounds per horizon step
pub level: f64, // Confidence level used
pub n_samples: usize, // Number of samples used
}
/// Generate bootstrap confidence intervals
pub fn bootstrap_intervals<M: Forecaster + Clone>(
model: &M,
series: &TimeSeries,
horizon: usize,
level: f64,
config: &BootstrapConfig,
) -> Result<BootstrapResult>;
/// Generate forecast with bootstrap intervals
pub fn bootstrap_forecast<M: Forecaster + Clone>(
model: &M,
series: &TimeSeries,
horizon: usize,
level: f64,
config: &BootstrapConfig,
) -> Result<Forecast>;| Method | Description |
|---|---|
| Residual Bootstrap | Resamples fitted residuals with replacement |
| Block Bootstrap | Preserves autocorrelation structure |
Example:
use anofox_forecast::utils::bootstrap::{bootstrap_forecast, BootstrapConfig};
let config = BootstrapConfig::new(500).with_seed(42);
let forecast = bootstrap_forecast(&model, &ts, 12, 0.95, &config)?;The postprocessing module provides methods to convert point forecasts into calibrated predictive distributions with coverage guarantees. It follows the approach of PostForecasts.jl.
Point forecasts with optional timestamps and metadata.
pub struct PointForecasts {
timestamps: Vec<DateTime<Utc>>,
values: Vec<f64>,
model_name: Option<String>,
}
impl PointForecasts {
pub fn new(timestamps: Vec<DateTime<Utc>>, values: Vec<f64>) -> Result<Self>;
pub fn from_values(values: Vec<f64>) -> Self;
pub fn empty() -> Self;
pub fn with_model_name(self, name: impl Into<String>) -> Self;
pub fn len(&self) -> usize;
pub fn values(&self) -> &[f64];
pub fn timestamps(&self) -> &[DateTime<Utc>];
}Multi-quantile forecasts representing a discrete predictive distribution.
pub struct QuantileForecasts {
timestamps: Vec<DateTime<Utc>>,
quantiles: Vec<f64>,
values: Vec<Vec<f64>>, // values[time][quantile]
}
impl QuantileForecasts {
pub fn new(timestamps: Vec<DateTime<Utc>>, quantiles: Vec<f64>, values: Vec<Vec<f64>>) -> Result<Self>;
pub fn from_values(quantiles: Vec<f64>, values: Vec<Vec<f64>>) -> Result<Self>;
pub fn n_times(&self) -> usize;
pub fn n_quantiles(&self) -> usize;
pub fn quantiles(&self) -> &[f64];
pub fn at_time(&self, idx: usize) -> Option<&[f64]>;
pub fn at_quantile(&self, idx: usize) -> Option<Vec<f64>>;
pub fn median(&self) -> Option<Vec<f64>>;
pub fn to_prediction_intervals(&self, coverage: f64) -> Option<PredictionIntervals>;
}Lower and upper bounds with coverage level.
pub struct PredictionIntervals {
timestamps: Vec<DateTime<Utc>>,
lower: Vec<f64>,
upper: Vec<f64>,
coverage: f64,
}
impl PredictionIntervals {
pub fn new(timestamps: Vec<DateTime<Utc>>, lower: Vec<f64>, upper: Vec<f64>, coverage: f64) -> Result<Self>;
pub fn from_bounds(lower: Vec<f64>, upper: Vec<f64>, coverage: f64) -> Result<Self>;
pub fn lower(&self) -> &[f64];
pub fn upper(&self) -> &[f64];
pub fn coverage(&self) -> f64;
pub fn widths(&self) -> Vec<f64>;
pub fn midpoints(&self) -> Vec<f64>;
pub fn contains(&self, values: &[f64]) -> Vec<bool>;
pub fn empirical_coverage(&self, actuals: &[f64]) -> Option<f64>;
}Distribution-free prediction intervals with coverage guarantees.
pub struct ConformalPredictor {
coverage: f64,
method: ConformalMethod,
}
pub enum ConformalMethod {
Split { cal_fraction: f64 },
CrossVal { n_folds: usize },
JackknifePlus,
}
impl ConformalPredictor {
pub fn new(coverage: f64, method: ConformalMethod) -> Self;
pub fn split(coverage: f64) -> Self;
pub fn cross_val(coverage: f64, n_folds: usize) -> Self;
pub fn jackknife_plus(coverage: f64) -> Self;
pub fn fit(&self, forecasts: &[f64], actuals: &[f64]) -> Result<ConformalResult>;
pub fn predict(&self, result: &ConformalResult, forecasts: &PointForecasts) -> PredictionIntervals;
pub fn predict_values(&self, result: &ConformalResult, values: &[f64]) -> PredictionIntervals;
}
pub struct ConformalResult {
pub fn scores(&self) -> &[f64];
pub fn quantile_value(&self) -> f64;
pub fn coverage(&self) -> f64;
pub fn method(&self) -> &ConformalMethod;
}| Method | Description |
|---|---|
Split |
Fast method using a holdout calibration set |
CrossVal |
Uses all data via k-fold cross-validation |
JackknifePlus |
Leave-one-out with finite sample validity |
Example:
use anofox_forecast::postprocess::{ConformalPredictor, ConformalMethod, PointForecasts};
let predictor = ConformalPredictor::split(0.90);
let result = predictor.fit(&historical_forecasts, &historical_actuals)?;
let new_forecasts = PointForecasts::from_values(vec![20.0, 21.0, 22.0]);
let intervals = predictor.predict(&result, &new_forecasts);Non-parametric empirical error distribution for uncertainty quantification.
pub struct HistoricalSimulator {
quantiles: Vec<f64>,
window_size: Option<usize>,
}
impl HistoricalSimulator {
pub fn new(quantiles: Vec<f64>) -> Self;
pub fn with_window(quantiles: Vec<f64>, window_size: usize) -> Self;
pub fn fit(&self, forecasts: &[f64], actuals: &[f64]) -> Result<HistoricalSimResult>;
pub fn predict_values(&self, result: &HistoricalSimResult, values: &[f64]) -> Result<QuantileForecasts>;
}Gaussian error assumption baseline for uncertainty quantification.
pub struct NormalPredictor {
quantiles: Vec<f64>,
}
impl NormalPredictor {
pub fn new(quantiles: Vec<f64>) -> Self;
pub fn fit(&self, forecasts: &[f64], actuals: &[f64]) -> Result<NormalResult>;
pub fn predict_values(&self, result: &NormalResult, values: &[f64]) -> Result<QuantileForecasts>;
}Isotonic Distributional Regression for state-of-the-art calibration.
pub struct IDRPredictor {
quantiles: Vec<f64>,
}
impl IDRPredictor {
pub fn new(quantiles: Vec<f64>) -> Self;
pub fn fit(&self, forecasts: &[f64], actuals: &[f64]) -> Result<IDRResult>;
pub fn predict_values(&self, result: &IDRResult, values: &[f64]) -> Result<QuantileForecasts>;
}Quantile Regression Averaging for ensemble combining.
pub struct QRAPredictor {
quantiles: Vec<f64>,
regularization: QRARegularization,
}
pub enum QRARegularization {
None,
L1(f64),
L2(f64),
}
impl QRAPredictor {
pub fn new(quantiles: Vec<f64>) -> Self;
pub fn with_regularization(quantiles: Vec<f64>, reg: QRARegularization) -> Self;
}Unified interface for all postprocessing methods.
pub struct PostProcessor {
model: PostModel,
}
pub enum PostModel {
Conformal { coverage: f64, method: ConformalMethod },
HistoricalSim { quantiles: Vec<f64>, window_size: Option<usize> },
Normal { quantiles: Vec<f64> },
IDR { quantiles: Vec<f64> },
}
impl PostProcessor {
pub fn new(model: PostModel) -> Self;
pub fn conformal(coverage: f64) -> Self;
pub fn historical_sim(quantiles: Vec<f64>) -> Self;
pub fn normal(quantiles: Vec<f64>) -> Self;
pub fn idr(quantiles: Vec<f64>) -> Self;
pub fn train(&self, forecasts: &PointForecasts, actuals: &[f64]) -> Result<TrainedModel>;
pub fn predict_intervals(&self, trained: &TrainedModel, forecasts: &PointForecasts) -> Result<PredictionIntervals>;
pub fn predict_quantiles(&self, trained: &TrainedModel, forecasts: &PointForecasts) -> Result<QuantileForecasts>;
pub fn point_to_quantiles(&self, train_forecasts: &PointForecasts, train_actuals: &[f64], predict_forecasts: &PointForecasts) -> Result<QuantileForecasts>;
}
pub enum TrainedModel {
Conformal(ConformalResult),
HistoricalSim(HistoricalSimResult),
Normal(NormalResult),
IDR(IDRResult),
}Example:
use anofox_forecast::postprocess::{PostProcessor, PointForecasts};
// Create a conformal processor with 90% coverage
let processor = PostProcessor::conformal(0.90);
// Train on historical data
let train_forecasts = PointForecasts::from_values(historical_f);
let trained = processor.train(&train_forecasts, &historical_actuals)?;
// Generate prediction intervals
let new_forecasts = PointForecasts::from_values(new_f);
let intervals = processor.predict_intervals(&trained, &new_forecasts)?;Rolling/expanding window backtesting with horizon-aware calibration.
pub struct BacktestConfig {
pub initial_window: usize,
pub step: usize,
pub horizon: usize,
pub expanding: bool,
pub horizon_aware: bool,
}
impl BacktestConfig {
pub fn new() -> Self;
pub fn initial_window(self, size: usize) -> Self;
pub fn step(self, step: usize) -> Self;
pub fn horizon(self, horizon: usize) -> Self;
pub fn expanding(self, expanding: bool) -> Self;
pub fn horizon_aware(self, aware: bool) -> Self;
}
pub struct BacktestResult {
pub fn n_folds(&self) -> usize;
pub fn config(&self) -> &BacktestConfig;
pub fn folds(&self) -> impl Iterator<Item = &BacktestFold>;
pub fn coverage(&self) -> f64;
pub fn calibration_error(&self, target_coverage: f64) -> f64;
pub fn interval_widths(&self) -> f64;
pub fn coverage_by_horizon(&self) -> &HashMap<usize, f64>;
pub fn calibrated_model(&self, processor: &PostProcessor) -> Result<TrainedModel>;
pub fn calibrated_model_by_horizon(&self, processor: &PostProcessor) -> Result<CalibratedModelByHorizon>;
}
pub struct BacktestFold {
pub fold_idx: usize,
pub train_start: usize,
pub train_end: usize,
pub test_start: usize,
pub test_end: usize,
pub intervals: PredictionIntervals,
pub actuals: Vec<f64>,
pub coverage: f64,
pub avg_width: f64,
}Example:
use anofox_forecast::postprocess::{PostProcessor, BacktestConfig, PointForecasts};
let processor = PostProcessor::conformal(0.90);
let config = BacktestConfig::new()
.initial_window(100)
.step(10)
.horizon(7)
.horizon_aware(true);
let forecasts = PointForecasts::from_values(all_forecasts);
let results = processor.backtest(&forecasts, &all_actuals, config)?;
println!("Coverage: {:.1}%", results.coverage() * 100.0);
println!("Calibration error: {:.3}", results.calibration_error(0.90));Recalibrate quantile forecasts using conformal prediction.
pub fn conformalize(
forecasts: &QuantileForecasts,
calib_forecasts: &QuantileForecasts,
calib_actuals: &[f64],
) -> Result<ConformalizeResult>;
pub fn conformalize_with_config(
forecasts: &QuantileForecasts,
calib_forecasts: &QuantileForecasts,
calib_actuals: &[f64],
config: ConformalizeConfig,
) -> Result<ConformalizeResult>;
pub struct ConformalizeConfig {
method: ConformalMethod,
symmetric: bool,
}
impl ConformalizeConfig {
pub fn new() -> Self;
pub fn method(self, method: ConformalMethod) -> Self;
pub fn symmetric(self, symmetric: bool) -> Self;
}
pub struct ConformalizeResult {
pub fn forecasts(&self) -> &QuantileForecasts;
pub fn into_forecasts(self) -> QuantileForecasts;
pub fn adjustments(&self) -> &[f64];
pub fn original_coverage(&self) -> &[f64];
}Example:
use anofox_forecast::postprocess::{conformalize, QuantileForecasts};
// Calibrate quantile forecasts
let calibrated = conformalize(&test_forecasts, &calib_forecasts, &calib_actuals)?;
// Get the recalibrated forecasts
let improved = calibrated.into_forecasts();Convenience re-exports for common usage:
pub use crate::core::{Forecast, TimeSeries};
pub use crate::error::{ForecastError, Result};
pub use crate::models::Forecaster;
pub use crate::utils::{calculate_metrics, AccuracyMetrics};Usage:
use anofox_forecast::prelude::*;