fdars_core/detrend/
linear.rs1use crate::iter_maybe_parallel;
2use crate::matrix::FdMatrix;
3#[cfg(feature = "parallel")]
4use rayon::iter::ParallelIterator;
5use std::borrow::Cow;
6
7use super::TrendResult;
8
9#[must_use = "returns the detrending result which should not be discarded"]
18pub fn detrend_linear(data: &FdMatrix, argvals: &[f64]) -> TrendResult {
19 let (n, m) = data.shape();
20 if n == 0 || m < 2 || argvals.len() != m {
21 return TrendResult::empty(data, n, m, Cow::Borrowed("linear"), 2);
22 }
23
24 let mean_t: f64 = argvals.iter().sum::<f64>() / m as f64;
25 let ss_t: f64 = argvals.iter().map(|&t| (t - mean_t).powi(2)).sum();
26
27 let scalars: Vec<(f64, f64, f64)> = iter_maybe_parallel!(0..n)
29 .map(|i| {
30 let mut sum_y = 0.0;
31 for j in 0..m {
32 sum_y += data[(i, j)];
33 }
34 let mean_y = sum_y / m as f64;
35 let mut sp = 0.0;
36 for j in 0..m {
37 sp += (argvals[j] - mean_t) * (data[(i, j)] - mean_y);
38 }
39 let slope = if ss_t.abs() > 1e-15 { sp / ss_t } else { 0.0 };
40 let intercept = mean_y - slope * mean_t;
41 let mut rss = 0.0;
42 for j in 0..m {
43 let residual = data[(i, j)] - (intercept + slope * argvals[j]);
44 rss += residual * residual;
45 }
46 (intercept, slope, rss)
47 })
48 .collect();
49
50 let mut trend = FdMatrix::zeros(n, m);
52 let mut detrended = FdMatrix::zeros(n, m);
53 let mut coefficients = FdMatrix::zeros(n, 2);
54 let mut rss = vec![0.0; n];
55
56 for (i, &(intercept, slope, r)) in scalars.iter().enumerate() {
57 coefficients[(i, 0)] = intercept;
58 coefficients[(i, 1)] = slope;
59 rss[i] = r;
60 for j in 0..m {
61 let trend_val = intercept + slope * argvals[j];
62 trend[(i, j)] = trend_val;
63 detrended[(i, j)] = data[(i, j)] - trend_val;
64 }
65 }
66
67 TrendResult {
68 trend,
69 detrended,
70 method: Cow::Borrowed("linear"),
71 coefficients: Some(coefficients),
72 rss,
73 n_params: 2,
74 }
75}