oxits 0.1.0

Time series classification and transformation library for Rust
Documentation
/// Extract sliding windows of length `window_size` from `ts`, stepping by `window_step`.
///
/// Returns a vector of slices (as `Vec<f64>`) of length `window_size`.
/// This is equivalent to numpy's sliding_window_view with step.
pub fn windowed_view(ts: &[f64], window_size: usize, window_step: usize) -> Vec<Vec<f64>> {
    assert!(window_size > 0, "window_size must be positive");
    assert!(window_step > 0, "window_step must be positive");
    assert!(
        window_size <= ts.len(),
        "window_size ({window_size}) must not exceed time series length ({})",
        ts.len()
    );

    (0..=ts.len() - window_size)
        .step_by(window_step)
        .map(|i| ts[i..i + window_size].to_vec())
        .collect()
}

/// Extract sliding windows and return references to the original data.
pub fn windowed_view_refs(ts: &[f64], window_size: usize, window_step: usize) -> Vec<&[f64]> {
    assert!(window_size > 0, "window_size must be positive");
    assert!(window_step > 0, "window_step must be positive");
    assert!(
        window_size <= ts.len(),
        "window_size ({window_size}) must not exceed time series length ({})",
        ts.len()
    );

    (0..=ts.len() - window_size)
        .step_by(window_step)
        .map(|i| &ts[i..i + window_size])
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_basic() {
        let ts = vec![1.0, 2.0, 3.0, 4.0, 5.0];
        let windows = windowed_view(&ts, 3, 1);
        assert_eq!(
            windows,
            vec![
                vec![1.0, 2.0, 3.0],
                vec![2.0, 3.0, 4.0],
                vec![3.0, 4.0, 5.0],
            ]
        );
    }

    #[test]
    fn test_step_2() {
        let ts = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
        let windows = windowed_view(&ts, 3, 2);
        assert_eq!(windows, vec![vec![1.0, 2.0, 3.0], vec![3.0, 4.0, 5.0],]);
    }

    #[test]
    fn test_full_window() {
        let ts = vec![1.0, 2.0, 3.0];
        let windows = windowed_view(&ts, 3, 1);
        assert_eq!(windows, vec![vec![1.0, 2.0, 3.0]]);
    }

    #[test]
    fn test_refs() {
        let ts = vec![1.0, 2.0, 3.0, 4.0];
        let refs = windowed_view_refs(&ts, 2, 1);
        assert_eq!(refs.len(), 3);
        assert_eq!(refs[0], &[1.0, 2.0]);
        assert_eq!(refs[2], &[3.0, 4.0]);
    }

    #[test]
    #[should_panic(expected = "window_size must be positive")]
    fn test_zero_window() {
        windowed_view(&[1.0], 0, 1);
    }

    #[test]
    #[should_panic(expected = "window_size")]
    fn test_window_too_large() {
        windowed_view(&[1.0, 2.0], 3, 1);
    }
}