fdars_core/irreg_fdata/
mod.rs1pub mod face;
24pub mod kernels;
25pub mod smoothing;
26
27#[cfg(test)]
28mod tests;
29
30pub use face::{face_covariance, face_trajectory, mface_covariance, MfaceCovResult};
32pub use kernels::{mean_irreg, KernelType};
33pub use smoothing::{cov_irreg, integrate_irreg, metric_lp_irreg, norm_lp_irreg, to_regular_grid};
34
35#[derive(Clone, Debug)]
40pub struct IrregFdata {
41 pub offsets: Vec<usize>,
44 pub argvals: Vec<f64>,
46 pub values: Vec<f64>,
48 pub rangeval: [f64; 2],
50}
51
52impl IrregFdata {
53 pub fn from_lists(argvals_list: &[Vec<f64>], values_list: &[Vec<f64>]) -> Self {
62 let n = argvals_list.len();
63 assert_eq!(
64 n,
65 values_list.len(),
66 "argvals_list and values_list must have same length"
67 );
68
69 let mut offsets = Vec::with_capacity(n + 1);
70 offsets.push(0);
71
72 let total_points: usize = argvals_list.iter().map(std::vec::Vec::len).sum();
73 let mut argvals = Vec::with_capacity(total_points);
74 let mut values = Vec::with_capacity(total_points);
75
76 let mut range_min = f64::INFINITY;
77 let mut range_max = f64::NEG_INFINITY;
78
79 for i in 0..n {
80 assert_eq!(
81 argvals_list[i].len(),
82 values_list[i].len(),
83 "Observation {i} has mismatched argvals/values lengths"
84 );
85
86 argvals.extend_from_slice(&argvals_list[i]);
87 values.extend_from_slice(&values_list[i]);
88 offsets.push(argvals.len());
89
90 if let (Some(&min), Some(&max)) = (argvals_list[i].first(), argvals_list[i].last()) {
91 range_min = range_min.min(min);
92 range_max = range_max.max(max);
93 }
94 }
95
96 IrregFdata {
97 offsets,
98 argvals,
99 values,
100 rangeval: [range_min, range_max],
101 }
102 }
103
104 pub fn from_flat(
115 offsets: Vec<usize>,
116 argvals: Vec<f64>,
117 values: Vec<f64>,
118 rangeval: [f64; 2],
119 ) -> Result<Self, crate::FdarError> {
120 let last = offsets.last().copied().unwrap_or(0);
121 if offsets.is_empty()
122 || argvals.len() != values.len()
123 || last != argvals.len()
124 || offsets.windows(2).any(|w| w[0] > w[1])
125 {
126 return Err(crate::FdarError::InvalidDimension {
127 parameter: "offsets/argvals/values",
128 expected: "non-empty offsets, argvals.len() == values.len(), monotone offsets"
129 .to_string(),
130 actual: format!(
131 "offsets.len()={}, argvals.len()={}, values.len()={}",
132 offsets.len(),
133 argvals.len(),
134 values.len()
135 ),
136 });
137 }
138 Ok(IrregFdata {
139 offsets,
140 argvals,
141 values,
142 rangeval,
143 })
144 }
145
146 #[inline]
148 pub fn n_obs(&self) -> usize {
149 self.offsets.len().saturating_sub(1)
150 }
151
152 #[inline]
154 pub fn n_points(&self, i: usize) -> usize {
155 self.offsets[i + 1] - self.offsets[i]
156 }
157
158 #[inline]
160 pub fn get_obs(&self, i: usize) -> (&[f64], &[f64]) {
161 let start = self.offsets[i];
162 let end = self.offsets[i + 1];
163 (&self.argvals[start..end], &self.values[start..end])
164 }
165
166 #[inline]
168 pub fn total_points(&self) -> usize {
169 self.argvals.len()
170 }
171
172 pub fn obs_counts(&self) -> Vec<usize> {
174 (0..self.n_obs()).map(|i| self.n_points(i)).collect()
175 }
176
177 pub fn min_obs(&self) -> usize {
179 (0..self.n_obs())
180 .map(|i| self.n_points(i))
181 .min()
182 .unwrap_or(0)
183 }
184
185 pub fn max_obs(&self) -> usize {
187 (0..self.n_obs())
188 .map(|i| self.n_points(i))
189 .max()
190 .unwrap_or(0)
191 }
192}
193
194pub(super) fn linear_interp(argvals: &[f64], values: &[f64], t: f64) -> f64 {
196 if t <= argvals[0] {
197 return values[0];
198 }
199 if t >= argvals[argvals.len() - 1] {
200 return values[values.len() - 1];
201 }
202
203 let idx = argvals
205 .iter()
206 .position(|&x| x > t)
207 .expect("element must exist in collection");
208 let t0 = argvals[idx - 1];
209 let t1 = argvals[idx];
210 let x0 = values[idx - 1];
211 let x1 = values[idx];
212
213 x0 + (x1 - x0) * (t - t0) / (t1 - t0)
215}