use crate::batch;
use crate::error::{Result, StatsError};
#[derive(Default, Debug, PartialEq)]
pub struct Stats<'a> {
data: &'a [f64],
min: Option<f64>,
max: Option<f64>,
sum: Option<f64>,
mean: Option<f64>,
population_variance: Option<f64>,
sum_squared_deltas: Option<f64>,
sample_variance: Option<f64>,
population_standard_deviation: Option<f64>,
sample_standard_deviation: Option<f64>,
population_skewness: Option<f64>,
sample_skewness: Option<f64>,
population_kurtosis: Option<f64>,
sample_kurtosis: Option<f64>,
}
impl<'a> Stats<'a> {
pub fn new(data: &'a [f64]) -> Result<Self> {
batch::validate(data)?;
Ok(Stats {
data,
..Default::default()
})
}
pub fn count(&self) -> u32 {
self.data.len() as u32
}
pub fn min(&mut self) -> Result<f64> {
if let Some(min) = self.min {
return Ok(min);
}
let min = batch::min(self.data)?;
self.min = Some(min);
Ok(min)
}
pub fn max(&mut self) -> Result<f64> {
if let Some(max) = self.max {
return Ok(max);
}
let max = batch::max(self.data)?;
self.max = Some(max);
Ok(max)
}
pub fn sum(&mut self) -> Result<f64> {
if let Some(sum) = self.sum {
return Ok(sum);
}
let sum = batch::sum(self.data)?;
self.sum = Some(sum);
Ok(sum)
}
pub fn mean(&mut self) -> Result<f64> {
if let Some(mean) = self.mean {
return Ok(mean);
}
if self.data.is_empty() {
return Err(StatsError::NotEnoughData);
}
let mean = self.sum()? / (self.data.len() as f64);
self.mean = Some(mean);
Ok(mean)
}
fn sum_squared_deltas(&mut self) -> Result<f64> {
if let Some(sum_squared_deltas) = self.sum_squared_deltas {
return Ok(sum_squared_deltas);
}
let mean = self.mean()?;
let mut ssd = 0.0;
self.data.iter().for_each(|v| {
let delta = v - mean;
ssd += delta * delta;
});
self.sum_squared_deltas = Some(ssd);
Ok(ssd)
}
pub fn population_variance(&mut self) -> Result<f64> {
if let Some(population_variance) = self.population_variance {
return Ok(population_variance);
}
if self.data.len() <= 1 {
return Err(StatsError::NotEnoughData);
}
let population_variance = self.sum_squared_deltas()? / (self.data.len() as f64);
self.population_variance = Some(population_variance);
Ok(population_variance)
}
pub fn sample_variance(&mut self) -> Result<f64> {
if let Some(sample_variance) = self.sample_variance {
return Ok(sample_variance);
}
if self.data.len() <= 1 {
return Err(StatsError::NotEnoughData);
}
let sample_variance = self.sum_squared_deltas()? / ((self.data.len() - 1) as f64);
self.sample_variance = Some(sample_variance);
Ok(sample_variance)
}
pub fn population_standard_deviation(&mut self) -> Result<f64> {
if let Some(population_standard_deviation) = self.population_standard_deviation {
return Ok(population_standard_deviation);
}
let population_standard_deviation = f64::sqrt(self.population_variance()?);
self.population_standard_deviation = Some(population_standard_deviation);
Ok(population_standard_deviation)
}
pub fn sample_standard_deviation(&mut self) -> Result<f64> {
if let Some(sample_standard_deviation) = self.sample_standard_deviation {
return Ok(sample_standard_deviation);
}
let sample_standard_deviation = f64::sqrt(self.sample_variance()?);
self.sample_standard_deviation = Some(sample_standard_deviation);
Ok(sample_standard_deviation)
}
pub fn population_skewness(&mut self) -> Result<f64> {
if let Some(population_skewness) = self.population_skewness {
return Ok(population_skewness);
}
if self.data.len() <= 1 {
return Err(StatsError::NotEnoughData);
}
let mean = self.mean()?;
let sum3 = self.data.iter().fold(0.0, |sum, v| {
let delta = v - mean;
sum + delta * delta * delta
});
let ssv = self.population_variance()?;
let n = self.data.len() as f64;
let variance = f64::sqrt(ssv);
if variance == 0.0 {
return Err(StatsError::Undefined);
}
let population_skewness = sum3 / n / (variance * variance * variance);
self.population_skewness = Some(population_skewness);
Ok(population_skewness)
}
pub fn sample_skewness(&mut self) -> Result<f64> {
if let Some(sample_skewness) = self.sample_skewness {
return Ok(sample_skewness);
}
if self.data.len() <= 2 {
return Err(StatsError::NotEnoughData);
}
let pop_skewness = self.population_skewness()?;
let n = self.data.len() as f64;
let sample_skewness = f64::sqrt(n * (n - 1.0)) / (n - 2.0) * pop_skewness;
self.sample_skewness = Some(sample_skewness);
Ok(sample_skewness)
}
pub fn population_kurtosis(&mut self) -> Result<f64> {
if let Some(population_kurtosis) = self.population_kurtosis {
return Ok(population_kurtosis);
}
if self.data.len() <= 1 {
return Err(StatsError::NotEnoughData);
}
let mean = self.mean()?;
let n = self.data.len() as f64;
let sum4 = self.data.iter().fold(0.0, |sum4, v| {
let delta = v - mean;
sum4 + delta * delta * delta * delta
});
let variance = self.population_variance()?;
if variance == 0.0 {
return Err(StatsError::Undefined);
}
let population_kurtosis = sum4 / (variance * variance) / n - 3.0;
self.population_kurtosis = Some(population_kurtosis);
Ok(population_kurtosis)
}
pub fn sample_kurtosis(&mut self) -> Result<f64> {
if let Some(sample_kurtosis) = self.sample_kurtosis {
return Ok(sample_kurtosis);
}
if self.data.len() <= 3 {
return Err(StatsError::NotEnoughData);
}
let n = self.data.len() as f64;
let sample_kurtosis =
(n - 1.0) / ((n - 2.0) * (n - 3.0)) * ((n + 1.0) * self.population_kurtosis()? + 6.0);
self.sample_kurtosis = Some(sample_kurtosis);
Ok(sample_kurtosis)
}
}
pub fn descriptive(a: &[f64]) -> Result<Stats> {
let mut d = Stats::new(a)?;
d.min()?;
d.max()?;
d.sum()?;
d.mean()?;
d.population_variance()?;
d.sample_variance()?;
d.population_standard_deviation()?;
d.sample_standard_deviation()?;
d.population_skewness()?;
d.sample_skewness()?;
d.population_kurtosis()?;
d.sample_kurtosis()?;
Ok(d)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sum_squared_deltas() {
assert_eq!(
Stats::new(&vec![]).unwrap().sum_squared_deltas(),
Err(StatsError::NotEnoughData)
);
assert_eq!(
Stats::new(&vec![0.0]).unwrap().sum_squared_deltas(),
Ok(0.0)
);
assert_eq!(
Stats::new(&vec![1.0]).unwrap().sum_squared_deltas(),
Ok(0.0)
);
assert_eq!(
Stats::new(&vec![2.0]).unwrap().sum_squared_deltas(),
Ok(0.0)
);
assert_eq!(
Stats::new(&vec![-1.0]).unwrap().sum_squared_deltas(),
Ok(0.0)
);
assert_eq!(
Stats::new(&vec![0.0, 0.0]).unwrap().sum_squared_deltas(),
Ok(0.0)
);
assert_eq!(
Stats::new(&vec![1.0, 1.0]).unwrap().sum_squared_deltas(),
Ok(0.0)
);
assert_eq!(
Stats::new(&vec![2.0, 2.0]).unwrap().sum_squared_deltas(),
Ok(0.0)
);
assert_eq!(
Stats::new(&vec![0.0, 1.0]).unwrap().sum_squared_deltas(),
Ok(0.5)
);
assert_eq!(
Stats::new(&vec![1.0, 2.0]).unwrap().sum_squared_deltas(),
Ok(0.5)
);
assert_eq!(
Stats::new(&vec![-1.0, 0.0]).unwrap().sum_squared_deltas(),
Ok(0.5)
);
assert_eq!(
Stats::new(&vec![-1.0, 0.0, 1.0])
.unwrap()
.sum_squared_deltas(),
Ok(2.0)
);
}
}