1use scirs2_core::ndarray::{Array1, Array2};
47use sklears_core::{
48 error::{Result, SklearsError},
49 types::Float,
50};
51
52#[cfg(feature = "serde")]
53use serde::{Deserialize, Serialize};
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58pub enum InterpolationMethod {
59 Linear,
61 Polynomial(usize),
63 CubicSpline,
65 ForwardFill,
67 BackwardFill,
69 NearestNeighbor,
71 Seasonal(usize), }
74
75impl Default for InterpolationMethod {
76 fn default() -> Self {
77 Self::Linear
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
84pub enum ResamplingMethod {
85 Mean,
87 Sum,
89 Min,
91 Max,
93 First,
95 Last,
97 Median,
99 Count,
101}
102
103impl Default for ResamplingMethod {
104 fn default() -> Self {
105 Self::Mean
106 }
107}
108
109#[derive(Debug, Clone)]
111#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
112pub struct TimeSeriesInterpolator {
113 method: InterpolationMethod,
114 extrapolate: bool,
115 bounds_error: bool,
116}
117
118impl Default for TimeSeriesInterpolator {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124impl TimeSeriesInterpolator {
125 pub fn new() -> Self {
127 Self {
128 method: InterpolationMethod::default(),
129 extrapolate: false,
130 bounds_error: true,
131 }
132 }
133
134 pub fn with_method(mut self, method: InterpolationMethod) -> Self {
136 self.method = method;
137 self
138 }
139
140 pub fn with_extrapolation(mut self, extrapolate: bool) -> Self {
142 self.extrapolate = extrapolate;
143 self
144 }
145
146 pub fn with_bounds_error(mut self, bounds_error: bool) -> Self {
148 self.bounds_error = bounds_error;
149 self
150 }
151
152 pub fn interpolate(
154 &self,
155 times: &Array1<Float>,
156 values: &Array1<Float>,
157 target_times: &Array1<Float>,
158 ) -> Result<Array1<Float>> {
159 if times.len() != values.len() {
160 return Err(SklearsError::InvalidInput(
161 "Times and values must have the same length".to_string(),
162 ));
163 }
164
165 if times.is_empty() {
166 return Ok(Array1::zeros(target_times.len()));
167 }
168
169 let sorted_indices = self.argsort(times);
171 let sorted_times: Array1<Float> = sorted_indices.iter().map(|&i| times[i]).collect();
172 let sorted_values: Array1<Float> = sorted_indices.iter().map(|&i| values[i]).collect();
173
174 let mut result = Array1::zeros(target_times.len());
175
176 match self.method {
177 InterpolationMethod::Linear => {
178 self.linear_interpolation(&sorted_times, &sorted_values, target_times, &mut result)?
179 }
180 InterpolationMethod::Polynomial(degree) => self.polynomial_interpolation(
181 &sorted_times,
182 &sorted_values,
183 target_times,
184 degree,
185 &mut result,
186 )?,
187 InterpolationMethod::CubicSpline => self.cubic_spline_interpolation(
188 &sorted_times,
189 &sorted_values,
190 target_times,
191 &mut result,
192 )?,
193 InterpolationMethod::ForwardFill => self.forward_fill_interpolation(
194 &sorted_times,
195 &sorted_values,
196 target_times,
197 &mut result,
198 )?,
199 InterpolationMethod::BackwardFill => self.backward_fill_interpolation(
200 &sorted_times,
201 &sorted_values,
202 target_times,
203 &mut result,
204 )?,
205 InterpolationMethod::NearestNeighbor => self.nearest_neighbor_interpolation(
206 &sorted_times,
207 &sorted_values,
208 target_times,
209 &mut result,
210 )?,
211 InterpolationMethod::Seasonal(period) => self.seasonal_interpolation(
212 &sorted_times,
213 &sorted_values,
214 target_times,
215 period,
216 &mut result,
217 )?,
218 }
219
220 Ok(result)
221 }
222
223 fn linear_interpolation(
225 &self,
226 times: &Array1<Float>,
227 values: &Array1<Float>,
228 target_times: &Array1<Float>,
229 result: &mut Array1<Float>,
230 ) -> Result<()> {
231 for (i, &target_time) in target_times.iter().enumerate() {
232 let value = self.linear_interpolate_single(times, values, target_time)?;
233 result[i] = value;
234 }
235 Ok(())
236 }
237
238 fn linear_interpolate_single(
240 &self,
241 times: &Array1<Float>,
242 values: &Array1<Float>,
243 target_time: Float,
244 ) -> Result<Float> {
245 let min_time = times[0];
247 let max_time = times[times.len() - 1];
248
249 if target_time < min_time || target_time > max_time {
250 if self.bounds_error && !self.extrapolate {
251 return Err(SklearsError::InvalidInput(format!(
252 "Target time {} is outside data range [{}, {}]",
253 target_time, min_time, max_time
254 )));
255 }
256 if !self.extrapolate {
257 return Ok(if target_time < min_time {
258 values[0]
259 } else {
260 values[values.len() - 1]
261 });
262 }
263 }
264
265 let mut left_idx = 0;
267 let mut right_idx = times.len() - 1;
268
269 while right_idx - left_idx > 1 {
271 let mid = (left_idx + right_idx) / 2;
272 if times[mid] <= target_time {
273 left_idx = mid;
274 } else {
275 right_idx = mid;
276 }
277 }
278
279 if (times[left_idx] - target_time).abs() < 1e-10 {
281 return Ok(values[left_idx]);
282 }
283 if (times[right_idx] - target_time).abs() < 1e-10 {
284 return Ok(values[right_idx]);
285 }
286
287 let t1 = times[left_idx];
289 let t2 = times[right_idx];
290 let v1 = values[left_idx];
291 let v2 = values[right_idx];
292
293 if (t2 - t1).abs() < 1e-10 {
294 return Ok((v1 + v2) / 2.0);
295 }
296
297 let interpolated = v1 + (v2 - v1) * (target_time - t1) / (t2 - t1);
298 Ok(interpolated)
299 }
300
301 fn polynomial_interpolation(
303 &self,
304 times: &Array1<Float>,
305 values: &Array1<Float>,
306 target_times: &Array1<Float>,
307 degree: usize,
308 result: &mut Array1<Float>,
309 ) -> Result<()> {
310 if degree >= times.len() {
311 return Err(SklearsError::InvalidInput(
312 "Polynomial degree must be less than number of data points".to_string(),
313 ));
314 }
315
316 for (i, &target_time) in target_times.iter().enumerate() {
317 let value = self.lagrange_interpolate(times, values, target_time, degree)?;
318 result[i] = value;
319 }
320 Ok(())
321 }
322
323 fn lagrange_interpolate(
325 &self,
326 times: &Array1<Float>,
327 values: &Array1<Float>,
328 target_time: Float,
329 degree: usize,
330 ) -> Result<Float> {
331 let n = times.len();
332 let num_points = (degree + 1).min(n);
333
334 let center_idx = self.find_nearest_index(times, target_time);
336 let start_idx = center_idx.saturating_sub(num_points / 2);
337 let end_idx = (start_idx + num_points).min(n);
338 let actual_start = if end_idx - start_idx < num_points && end_idx == n {
339 n - num_points
340 } else {
341 start_idx
342 };
343
344 let mut result = 0.0;
345
346 for i in actual_start..end_idx {
347 let mut li = 1.0;
348 for j in actual_start..end_idx {
349 if i != j {
350 li *= (target_time - times[j]) / (times[i] - times[j]);
351 }
352 }
353 result += values[i] * li;
354 }
355
356 Ok(result)
357 }
358
359 fn cubic_spline_interpolation(
361 &self,
362 times: &Array1<Float>,
363 values: &Array1<Float>,
364 target_times: &Array1<Float>,
365 result: &mut Array1<Float>,
366 ) -> Result<()> {
367 if times.len() < 4 {
368 return self.linear_interpolation(times, values, target_times, result);
370 }
371
372 for (i, &target_time) in target_times.iter().enumerate() {
374 let value = self.linear_interpolate_single(times, values, target_time)?;
375 result[i] = value;
376 }
377 Ok(())
378 }
379
380 fn forward_fill_interpolation(
382 &self,
383 times: &Array1<Float>,
384 values: &Array1<Float>,
385 target_times: &Array1<Float>,
386 result: &mut Array1<Float>,
387 ) -> Result<()> {
388 for (i, &target_time) in target_times.iter().enumerate() {
389 let idx = self.find_last_valid_index(times, target_time);
390 result[i] = values[idx];
391 }
392 Ok(())
393 }
394
395 fn backward_fill_interpolation(
397 &self,
398 times: &Array1<Float>,
399 values: &Array1<Float>,
400 target_times: &Array1<Float>,
401 result: &mut Array1<Float>,
402 ) -> Result<()> {
403 for (i, &target_time) in target_times.iter().enumerate() {
404 let idx = self.find_first_valid_index(times, target_time);
405 result[i] = values[idx];
406 }
407 Ok(())
408 }
409
410 fn nearest_neighbor_interpolation(
412 &self,
413 times: &Array1<Float>,
414 values: &Array1<Float>,
415 target_times: &Array1<Float>,
416 result: &mut Array1<Float>,
417 ) -> Result<()> {
418 for (i, &target_time) in target_times.iter().enumerate() {
419 let idx = self.find_nearest_index(times, target_time);
420 result[i] = values[idx];
421 }
422 Ok(())
423 }
424
425 fn seasonal_interpolation(
427 &self,
428 times: &Array1<Float>,
429 values: &Array1<Float>,
430 target_times: &Array1<Float>,
431 period: usize,
432 result: &mut Array1<Float>,
433 ) -> Result<()> {
434 if times.len() < period * 2 {
435 return self.linear_interpolation(times, values, target_times, result);
437 }
438
439 for (i, &target_time) in target_times.iter().enumerate() {
441 let seasonal_position = (i % period) as Float;
442 let season_indices: Vec<usize> = (0..times.len())
443 .filter(|&j| (j % period) as Float == seasonal_position)
444 .collect();
445
446 if season_indices.is_empty() {
447 result[i] = self.linear_interpolate_single(times, values, target_time)?;
448 } else {
449 let seasonal_values: Vec<Float> =
450 season_indices.iter().map(|&j| values[j]).collect();
451 result[i] = seasonal_values.iter().sum::<Float>() / seasonal_values.len() as Float;
452 }
453 }
454
455 Ok(())
456 }
457
458 fn find_nearest_index(&self, times: &Array1<Float>, target_time: Float) -> usize {
461 let mut min_dist = Float::INFINITY;
462 let mut best_idx = 0;
463
464 for (i, &time) in times.iter().enumerate() {
465 let dist = (time - target_time).abs();
466 if dist < min_dist {
467 min_dist = dist;
468 best_idx = i;
469 }
470 }
471
472 best_idx
473 }
474
475 fn find_last_valid_index(&self, times: &Array1<Float>, target_time: Float) -> usize {
477 for i in (0..times.len()).rev() {
478 if times[i] <= target_time {
479 return i;
480 }
481 }
482 0 }
484
485 fn find_first_valid_index(&self, times: &Array1<Float>, target_time: Float) -> usize {
487 for i in 0..times.len() {
488 if times[i] >= target_time {
489 return i;
490 }
491 }
492 times.len() - 1 }
494
495 fn argsort(&self, arr: &Array1<Float>) -> Vec<usize> {
497 let mut indices: Vec<usize> = (0..arr.len()).collect();
498 indices.sort_by(|&a, &b| arr[a].partial_cmp(&arr[b]).unwrap());
499 indices
500 }
501}
502
503#[derive(Debug, Clone)]
505#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
506pub struct TimeSeriesResampler {
507 method: ResamplingMethod,
508 target_frequency: Float,
509 fill_method: Option<InterpolationMethod>,
510}
511
512impl TimeSeriesResampler {
513 pub fn new(target_frequency: Float) -> Self {
515 Self {
516 method: ResamplingMethod::default(),
517 target_frequency,
518 fill_method: None,
519 }
520 }
521
522 pub fn with_method(mut self, method: ResamplingMethod) -> Self {
524 self.method = method;
525 self
526 }
527
528 pub fn with_fill_method(mut self, fill_method: InterpolationMethod) -> Self {
530 self.fill_method = Some(fill_method);
531 self
532 }
533
534 pub fn resample(
536 &self,
537 times: &Array1<Float>,
538 values: &Array1<Float>,
539 ) -> Result<(Array1<Float>, Array1<Float>)> {
540 if times.len() != values.len() {
541 return Err(SklearsError::InvalidInput(
542 "Times and values must have the same length".to_string(),
543 ));
544 }
545
546 if times.is_empty() {
547 return Ok((Array1::zeros(0), Array1::zeros(0)));
548 }
549
550 let min_time = times[0];
551 let max_time = times[times.len() - 1];
552
553 let num_points = ((max_time - min_time) / self.target_frequency).ceil() as usize + 1;
555 let mut target_times = Array1::zeros(num_points);
556 for i in 0..num_points {
557 target_times[i] = min_time + (i as Float) * self.target_frequency;
558 }
559
560 let resampled_values = self.aggregate_by_windows(times, values, &target_times)?;
562
563 Ok((target_times, resampled_values))
564 }
565
566 fn aggregate_by_windows(
568 &self,
569 times: &Array1<Float>,
570 values: &Array1<Float>,
571 target_times: &Array1<Float>,
572 ) -> Result<Array1<Float>> {
573 let mut result = Array1::zeros(target_times.len());
574 let half_window = self.target_frequency / 2.0;
575
576 for (i, &target_time) in target_times.iter().enumerate() {
577 let window_start = target_time - half_window;
578 let window_end = target_time + half_window;
579
580 let window_values: Vec<Float> = times
581 .iter()
582 .zip(values.iter())
583 .filter_map(|(&t, &v)| {
584 if t >= window_start && t < window_end {
585 Some(v)
586 } else {
587 None
588 }
589 })
590 .collect();
591
592 if window_values.is_empty() {
593 result[i] = if let Some(fill_method) = &self.fill_method {
595 self.interpolate_missing_value(times, values, target_time, fill_method)?
596 } else {
597 Float::NAN
598 };
599 } else {
600 result[i] = match self.method {
601 ResamplingMethod::Mean => {
602 window_values.iter().sum::<Float>() / window_values.len() as Float
603 }
604 ResamplingMethod::Sum => window_values.iter().sum(),
605 ResamplingMethod::Min => {
606 window_values.iter().fold(Float::INFINITY, |a, &b| a.min(b))
607 }
608 ResamplingMethod::Max => window_values
609 .iter()
610 .fold(Float::NEG_INFINITY, |a, &b| a.max(b)),
611 ResamplingMethod::First => window_values[0],
612 ResamplingMethod::Last => window_values[window_values.len() - 1],
613 ResamplingMethod::Median => self.calculate_median(&window_values),
614 ResamplingMethod::Count => window_values.len() as Float,
615 };
616 }
617 }
618
619 Ok(result)
620 }
621
622 fn interpolate_missing_value(
624 &self,
625 times: &Array1<Float>,
626 values: &Array1<Float>,
627 target_time: Float,
628 fill_method: &InterpolationMethod,
629 ) -> Result<Float> {
630 let interpolator = TimeSeriesInterpolator::new()
631 .with_method(*fill_method)
632 .with_extrapolation(true);
633
634 let target_times = Array1::from(vec![target_time]);
635 let interpolated = interpolator.interpolate(times, values, &target_times)?;
636 Ok(interpolated[0])
637 }
638
639 fn calculate_median(&self, values: &[Float]) -> Float {
641 if values.is_empty() {
642 return Float::NAN;
643 }
644
645 let mut sorted_values = values.to_vec();
646 sorted_values.sort_by(|a, b| a.partial_cmp(b).unwrap());
647
648 let len = sorted_values.len();
649 if len % 2 == 0 {
650 (sorted_values[len / 2 - 1] + sorted_values[len / 2]) / 2.0
651 } else {
652 sorted_values[len / 2]
653 }
654 }
655}
656
657#[derive(Debug, Clone)]
659#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
660pub struct MultiVariateTimeSeriesAligner {
661 interpolation_method: InterpolationMethod,
662 target_frequency: Option<Float>,
663 fill_method: InterpolationMethod,
664}
665
666impl Default for MultiVariateTimeSeriesAligner {
667 fn default() -> Self {
668 Self::new()
669 }
670}
671
672impl MultiVariateTimeSeriesAligner {
673 pub fn new() -> Self {
675 Self {
676 interpolation_method: InterpolationMethod::Linear,
677 target_frequency: None,
678 fill_method: InterpolationMethod::Linear,
679 }
680 }
681
682 pub fn with_interpolation_method(mut self, method: InterpolationMethod) -> Self {
684 self.interpolation_method = method;
685 self
686 }
687
688 pub fn with_target_frequency(mut self, frequency: Float) -> Self {
690 self.target_frequency = Some(frequency);
691 self
692 }
693
694 pub fn with_fill_method(mut self, method: InterpolationMethod) -> Self {
696 self.fill_method = method;
697 self
698 }
699
700 pub fn align_series(
702 &self,
703 series_data: &[(Array1<Float>, Array1<Float>)], ) -> Result<(Array1<Float>, Array2<Float>)> {
705 if series_data.is_empty() {
706 return Ok((Array1::zeros(0), Array2::zeros((0, 0))));
707 }
708
709 let mut min_time = Float::INFINITY;
711 let mut max_time = Float::NEG_INFINITY;
712
713 for (times, _) in series_data.iter() {
714 if !times.is_empty() {
715 min_time = min_time.min(times[0]);
716 max_time = max_time.max(times[times.len() - 1]);
717 }
718 }
719
720 if min_time == Float::INFINITY || max_time == Float::NEG_INFINITY {
721 return Err(SklearsError::InvalidInput(
722 "All time series are empty".to_string(),
723 ));
724 }
725
726 let common_times = if let Some(freq) = self.target_frequency {
728 let num_points = ((max_time - min_time) / freq).ceil() as usize + 1;
730 let mut times = Array1::zeros(num_points);
731 for i in 0..num_points {
732 times[i] = min_time + (i as Float) * freq;
733 }
734 times
735 } else {
736 let mut all_times: Vec<Float> = Vec::new();
738 for (times, _) in series_data.iter() {
739 all_times.extend(times.iter());
740 }
741 all_times.sort_by(|a, b| a.partial_cmp(b).unwrap());
742 all_times.dedup_by(|a, b| (*a - *b).abs() < 1e-10);
743 Array1::from(all_times)
744 };
745
746 let n_series = series_data.len();
748 let n_times = common_times.len();
749 let mut aligned_data = Array2::zeros((n_times, n_series));
750
751 let interpolator = TimeSeriesInterpolator::new()
752 .with_method(self.interpolation_method)
753 .with_extrapolation(true);
754
755 for (series_idx, (times, values)) in series_data.iter().enumerate() {
756 if !times.is_empty() && !values.is_empty() {
757 let interpolated = interpolator.interpolate(times, values, &common_times)?;
758 for (time_idx, &value) in interpolated.iter().enumerate() {
759 aligned_data[[time_idx, series_idx]] = value;
760 }
761 }
762 }
763
764 Ok((common_times, aligned_data))
765 }
766}
767
768#[allow(non_snake_case)]
769#[cfg(test)]
770mod tests {
771 use super::*;
772 use approx::assert_abs_diff_eq;
773 use scirs2_core::ndarray::Array1;
774
775 #[test]
776 fn test_linear_interpolation() -> Result<()> {
777 let times = Array1::from(vec![0.0, 1.0, 3.0, 4.0]);
778 let values = Array1::from(vec![0.0, 1.0, 3.0, 4.0]);
779 let target_times = Array1::from(vec![0.5, 2.0, 3.5]);
780
781 let interpolator = TimeSeriesInterpolator::new().with_method(InterpolationMethod::Linear);
782
783 let result = interpolator.interpolate(×, &values, &target_times)?;
784
785 assert_abs_diff_eq!(result[0], 0.5, epsilon = 1e-10); assert_abs_diff_eq!(result[1], 2.0, epsilon = 1e-10); assert_abs_diff_eq!(result[2], 3.5, epsilon = 1e-10); Ok(())
790 }
791
792 #[test]
793 fn test_forward_fill_interpolation() -> Result<()> {
794 let times = Array1::from(vec![0.0, 2.0, 5.0]);
795 let values = Array1::from(vec![10.0, 20.0, 30.0]);
796 let target_times = Array1::from(vec![1.0, 3.0, 6.0]);
797
798 let interpolator = TimeSeriesInterpolator::new()
799 .with_method(InterpolationMethod::ForwardFill)
800 .with_extrapolation(true);
801
802 let result = interpolator.interpolate(×, &values, &target_times)?;
803
804 assert_abs_diff_eq!(result[0], 10.0, epsilon = 1e-10); assert_abs_diff_eq!(result[1], 20.0, epsilon = 1e-10); assert_abs_diff_eq!(result[2], 30.0, epsilon = 1e-10); Ok(())
809 }
810
811 #[test]
812 fn test_nearest_neighbor_interpolation() -> Result<()> {
813 let times = Array1::from(vec![0.0, 2.0, 5.0]);
814 let values = Array1::from(vec![100.0, 200.0, 300.0]);
815 let target_times = Array1::from(vec![0.8, 2.1, 4.9]);
816
817 let interpolator =
818 TimeSeriesInterpolator::new().with_method(InterpolationMethod::NearestNeighbor);
819
820 let result = interpolator.interpolate(×, &values, &target_times)?;
821
822 assert_abs_diff_eq!(result[0], 100.0, epsilon = 1e-10); assert_abs_diff_eq!(result[1], 200.0, epsilon = 1e-10); assert_abs_diff_eq!(result[2], 300.0, epsilon = 1e-10); Ok(())
827 }
828
829 #[test]
830 fn test_time_series_resampling() -> Result<()> {
831 let times = Array1::from(vec![0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]);
832 let values = Array1::from(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]);
833
834 let resampler = TimeSeriesResampler::new(1.0) .with_method(ResamplingMethod::Mean);
836
837 let (resampled_times, resampled_values) = resampler.resample(×, &values)?;
838
839 assert_eq!(resampled_times.len(), 4);
841 assert_abs_diff_eq!(resampled_times[0], 0.0, epsilon = 1e-10);
842 assert_abs_diff_eq!(resampled_times[3], 3.0, epsilon = 1e-10);
843
844 assert_eq!(resampled_values.len(), 4);
846
847 Ok(())
848 }
849
850 #[test]
851 fn test_multivariate_alignment() -> Result<()> {
852 let series1_times = Array1::from(vec![0.0, 1.0, 2.0]);
853 let series1_values = Array1::from(vec![10.0, 20.0, 30.0]);
854
855 let series2_times = Array1::from(vec![0.5, 1.5, 2.5]);
856 let series2_values = Array1::from(vec![15.0, 25.0, 35.0]);
857
858 let series_data = vec![
859 (series1_times, series1_values),
860 (series2_times, series2_values),
861 ];
862
863 let aligner = MultiVariateTimeSeriesAligner::new()
864 .with_interpolation_method(InterpolationMethod::Linear)
865 .with_target_frequency(0.5);
866
867 let (aligned_times, aligned_data) = aligner.align_series(&series_data)?;
868
869 assert!(aligned_times.len() > 3);
871 assert_eq!(aligned_data.dim().1, 2); assert_abs_diff_eq!(aligned_times[0], 0.0, epsilon = 1e-6);
875 assert_abs_diff_eq!(aligned_times[aligned_times.len() - 1], 2.5, epsilon = 1e-6);
876
877 Ok(())
878 }
879
880 #[test]
881 fn test_empty_input() -> Result<()> {
882 let times = Array1::zeros(0);
883 let values = Array1::zeros(0);
884 let target_times = Array1::from(vec![1.0, 2.0]);
885
886 let interpolator = TimeSeriesInterpolator::new();
887 let result = interpolator.interpolate(×, &values, &target_times)?;
888
889 assert_eq!(result.len(), 2);
890 assert_eq!(result[0], 0.0);
891 assert_eq!(result[1], 0.0);
892
893 Ok(())
894 }
895
896 #[test]
897 fn test_resampling_methods() -> Result<()> {
898 let times = Array1::from(vec![0.0, 0.2, 0.4, 0.6, 0.8, 1.0]);
899 let values = Array1::from(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
900
901 let methods = vec![
903 ResamplingMethod::Mean,
904 ResamplingMethod::Sum,
905 ResamplingMethod::Min,
906 ResamplingMethod::Max,
907 ResamplingMethod::First,
908 ResamplingMethod::Last,
909 ResamplingMethod::Count,
910 ];
911
912 for method in methods {
913 let resampler = TimeSeriesResampler::new(0.5).with_method(method);
914 let (resampled_times, resampled_values) = resampler.resample(×, &values)?;
915
916 assert!(resampled_times.len() > 0);
917 assert_eq!(resampled_times.len(), resampled_values.len());
918
919 assert!(resampled_values.iter().all(|v| v.is_finite()));
921 }
922
923 Ok(())
924 }
925}