use crate::Matrix;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Aggregate {
Mean,
Median,
}
pub fn sync(data: &Matrix, frames: &[usize], aggregate: Aggregate) -> Matrix {
let n_features = data.rows();
let t = data.cols();
let mut boundaries: Vec<usize> = Vec::with_capacity(frames.len() + 2);
if frames.is_empty() || frames[0] > 0 {
boundaries.push(0);
}
for &f in frames {
boundaries.push(f.min(t));
}
if boundaries.last().copied().unwrap_or(0) < t {
boundaries.push(t);
}
let windows: Vec<(usize, usize)> = boundaries.windows(2).map(|w| (w[0], w[1])).collect();
let n_windows = windows.len();
let mut result = Matrix::zeros(n_features, n_windows);
for (col, &(start, end)) in windows.iter().enumerate() {
if end <= start {
continue;
}
let n = (end - start) as f64;
for r in 0..n_features {
let val = match aggregate {
Aggregate::Mean => {
let sum: f64 = (start..end).map(|c| data.get(r, c)).sum();
sum / n
}
Aggregate::Median => {
let mut vals: Vec<f64> = (start..end).map(|c| data.get(r, c)).collect();
median(&mut vals)
}
};
*result.get_mut(r, col) = val;
}
}
result
}
fn median(v: &mut [f64]) -> f64 {
let n = v.len();
if n == 0 {
return 0.0;
}
v.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
if n.is_multiple_of(2) {
(v[n / 2 - 1] + v[n / 2]) / 2.0
} else {
v[n / 2]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sync_mean_basic() {
let data = Matrix::from_rows(&[&[1.0, 2.0, 3.0, 4.0]]);
let result = sync(&data, &[2], Aggregate::Mean);
assert_eq!(result.shape(), (1, 2));
assert!((result.get(0, 0) - 1.5).abs() < 1e-12);
assert!((result.get(0, 1) - 3.5).abs() < 1e-12);
}
#[test]
fn test_sync_median_basic() {
let data = Matrix::from_rows(&[&[1.0, 3.0, 2.0, 4.0, 5.0]]);
let result = sync(&data, &[3], Aggregate::Median);
assert_eq!(result.shape(), (1, 2));
assert!((result.get(0, 0) - 2.0).abs() < 1e-12);
assert!((result.get(0, 1) - 4.5).abs() < 1e-12);
}
#[test]
fn test_sync_empty_frames() {
let data = Matrix::from_rows(&[&[1.0, 2.0, 3.0]]);
let result = sync(&data, &[], Aggregate::Mean);
assert_eq!(result.shape(), (1, 1));
assert!((result.get(0, 0) - 2.0).abs() < 1e-12);
}
}