use num_traits::{FromPrimitive, ToPrimitive};
use crate::error::DataType;
use crate::helpers::MinMax;
use crate::helpers::NumExt;
use crate::Result;
use crate::StatsError;
pub trait Stats: IntoIterator + Clone
where
Self::Item: NumExt,
{
#[inline]
fn sum(&self) -> Self::Item {
self.clone().into_iter().sum()
}
#[inline]
fn count(&self) -> usize {
self.clone().into_iter().count()
}
fn non_zero_count(&self) -> Result<usize> {
let count = self.count();
if count == 0 {
Err(StatsError::EmptyCollection)
} else {
Ok(count)
}
}
fn non_zero_count_into_item(&self) -> Result<Self::Item> {
Self::Item::from_usize(self.non_zero_count()?).ok_or(StatsError::CouldNotConvert {
from: DataType::Usize,
to: DataType::Item,
})
}
fn mean(&self) -> Result<Self::Item> {
Ok(self.sum() / self.non_zero_count_into_item()?)
}
fn variance(&self) -> Result<Self::Item> {
let mean = self.mean()?;
Ok(self
.clone()
.into_iter()
.map(|x| (x - mean) * (x - mean))
.sum::<Self::Item>()
/ self.non_zero_count_into_item()?)
}
fn std_dev(&self) -> Result<Self::Item>
where
Self::Item: ToPrimitive,
{
Self::Item::from_f64(match self.variance()?.to_f64() {
Some(x) => x.sqrt(),
None => {
return Err(StatsError::CouldNotConvert {
from: DataType::Item,
to: DataType::F64,
})
}
})
.ok_or(StatsError::CouldNotConvert {
from: DataType::F64,
to: DataType::Item,
})
}
fn min(&self) -> Result<Self::Item>
where
Self::Item: MinMax,
{
self.clone()
.into_iter()
.reduce(Self::Item::min)
.ok_or(StatsError::EmptyCollection)
}
fn max(&self) -> Result<Self::Item>
where
Self::Item: MinMax,
{
self.clone()
.into_iter()
.reduce(Self::Item::max)
.ok_or(StatsError::EmptyCollection)
}
#[inline]
fn range(&self) -> Result<Self::Item>
where
Self::Item: MinMax,
{
Ok(self.max()? - self.min()?)
}
}
impl<T> Stats for T
where
T: IntoIterator + Clone,
T::Item: NumExt,
{
}
#[cfg(test)]
mod tests {
use super::*;
extern crate std;
use std::prelude::rust_2021::*;
use std::vec;
use approx::assert_relative_eq;
#[test]
fn test_sum_vec() {
let v = vec![1, 2, 3];
assert_eq!(v.sum(), 6);
let v = vec![1.0, 2.0, 3.0];
assert_relative_eq!(v.sum(), 6.0);
}
#[test]
fn test_count_vec() {
let v = vec![1, 2, 3];
assert_eq!(v.count(), 3);
}
#[test]
fn test_mean_vec() {
let v = vec![1, 2, 3];
assert_eq!(v.mean(), Ok(2));
let v: Vec<f64> = vec![1.0, 2.0, 3.0];
assert_relative_eq!(v.mean().unwrap(), 2.0);
let v = vec![1, 2, 3, 4];
assert_eq!(v.mean(), Ok(2));
}
#[test]
fn test_variance_vec() {
let v = vec![1.0, 2.0, 3.0];
assert_relative_eq!(v.variance().unwrap(), 2.0 / 3.0);
}
#[test]
fn test_std_dev_vec() {
let v = vec![1.0, 2.0, 3.0];
assert_relative_eq!(v.std_dev().unwrap(), 2.0_f64.sqrt() / 3.0_f64.sqrt());
}
#[test]
fn test_non_zero_count_fail() {
assert_eq!(
Vec::<i32>::new().non_zero_count(),
Err(StatsError::EmptyCollection)
)
}
#[test]
fn test_non_zero_count_as_item_fail() {
assert_eq!(
Vec::<i8>::from_iter(std::iter::repeat(1).take(128)).non_zero_count_into_item(),
Err(StatsError::CouldNotConvert {
from: DataType::Usize,
to: DataType::Item
})
)
}
#[test]
fn test_std_dev_vec_fail() {
assert_eq!(
Vec::<i32>::new().std_dev(),
Err(StatsError::EmptyCollection)
)
}
#[test]
fn test_min_vec() {
let v = vec![1, 2, 3];
assert_eq!(<Vec<i32> as Stats>::min(&v), Ok(1));
let v = vec![1.0, 2.0, 3.0];
assert_eq!(v.min(), Ok(1.0));
}
#[test]
fn test_max_vec() {
let v = vec![1, 2, 3];
assert_eq!(<Vec<i32> as Stats>::max(&v), Ok(3));
let v = vec![1.0, 2.0, 3.0];
assert_eq!(v.max(), Ok(3.0));
}
#[test]
fn test_range_vec() {
let v = vec![1, 2, 3];
assert_eq!(<Vec<i32> as Stats>::range(&v), Ok(2));
let v = vec![1.0, 2.0, 3.0];
assert_eq!(v.range(), Ok(2.0));
}
}