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