Skip to main content

fdars_core/detrend/
mod.rs

1//! Detrending and decomposition functions for non-stationary functional data.
2//!
3//! This module provides methods for removing trends from functional data
4//! to enable more accurate seasonal analysis. It includes:
5//! - Linear detrending (least squares)
6//! - Polynomial detrending (QR decomposition)
7//! - Differencing (first and second order)
8//! - LOESS detrending (local polynomial regression)
9//! - Spline detrending (P-splines)
10//! - Automatic method selection via AIC
11
12use crate::matrix::FdMatrix;
13use std::borrow::Cow;
14
15pub mod auto;
16pub mod decompose;
17pub mod diff;
18pub mod linear;
19pub mod loess;
20pub mod polynomial;
21pub mod stl;
22
23#[cfg(test)]
24mod tests;
25
26// ---------------------------------------------------------------------------
27// Shared types
28// ---------------------------------------------------------------------------
29
30/// Result of detrending operation.
31#[derive(Debug, Clone)]
32pub struct TrendResult {
33    /// Estimated trend values (n x m)
34    pub trend: FdMatrix,
35    /// Detrended data (n x m)
36    pub detrended: FdMatrix,
37    /// Method used for detrending
38    pub method: Cow<'static, str>,
39    /// Polynomial coefficients (for polynomial methods, per sample)
40    /// For n samples with polynomial degree d: n x (d+1)
41    pub coefficients: Option<FdMatrix>,
42    /// Residual sum of squares for each sample
43    pub rss: Vec<f64>,
44    /// Number of parameters (for AIC calculation)
45    pub n_params: usize,
46}
47
48impl TrendResult {
49    /// Construct a no-op TrendResult (zero trend, data copied to detrended).
50    pub(super) fn empty(
51        data: &FdMatrix,
52        n: usize,
53        m: usize,
54        method: Cow<'static, str>,
55        n_params: usize,
56    ) -> Self {
57        TrendResult {
58            trend: FdMatrix::zeros(n, m),
59            detrended: FdMatrix::from_slice(data.as_slice(), n, m)
60                .unwrap_or_else(|_| FdMatrix::zeros(n, m)),
61            method,
62            coefficients: None,
63            rss: vec![0.0; n],
64            n_params,
65        }
66    }
67}
68
69/// Result of seasonal decomposition.
70#[derive(Debug, Clone)]
71pub struct DecomposeResult {
72    /// Trend component (n x m)
73    pub trend: FdMatrix,
74    /// Seasonal component (n x m)
75    pub seasonal: FdMatrix,
76    /// Remainder/residual component (n x m)
77    pub remainder: FdMatrix,
78    /// Period used for decomposition
79    pub period: f64,
80    /// Decomposition method ("additive" or "multiplicative")
81    pub method: Cow<'static, str>,
82}
83
84// ---------------------------------------------------------------------------
85// Shared helpers
86// ---------------------------------------------------------------------------
87
88/// Reassemble per-curve (trend, detrended, coefficients, rss) results into FdMatrix outputs.
89pub(super) fn reassemble_polynomial_results(
90    results: Vec<(Vec<f64>, Vec<f64>, Vec<f64>, f64)>,
91    n: usize,
92    m: usize,
93    n_coef: usize,
94) -> (FdMatrix, FdMatrix, FdMatrix, Vec<f64>) {
95    let mut trend = FdMatrix::zeros(n, m);
96    let mut detrended = FdMatrix::zeros(n, m);
97    let mut coefficients = FdMatrix::zeros(n, n_coef);
98    let mut rss = vec![0.0; n];
99    for (i, (t, d, coefs, r)) in results.into_iter().enumerate() {
100        for j in 0..m {
101            trend[(i, j)] = t[j];
102            detrended[(i, j)] = d[j];
103        }
104        for k in 0..n_coef {
105            coefficients[(i, k)] = coefs[k];
106        }
107        rss[i] = r;
108    }
109    (trend, detrended, coefficients, rss)
110}
111
112/// Reassemble per-curve (trend, detrended, rss) results into FdMatrix outputs.
113pub(super) fn reassemble_trend_results(
114    results: Vec<(Vec<f64>, Vec<f64>, f64)>,
115    n: usize,
116    m: usize,
117) -> (FdMatrix, FdMatrix, Vec<f64>) {
118    let mut trend = FdMatrix::zeros(n, m);
119    let mut detrended = FdMatrix::zeros(n, m);
120    let mut rss = vec![0.0; n];
121    for (i, (t, d, r)) in results.into_iter().enumerate() {
122        for j in 0..m {
123            trend[(i, j)] = t[j];
124            detrended[(i, j)] = d[j];
125        }
126        rss[i] = r;
127    }
128    (trend, detrended, rss)
129}
130
131// ---------------------------------------------------------------------------
132// Re-exports -- preserves the external API
133// ---------------------------------------------------------------------------
134
135pub use auto::auto_detrend;
136pub use decompose::{decompose_additive, decompose_multiplicative};
137pub use diff::detrend_diff;
138pub use linear::detrend_linear;
139pub use loess::detrend_loess;
140pub use polynomial::detrend_polynomial;
141pub use stl::{stl_decompose, stl_decompose_with_config, stl_fdata, StlConfig, StlResult};