use std::fmt::Debug;
use std::ops::{Add, Div, Mul, Sub};
use crate::Iterstats;
pub trait Normalize<A = Self>: Sized {
type Output;
fn normalize<I>(iter: I, min: Self::Output, max: Self::Output) -> NormalizeIter<Self::Output>
where
I: Iterator<Item = A> + Clone;
}
#[derive(Debug, Clone)]
pub struct NormalizeIter<T> {
iter: std::vec::IntoIter<T>,
norm_min: T,
norm_range: T,
data_min: T,
data_range: T,
}
impl<T> Iterator for NormalizeIter<T>
where
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + Debug,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.iter
.next()
.map(|v| (v - self.data_min) / self.data_range * self.norm_range + self.norm_min)
}
}
macro_rules! norm_impl {
($typ:ty) => {
impl Normalize for $typ {
type Output = $typ;
fn normalize<I>(iter: I, min: $typ, max: $typ) -> NormalizeIter<Self::Output>
where
I: Iterator<Item = Self> + Clone,
{
let norm_range = max - min;
let (data_min, data_max) = iter.clone().range().unwrap_or_default();
let data_conv = iter.collect::<Vec<_>>().into_iter();
let data_range = data_max - data_min;
NormalizeIter {
iter: data_conv,
norm_min: min,
norm_range,
data_min,
data_range,
}
}
}
impl Normalize for &$typ {
type Output = $typ;
fn normalize<I>(iter: I, min: $typ, max: $typ) -> NormalizeIter<Self::Output>
where
I: Iterator<Item = Self> + Clone,
{
iter.map(|v| *v).normalize(min, max)
}
}
};
}
norm_impl!(f64);
norm_impl!(f32);
#[cfg(test)]
mod tests {
use super::*;
use paste::paste;
macro_rules! test_norm {
($name:ident: $typ:ty as $iter:expr ; iter => ($min:expr, $max:expr) $expected:expr ) => {
paste! {
#[test]
fn [<$name:snake _iter >]() {
let zscores: Vec<_> = <&$typ>::normalize($iter.iter(), $min, $max).collect();
assert_eq!(zscores, $expected);
}
}
};
($name:ident: $typ:ty as $iter:expr ; into_iter => ($min:expr, $max:expr) $expected:expr ) => {
paste! {
#[test]
fn [<$name:snake _into_iter >]() {
let zscores: Vec<_> = <$typ>::normalize($iter.into_iter(), $min, $max).collect();
assert_eq!(zscores, $expected);
}
}
};
($name:ident: $typ:ty as $iter:expr => ($min:expr, $max:expr) $expected:expr ) => {
test_norm!($name: $typ as $iter; iter => ($min, $max) $expected);
test_norm!($name: $typ as $iter; into_iter => ($min, $max) $expected);
};
}
test_norm!(f64_0_100: f64 as [-1., -2., 3., 8.] => (0., 100.) vec![10., 0., 50., 100.]);
test_norm!(f64_m10_90: f64 as [-1., -2., 3., 8.] => (-10., 90.) vec![0., -10., 40., 90.]);
test_norm!(f32_0_1: f32 as [-1., -2., 3., 8.] => (0., 1.) vec![0.1, 0., 0.5, 1.]);
test_norm!(f64_empty: f64 as [] => (0., 1.) vec![]);
}