use crate::error::{Result, StatsError};
pub fn validate(data: &[f64]) -> Result<()> {
for v in data {
if f64::is_nan(*v) || f64::is_infinite(*v) {
return Err(StatsError::InvalidData);
}
}
Ok(())
}
pub fn count(data: &[f64]) -> u32 {
data.len() as u32
}
pub fn min(data: &[f64]) -> Result<f64> {
if data.is_empty() {
return Err(StatsError::NotEnoughData);
}
Ok(data.iter().fold(f64::INFINITY, |a, &b| a.min(b)))
}
pub fn max(data: &[f64]) -> Result<f64> {
if data.is_empty() {
return Err(StatsError::NotEnoughData);
}
Ok(data.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b)))
}
pub fn sum(data: &[f64]) -> Result<f64> {
if data.is_empty() {
return Err(StatsError::NotEnoughData);
}
Ok(data.iter().fold(0.0, |sum, v| sum + v))
}
pub fn mean(data: &[f64]) -> Result<f64> {
if data.is_empty() {
return Err(StatsError::NotEnoughData);
}
Ok(sum(data)? / (data.len() as f64))
}
fn sum_squared_deltas(data: &[f64]) -> Result<f64> {
let mean = mean(data)?;
let mut ssd = 0.0;
data.iter().for_each(|v| {
let delta = v - mean;
ssd += delta * delta;
});
Ok(ssd)
}
pub fn population_variance(data: &[f64]) -> Result<f64> {
if data.len() <= 1 {
return Err(StatsError::NotEnoughData);
}
Ok(sum_squared_deltas(data)? / (data.len() as f64))
}
pub fn sample_variance(data: &[f64]) -> Result<f64> {
if data.len() <= 1 {
return Err(StatsError::NotEnoughData);
}
Ok(sum_squared_deltas(data)? / ((data.len() - 1) as f64))
}
pub fn population_standard_deviation(data: &[f64]) -> Result<f64> {
Ok(f64::sqrt(population_variance(data)?))
}
pub fn sample_standard_deviation(data: &[f64]) -> Result<f64> {
Ok(f64::sqrt(sample_variance(data)?))
}
pub fn population_skewness(data: &[f64]) -> Result<f64> {
if data.len() <= 1 {
return Err(StatsError::NotEnoughData);
}
let mean = mean(data)?;
let sum3 = data.iter().fold(0.0, |sum, v| {
let delta = v - mean;
sum + delta * delta * delta
});
let ssv = population_variance(data)?;
let n = data.len() as f64;
let variance = f64::sqrt(ssv);
if variance == 0.0 {
return Err(StatsError::Undefined);
}
Ok(sum3 / n / (variance * variance * variance))
}
pub fn sample_skewness(data: &[f64]) -> Result<f64> {
if data.len() <= 2 {
return Err(StatsError::NotEnoughData);
}
let pop_skewness = population_skewness(data)?;
let n = data.len() as f64;
let skew = f64::sqrt(n * (n - 1.0)) / (n - 2.0) * pop_skewness;
Ok(skew)
}
pub fn population_kurtosis(data: &[f64]) -> Result<f64> {
if data.len() <= 1 {
return Err(StatsError::NotEnoughData);
}
let mean = mean(data)?;
let n = data.len() as f64;
let sum4 = data.iter().fold(0.0, |sum4, v| {
let delta = v - mean;
sum4 + delta * delta * delta * delta
});
let variance = population_variance(data)?;
if variance == 0.0 {
return Err(StatsError::Undefined);
}
let kurtosis = sum4 / (variance * variance) / n - 3.0;
Ok(kurtosis)
}
pub fn sample_kurtosis(data: &[f64]) -> Result<f64> {
if data.len() <= 3 {
return Err(StatsError::NotEnoughData);
}
let n = data.len() as f64;
Ok((n - 1.0) / ((n - 2.0) * (n - 3.0)) * ((n + 1.0) * population_kurtosis(data)? + 6.0))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sum_squared_deltas() {
assert_eq!(sum_squared_deltas(&vec![]), Err(StatsError::NotEnoughData));
assert_eq!(sum_squared_deltas(&vec![0.0]), Ok(0.0));
assert_eq!(sum_squared_deltas(&vec![1.0]), Ok(0.0));
assert_eq!(sum_squared_deltas(&vec![2.0]), Ok(0.0));
assert_eq!(sum_squared_deltas(&vec![-1.0]), Ok(0.0));
assert_eq!(sum_squared_deltas(&vec![0.0, 0.0]), Ok(0.0));
assert_eq!(sum_squared_deltas(&vec![1.0, 1.0]), Ok(0.0));
assert_eq!(sum_squared_deltas(&vec![2.0, 2.0]), Ok(0.0));
assert_eq!(sum_squared_deltas(&vec![0.0, 1.0]), Ok(0.5));
assert_eq!(sum_squared_deltas(&vec![1.0, 2.0]), Ok(0.5));
assert_eq!(sum_squared_deltas(&vec![-1.0, 0.0]), Ok(0.5));
assert_eq!(sum_squared_deltas(&vec![-1.0, 0.0, 1.0]), Ok(2.0));
}
}