use num_traits::{FromPrimitive, ToPrimitive};
use std::borrow::Borrow;
use std::collections::HashMap;
use std::ops::Deref;
#[allow(unused_imports)]
use std::ops::DerefMut;
#[cfg_attr(doc, katexit::katexit)]
#[derive(Clone, PartialEq)]
pub struct Sample<T> {
data: Vec<T>,
mean: Option<f64>,
m2: Option<f64>,
}
impl<T> Sample<T>
where
T: Copy + FromPrimitive + ToPrimitive,
{
pub fn new() -> Self {
Self {
data: Vec::new(),
mean: None,
m2: None,
}
}
pub fn from_values<U, I>(data: U) -> Self
where
U: IntoIterator<Item = I>,
I: Borrow<T>,
{
let mut sample = Self::new();
for value in data.into_iter().map(|t| *t.borrow()) {
sample.push(value);
}
sample
}
pub fn push(&mut self, value: T) {
self.insert(self.len(), value);
}
pub fn pop(&mut self) -> Option<T> {
if self.is_empty() {
None
} else {
Some(self.remove(self.len() - 1))
}
}
pub fn insert(&mut self, index: usize, element: T) {
self.data.insert(index, element);
let x = element.to_f64().expect("Cannot convert to f64.");
let delta = x - self.mean.unwrap_or(0.0);
self.mean = Some(self.mean.unwrap_or(0.0) + delta / (self.len() as f64));
self.m2 = Some(self.m2.unwrap_or(0.0) + delta * (x - self.mean.unwrap()));
}
pub fn remove(&mut self, index: usize) -> T {
let value = self.data.remove(index);
let x = value.to_f64().expect("Cannot convert to f64.");
if self.is_empty() {
self.mean = None;
self.m2 = None;
} else {
let delta = x - self.mean.unwrap();
self.mean = Some(self.mean.unwrap() - (delta / (self.len() as f64)));
self.m2 = Some(self.m2.unwrap() - delta * (x - self.mean.unwrap()));
}
value
}
pub fn clear(&mut self) {
self.data.clear();
self.mean = None;
self.m2 = None;
}
pub fn mean(&self) -> Option<f64> {
self.mean
}
pub fn median(&self) -> Option<f64> {
if self.is_empty() {
return None;
}
let mut sorted = Vec::with_capacity(self.len());
for &value in &self.data {
sorted.push(value.to_f64().expect("Cannot convert to f64."));
}
sorted.sort_unstable_by(|x, x1| x.partial_cmp(x1).expect("Cannot compare f64 values."));
let mid = sorted.len() / 2;
if sorted.len() % 2 == 0 {
let left = sorted[mid - 1].to_f64().expect("Cannot convert to f64.");
let right = sorted[mid].to_f64().expect("Cannot convert to f64.");
Some((left + right) / 2.0)
} else {
Some(sorted[mid].to_f64().expect("Cannot convert to f64."))
}
}
pub fn mode(&self) -> Option<T> {
if self.is_empty() {
return None;
}
let mut occurrences = HashMap::new();
for &value in &self.data {
*occurrences
.entry(value.to_f64().expect("Cannot convert to f64.").to_bits())
.or_insert(0) += 1usize;
}
let highest_frequency = *occurrences.values().max().unwrap();
for v in self.iter().rev() {
if let Some(&count) =
occurrences.get(&v.to_f64().expect("Cannot convert to f64.").to_bits())
&& count == highest_frequency
{
return Some(*v);
}
}
unreachable!("One of the values must be the mode.");
}
pub fn sample_variance(&self) -> Option<f64> {
if self.len() < 2 {
None
} else {
Some(self.m2.unwrap() / (self.len() as f64 - 1.0))
}
}
pub fn sample_stddev(&self) -> Option<f64> {
self.sample_variance().map(|v| v.sqrt())
}
pub fn population_variance(&self) -> Option<f64> {
self.m2.map(|m2| m2 / self.len() as f64)
}
pub fn population_stddev(&self) -> Option<f64> {
self.population_variance().map(|v| v.sqrt())
}
}
impl<T> Default for Sample<T>
where
T: Copy + FromPrimitive + ToPrimitive,
{
fn default() -> Self {
Self::new()
}
}
impl<T> Deref for Sample<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.data
}
}