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| {
499 arr[a]
500 .partial_cmp(&arr[b])
501 .expect("operation should succeed")
502 });
503 indices
504 }
505}
506
507#[derive(Debug, Clone)]
509#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
510pub struct TimeSeriesResampler {
511 method: ResamplingMethod,
512 target_frequency: Float,
513 fill_method: Option<InterpolationMethod>,
514}
515
516impl TimeSeriesResampler {
517 pub fn new(target_frequency: Float) -> Self {
519 Self {
520 method: ResamplingMethod::default(),
521 target_frequency,
522 fill_method: None,
523 }
524 }
525
526 pub fn with_method(mut self, method: ResamplingMethod) -> Self {
528 self.method = method;
529 self
530 }
531
532 pub fn with_fill_method(mut self, fill_method: InterpolationMethod) -> Self {
534 self.fill_method = Some(fill_method);
535 self
536 }
537
538 pub fn resample(
540 &self,
541 times: &Array1<Float>,
542 values: &Array1<Float>,
543 ) -> Result<(Array1<Float>, Array1<Float>)> {
544 if times.len() != values.len() {
545 return Err(SklearsError::InvalidInput(
546 "Times and values must have the same length".to_string(),
547 ));
548 }
549
550 if times.is_empty() {
551 return Ok((Array1::zeros(0), Array1::zeros(0)));
552 }
553
554 let min_time = times[0];
555 let max_time = times[times.len() - 1];
556
557 let num_points = ((max_time - min_time) / self.target_frequency).ceil() as usize + 1;
559 let mut target_times = Array1::zeros(num_points);
560 for i in 0..num_points {
561 target_times[i] = min_time + (i as Float) * self.target_frequency;
562 }
563
564 let resampled_values = self.aggregate_by_windows(times, values, &target_times)?;
566
567 Ok((target_times, resampled_values))
568 }
569
570 fn aggregate_by_windows(
572 &self,
573 times: &Array1<Float>,
574 values: &Array1<Float>,
575 target_times: &Array1<Float>,
576 ) -> Result<Array1<Float>> {
577 let mut result = Array1::zeros(target_times.len());
578 let half_window = self.target_frequency / 2.0;
579
580 for (i, &target_time) in target_times.iter().enumerate() {
581 let window_start = target_time - half_window;
582 let window_end = target_time + half_window;
583
584 let window_values: Vec<Float> = times
585 .iter()
586 .zip(values.iter())
587 .filter_map(|(&t, &v)| {
588 if t >= window_start && t < window_end {
589 Some(v)
590 } else {
591 None
592 }
593 })
594 .collect();
595
596 if window_values.is_empty() {
597 result[i] = if let Some(fill_method) = &self.fill_method {
599 self.interpolate_missing_value(times, values, target_time, fill_method)?
600 } else {
601 Float::NAN
602 };
603 } else {
604 result[i] = match self.method {
605 ResamplingMethod::Mean => {
606 window_values.iter().sum::<Float>() / window_values.len() as Float
607 }
608 ResamplingMethod::Sum => window_values.iter().sum(),
609 ResamplingMethod::Min => {
610 window_values.iter().fold(Float::INFINITY, |a, &b| a.min(b))
611 }
612 ResamplingMethod::Max => window_values
613 .iter()
614 .fold(Float::NEG_INFINITY, |a, &b| a.max(b)),
615 ResamplingMethod::First => window_values[0],
616 ResamplingMethod::Last => window_values[window_values.len() - 1],
617 ResamplingMethod::Median => self.calculate_median(&window_values),
618 ResamplingMethod::Count => window_values.len() as Float,
619 };
620 }
621 }
622
623 Ok(result)
624 }
625
626 fn interpolate_missing_value(
628 &self,
629 times: &Array1<Float>,
630 values: &Array1<Float>,
631 target_time: Float,
632 fill_method: &InterpolationMethod,
633 ) -> Result<Float> {
634 let interpolator = TimeSeriesInterpolator::new()
635 .with_method(*fill_method)
636 .with_extrapolation(true);
637
638 let target_times = Array1::from(vec![target_time]);
639 let interpolated = interpolator.interpolate(times, values, &target_times)?;
640 Ok(interpolated[0])
641 }
642
643 fn calculate_median(&self, values: &[Float]) -> Float {
645 if values.is_empty() {
646 return Float::NAN;
647 }
648
649 let mut sorted_values = values.to_vec();
650 sorted_values.sort_by(|a, b| a.partial_cmp(b).expect("operation should succeed"));
651
652 let len = sorted_values.len();
653 if len % 2 == 0 {
654 (sorted_values[len / 2 - 1] + sorted_values[len / 2]) / 2.0
655 } else {
656 sorted_values[len / 2]
657 }
658 }
659}
660
661#[derive(Debug, Clone)]
663#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
664pub struct MultiVariateTimeSeriesAligner {
665 interpolation_method: InterpolationMethod,
666 target_frequency: Option<Float>,
667 fill_method: InterpolationMethod,
668}
669
670impl Default for MultiVariateTimeSeriesAligner {
671 fn default() -> Self {
672 Self::new()
673 }
674}
675
676impl MultiVariateTimeSeriesAligner {
677 pub fn new() -> Self {
679 Self {
680 interpolation_method: InterpolationMethod::Linear,
681 target_frequency: None,
682 fill_method: InterpolationMethod::Linear,
683 }
684 }
685
686 pub fn with_interpolation_method(mut self, method: InterpolationMethod) -> Self {
688 self.interpolation_method = method;
689 self
690 }
691
692 pub fn with_target_frequency(mut self, frequency: Float) -> Self {
694 self.target_frequency = Some(frequency);
695 self
696 }
697
698 pub fn with_fill_method(mut self, method: InterpolationMethod) -> Self {
700 self.fill_method = method;
701 self
702 }
703
704 pub fn align_series(
706 &self,
707 series_data: &[(Array1<Float>, Array1<Float>)], ) -> Result<(Array1<Float>, Array2<Float>)> {
709 if series_data.is_empty() {
710 return Ok((Array1::zeros(0), Array2::zeros((0, 0))));
711 }
712
713 let mut min_time = Float::INFINITY;
715 let mut max_time = Float::NEG_INFINITY;
716
717 for (times, _) in series_data.iter() {
718 if !times.is_empty() {
719 min_time = min_time.min(times[0]);
720 max_time = max_time.max(times[times.len() - 1]);
721 }
722 }
723
724 if min_time == Float::INFINITY || max_time == Float::NEG_INFINITY {
725 return Err(SklearsError::InvalidInput(
726 "All time series are empty".to_string(),
727 ));
728 }
729
730 let common_times = if let Some(freq) = self.target_frequency {
732 let num_points = ((max_time - min_time) / freq).ceil() as usize + 1;
734 let mut times = Array1::zeros(num_points);
735 for i in 0..num_points {
736 times[i] = min_time + (i as Float) * freq;
737 }
738 times
739 } else {
740 let mut all_times: Vec<Float> = Vec::new();
742 for (times, _) in series_data.iter() {
743 all_times.extend(times.iter());
744 }
745 all_times.sort_by(|a, b| a.partial_cmp(b).expect("operation should succeed"));
746 all_times.dedup_by(|a, b| (*a - *b).abs() < 1e-10);
747 Array1::from(all_times)
748 };
749
750 let n_series = series_data.len();
752 let n_times = common_times.len();
753 let mut aligned_data = Array2::zeros((n_times, n_series));
754
755 let interpolator = TimeSeriesInterpolator::new()
756 .with_method(self.interpolation_method)
757 .with_extrapolation(true);
758
759 for (series_idx, (times, values)) in series_data.iter().enumerate() {
760 if !times.is_empty() && !values.is_empty() {
761 let interpolated = interpolator.interpolate(times, values, &common_times)?;
762 for (time_idx, &value) in interpolated.iter().enumerate() {
763 aligned_data[[time_idx, series_idx]] = value;
764 }
765 }
766 }
767
768 Ok((common_times, aligned_data))
769 }
770}
771
772#[allow(non_snake_case)]
773#[cfg(test)]
774mod tests {
775 use super::*;
776 use approx::assert_abs_diff_eq;
777 use scirs2_core::ndarray::Array1;
778
779 #[test]
780 fn test_linear_interpolation() -> Result<()> {
781 let times = Array1::from(vec![0.0, 1.0, 3.0, 4.0]);
782 let values = Array1::from(vec![0.0, 1.0, 3.0, 4.0]);
783 let target_times = Array1::from(vec![0.5, 2.0, 3.5]);
784
785 let interpolator = TimeSeriesInterpolator::new().with_method(InterpolationMethod::Linear);
786
787 let result = interpolator.interpolate(×, &values, &target_times)?;
788
789 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(())
794 }
795
796 #[test]
797 fn test_forward_fill_interpolation() -> Result<()> {
798 let times = Array1::from(vec![0.0, 2.0, 5.0]);
799 let values = Array1::from(vec![10.0, 20.0, 30.0]);
800 let target_times = Array1::from(vec![1.0, 3.0, 6.0]);
801
802 let interpolator = TimeSeriesInterpolator::new()
803 .with_method(InterpolationMethod::ForwardFill)
804 .with_extrapolation(true);
805
806 let result = interpolator.interpolate(×, &values, &target_times)?;
807
808 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(())
813 }
814
815 #[test]
816 fn test_nearest_neighbor_interpolation() -> Result<()> {
817 let times = Array1::from(vec![0.0, 2.0, 5.0]);
818 let values = Array1::from(vec![100.0, 200.0, 300.0]);
819 let target_times = Array1::from(vec![0.8, 2.1, 4.9]);
820
821 let interpolator =
822 TimeSeriesInterpolator::new().with_method(InterpolationMethod::NearestNeighbor);
823
824 let result = interpolator.interpolate(×, &values, &target_times)?;
825
826 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(())
831 }
832
833 #[test]
834 fn test_time_series_resampling() -> Result<()> {
835 let times = Array1::from(vec![0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]);
836 let values = Array1::from(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]);
837
838 let resampler = TimeSeriesResampler::new(1.0) .with_method(ResamplingMethod::Mean);
840
841 let (resampled_times, resampled_values) = resampler.resample(×, &values)?;
842
843 assert_eq!(resampled_times.len(), 4);
845 assert_abs_diff_eq!(resampled_times[0], 0.0, epsilon = 1e-10);
846 assert_abs_diff_eq!(resampled_times[3], 3.0, epsilon = 1e-10);
847
848 assert_eq!(resampled_values.len(), 4);
850
851 Ok(())
852 }
853
854 #[test]
855 fn test_multivariate_alignment() -> Result<()> {
856 let series1_times = Array1::from(vec![0.0, 1.0, 2.0]);
857 let series1_values = Array1::from(vec![10.0, 20.0, 30.0]);
858
859 let series2_times = Array1::from(vec![0.5, 1.5, 2.5]);
860 let series2_values = Array1::from(vec![15.0, 25.0, 35.0]);
861
862 let series_data = vec![
863 (series1_times, series1_values),
864 (series2_times, series2_values),
865 ];
866
867 let aligner = MultiVariateTimeSeriesAligner::new()
868 .with_interpolation_method(InterpolationMethod::Linear)
869 .with_target_frequency(0.5);
870
871 let (aligned_times, aligned_data) = aligner.align_series(&series_data)?;
872
873 assert!(aligned_times.len() > 3);
875 assert_eq!(aligned_data.dim().1, 2); assert_abs_diff_eq!(aligned_times[0], 0.0, epsilon = 1e-6);
879 assert_abs_diff_eq!(aligned_times[aligned_times.len() - 1], 2.5, epsilon = 1e-6);
880
881 Ok(())
882 }
883
884 #[test]
885 fn test_empty_input() -> Result<()> {
886 let times = Array1::zeros(0);
887 let values = Array1::zeros(0);
888 let target_times = Array1::from(vec![1.0, 2.0]);
889
890 let interpolator = TimeSeriesInterpolator::new();
891 let result = interpolator.interpolate(×, &values, &target_times)?;
892
893 assert_eq!(result.len(), 2);
894 assert_eq!(result[0], 0.0);
895 assert_eq!(result[1], 0.0);
896
897 Ok(())
898 }
899
900 #[test]
901 fn test_resampling_methods() -> Result<()> {
902 let times = Array1::from(vec![0.0, 0.2, 0.4, 0.6, 0.8, 1.0]);
903 let values = Array1::from(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
904
905 let methods = vec![
907 ResamplingMethod::Mean,
908 ResamplingMethod::Sum,
909 ResamplingMethod::Min,
910 ResamplingMethod::Max,
911 ResamplingMethod::First,
912 ResamplingMethod::Last,
913 ResamplingMethod::Count,
914 ];
915
916 for method in methods {
917 let resampler = TimeSeriesResampler::new(0.5).with_method(method);
918 let (resampled_times, resampled_values) = resampler.resample(×, &values)?;
919
920 assert!(resampled_times.len() > 0);
921 assert_eq!(resampled_times.len(), resampled_values.len());
922
923 assert!(resampled_values.iter().all(|v| v.is_finite()));
925 }
926
927 Ok(())
928 }
929}