fraug/augmenters/
time_warp.rs

1use super::base::Augmenter;
2use rand::{Rng, rng};
3use tracing:: {info, info_span};
4
5/// Augmenter that applies random time warping to the dataset
6/// This augmenter randomly selects a window of the time series, specified by the `window_size` argument and applies a speed change to it.
7/// The speed change is defined by the `speed_ratio_range` argument, which specifies the minimum and maximum speed ratio.
8/// The speed ratio is a multiplier that affects how fast or slow the selected window is stretched or compressed.
9/// If the window size is 0 or larger than the time series length, the entire series is warped.
10pub struct RandomTimeWarpAugmenter {
11    pub name: String,
12    /// Length of the window to warp - a window of this size will be selected randomly for every time series in the dataset
13    pub window_size: usize,
14    /// Range for random speed ratio: [min, max]
15    pub speed_ratio_range: (f64, f64),
16    p: f64,
17}
18
19impl RandomTimeWarpAugmenter {
20    /// Create a new augmenter with given window size.
21    /// `speed_ratio_range` defines the min and max speed change (e.g. (0.5, 2.0)).
22    pub fn new(window_size: usize, speed_ratio_range: (f64, f64)) -> Self {
23        RandomTimeWarpAugmenter {
24            name: "RandomTimeWarpAugmenter".to_string(),
25            window_size,
26            speed_ratio_range,
27            p: 1.0,
28        }
29    }
30
31    fn warp_series(series: &[f64],speed_ratio_range: (f64, f64),rng: &mut impl Rng)-> Vec<f64> {
32        let len = series.len();
33        if len < 2 { return series.to_vec(); }
34
35        // random number between the min anfd max speeed is picked to warp
36        let warp_ratio = rng.random_range(speed_ratio_range.0..=speed_ratio_range.1);
37
38        let times: Vec<f64> = (0..len).map(|i| (i as f64) / warp_ratio).collect();
39
40        times.into_iter().map(|t| {
41            let t = t.clamp(0.0, (len - 1) as f64);
42            let lo = t.floor() as usize;
43            let hi = t.ceil()  as usize;
44            if lo == hi {
45                series[lo]
46            } else {
47                let w = t - lo as f64;
48                series[lo] * (1.0 - w) + series[hi] * w
49            }
50            }).collect()
51        }
52}
53
54impl Augmenter for RandomTimeWarpAugmenter {
55    fn augment_one(&self, x: &[f64]) -> Vec<f64> {
56        let span = info_span!("", step = "augment_one");
57        let _enter = span.enter();
58        let mut rng = rng();
59        let mut series = x.to_vec();
60        let len = series.len();
61
62        let (window_start, window_end) = if self.window_size == 0 || self.window_size >= len {
63            (0, len-1)
64        } else {
65            let start_index = rng.random_range(0..len - self.window_size);
66            (start_index, start_index + self.window_size)
67        };
68        info!("window selected from : {:?} to {:?} ", window_start, window_end);
69        let warped_series = Self::warp_series(
70            &series[window_start..=window_end],
71            self.speed_ratio_range,
72            &mut rng,
73        );
74        info!("Warped series: {:?}", warped_series);
75        series[window_start..=window_end].copy_from_slice(&warped_series);
76
77        series
78    }
79
80    fn get_probability(&self) -> f64 {
81        self.p
82    }
83
84    fn set_probability(&mut self, probability: f64) {
85        self.p = probability;
86    }
87
88    fn get_name(&self) -> String{
89        self.name.clone()
90    }
91}