use num_traits::ToPrimitive;
use crate::error::DataType;
use crate::helpers::MinMax;
use crate::helpers::NumExt;
use crate::Result;
use crate::StatsError;
type Frequency<T> = (usize, T);
pub trait FrequencyStats<T>: IntoIterator<Item = Frequency<T>> + Clone
where
T: NumExt,
{
fn count(&self) -> usize {
self.clone().into_iter().map(|(freq, _)| freq).sum()
}
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<T> {
T::from_usize(self.non_zero_count()?).ok_or(StatsError::CouldNotConvert {
from: DataType::Usize,
to: DataType::Item,
})
}
fn sum(&self) -> Result<T> {
let mut sum = T::zero();
for (freq, val) in self.clone().into_iter() {
sum = sum
+ val
* T::from_usize(freq).ok_or(StatsError::CouldNotConvert {
from: DataType::Usize,
to: DataType::Item,
})?;
}
Ok(sum)
}
fn mean(&self) -> Result<T> {
Ok(self.sum()? / self.non_zero_count_into_item()?)
}
fn variance(&self) -> Result<T> {
let mean = self.mean()?;
let mut sum = T::zero();
for (freq, val) in self.clone().into_iter() {
let diff = val - mean;
sum = sum
+ diff
* diff
* T::from_usize(freq).ok_or(StatsError::CouldNotConvert {
from: DataType::Usize,
to: DataType::Item,
})?;
}
Ok(sum / self.non_zero_count_into_item()?)
}
fn std_dev(&self) -> Result<T>
where
T: ToPrimitive,
{
T::from_f64(
self.variance()?
.to_f64()
.ok_or(StatsError::CouldNotConvert {
from: DataType::Item,
to: DataType::F64,
})?
.sqrt(),
)
.ok_or(StatsError::CouldNotConvert {
from: DataType::F64,
to: DataType::Item,
})
}
fn min(&self) -> Result<T>
where
T: MinMax,
{
self.clone()
.into_iter()
.map(|(_, val)| val)
.reduce(T::min)
.ok_or(StatsError::EmptyCollection)
}
fn max(&self) -> Result<T>
where
T: MinMax,
{
self.clone()
.into_iter()
.map(|(_, val)| val)
.reduce(T::max)
.ok_or(StatsError::EmptyCollection)
}
#[inline]
fn range(&self) -> Result<T>
where
T: MinMax,
{
Ok(self.max()? - self.min()?)
}
fn mode(&self) -> Result<T> {
let mut mode = (0, T::zero());
self.non_zero_count()?;
for (freq, val) in self.clone() {
if freq >= mode.0 {
mode = (freq, val);
}
}
Ok(mode.1)
}
}
impl<T, I> FrequencyStats<T> for I
where
I: IntoIterator<Item = Frequency<T>> + Clone,
T: 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_non_zero_count() {
let vec: Vec<(usize, i32)> = vec![(1, 1), (2, 2)];
assert_eq!(vec.non_zero_count(), Ok(3));
}
#[test]
fn test_sum() {
let vec: Vec<(usize, i32)> = vec![(1, 1), (2, 2)];
assert_eq!(vec.sum(), Ok(5));
}
#[test]
fn test_mean() {
let vec: Vec<(usize, i32)> = vec![(1, 1), (2, 2)];
assert_eq!(vec.mean(), Ok(1));
let vec: Vec<(usize, f32)> = vec![(1, 1.0), (2, 2.0)];
assert_relative_eq!(vec.mean().unwrap(), 5.0 / 3.0);
}
#[test]
fn test_variance() {
let vec: Vec<(usize, i32)> = vec![(1, 1), (2, 2)];
assert_eq!(vec.variance(), Ok(0));
let vec: Vec<(usize, f32)> = vec![(1, 1.0), (2, 2.0)];
assert_relative_eq!(vec.variance().unwrap(), 2.0 / 9.0);
}
#[test]
fn test_std_dev() {
let vec: Vec<(usize, i32)> = vec![(1, 1), (2, 2)];
assert_eq!(vec.std_dev(), Ok(0));
}
}