use num_traits::ToPrimitive;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::{Estimate, Merge};
include!("mean.rs");
include!("variance.rs");
#[cfg(any(feature = "std", feature = "libm"))]
include!("skewness.rs");
#[cfg(any(feature = "std", feature = "libm"))]
include!("kurtosis.rs");
pub type MeanWithError = Variance;
#[doc(hidden)]
#[macro_export]
macro_rules! define_moments_common {
($name:ident, $MAX_MOMENT:expr) => {
use num_traits::{pow, ToPrimitive};
struct IterBinomial {
a: u64,
n: u64,
k: u64,
}
impl IterBinomial {
#[inline]
pub fn new(n: u64) -> IterBinomial {
IterBinomial { k: 0, a: 1, n }
}
}
impl Iterator for IterBinomial {
type Item = u64;
#[inline]
fn next(&mut self) -> Option<u64> {
if self.k > self.n {
return None;
}
self.a = if !(self.k == 0) {
self.a * (self.n - self.k + 1) / self.k
} else {
1
};
self.k += 1;
Some(self.a)
}
}
const MAX_MOMENT: usize = $MAX_MOMENT;
impl $name {
#[inline]
pub fn new() -> $name {
$name {
n: 0,
avg: 0.,
m: [0.; MAX_MOMENT - 1],
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.n == 0
}
#[inline]
pub fn len(&self) -> u64 {
self.n
}
#[inline]
pub fn mean(&self) -> f64 {
if self.n > 0 { self.avg } else { f64::NAN }
}
#[inline]
pub fn central_moment(&self, p: usize) -> f64 {
let n = self.n.to_f64().unwrap();
match p {
0 => 1.,
1 => 0.,
_ => if self.n > 0 { self.m[p - 2] / n } else { f64::NAN },
}
}
#[cfg(any(feature = "std", feature = "libm"))]
#[inline]
pub fn standardized_moment(&self, p: usize) -> f64 {
match p {
0 => self.n.to_f64().unwrap(),
1 => 0.,
2 => 1.,
_ => {
let variance = self.central_moment(2);
assert_ne!(variance, 0.);
self.central_moment(p) / pow(num_traits::Float::sqrt(variance), p)
}
}
}
#[inline]
pub fn sample_variance(&self) -> f64 {
if self.n < 2 {
return f64::NAN;
}
self.m[0] / (self.n - 1).to_f64().unwrap()
}
#[cfg(any(feature = "std", feature = "libm"))]
#[inline]
pub fn sample_skewness(&self) -> f64 {
use num_traits::Float;
if self.n == 0 {
return f64::NAN;
}
if self.n == 1 {
return 0.;
}
let n = self.n.to_f64().unwrap();
if self.n < 3 {
return self.central_moment(3)
/ Float::powf(n * (self.central_moment(2) / (n - 1.)), 1.5);
}
Float::sqrt(n * (n - 1.)) / (n * (n - 2.))
* Float::powf(self.central_moment(3) / (self.central_moment(2) / n), 1.5)
}
#[inline]
pub fn sample_excess_kurtosis(&self) -> f64 {
if self.n < 4 {
return f64::NAN;
}
let n = self.n.to_f64().unwrap();
(n + 1.) * n * self.central_moment(4)
/ ((n - 1.) * (n - 2.) * (n - 3.) * pow(self.central_moment(2), 2))
- 3. * pow(n - 1., 2) / ((n - 2.) * (n - 3.))
}
#[inline]
pub fn add(&mut self, x: f64) {
self.n += 1;
let delta = x - self.avg;
let n = self.n.to_f64().unwrap();
self.avg += delta / n;
let mut coeff_delta = delta;
let over_n = 1. / n;
let mut term1 = (n - 1.) * (-over_n);
let factor1 = -over_n;
let mut term2 = (n - 1.) * over_n;
let factor2 = (n - 1.) * over_n;
let factor_coeff = -delta * over_n;
let prev_m = self.m;
for p in 2..=MAX_MOMENT {
term1 *= factor1;
term2 *= factor2;
coeff_delta *= delta;
self.m[p - 2] += (term1 + term2) * coeff_delta;
let mut coeff = 1.;
let mut binom = IterBinomial::new(p as u64);
binom.next().unwrap(); for k in 1..(p - 1) {
coeff *= factor_coeff;
self.m[p - 2] +=
binom.next().unwrap().to_f64().unwrap() * prev_m[p - 2 - k] * coeff;
}
}
}
}
impl $crate::Merge for $name {
#[inline]
fn merge(&mut self, other: &$name) {
if other.is_empty() {
return;
}
if self.is_empty() {
*self = other.clone();
return;
}
let n_a = self.n.to_f64().unwrap();
let n_b = other.n.to_f64().unwrap();
let delta = other.avg - self.avg;
self.n += other.n;
let n = self.n.to_f64().unwrap();
let n_a_over_n = n_a / n;
let n_b_over_n = n_b / n;
self.avg += n_b_over_n * delta;
let factor_a = -n_b_over_n * delta;
let factor_b = n_a_over_n * delta;
let mut term_a = n_a * factor_a;
let mut term_b = n_b * factor_b;
let prev_m = self.m;
for p in 2..=MAX_MOMENT {
term_a *= factor_a;
term_b *= factor_b;
self.m[p - 2] += other.m[p - 2] + term_a + term_b;
let mut coeff_a = 1.;
let mut coeff_b = 1.;
let mut coeff_delta = 1.;
let mut binom = IterBinomial::new(p as u64);
binom.next().unwrap();
for k in 1..(p - 1) {
coeff_a *= -n_b_over_n;
coeff_b *= n_a_over_n;
coeff_delta *= delta;
self.m[p - 2] += binom.next().unwrap().to_f64().unwrap()
* coeff_delta
* (prev_m[p - 2 - k] * coeff_a + other.m[p - 2 - k] * coeff_b);
}
}
}
}
impl core::default::Default for $name {
fn default() -> $name {
$name::new()
}
}
$crate::impl_from_iterator!($name);
$crate::impl_from_par_iterator!($name);
$crate::impl_extend!($name);
};
}
#[cfg(feature = "serde")]
#[doc(hidden)]
#[macro_export]
macro_rules! define_moments_inner {
($name:ident, $MAX_MOMENT:expr) => {
$crate::define_moments_common!($name, $MAX_MOMENT);
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct $name {
/// Number of samples.
n: u64,
avg: f64,
m: [f64; MAX_MOMENT - 1],
}
};
}
#[cfg(not(feature = "serde"))]
#[doc(hidden)]
#[macro_export]
macro_rules! define_moments_inner {
($name:ident, $MAX_MOMENT:expr) => {
$crate::define_moments_common!($name, $MAX_MOMENT);
#[derive(Debug, Clone)]
pub struct $name {
/// Number of samples.
n: u64,
avg: f64,
m: [f64; MAX_MOMENT - 1],
}
};
}
#[macro_export]
macro_rules! define_moments {
($name:ident, $MAX_MOMENT:expr) => {
$crate::define_moments_inner!($name, $MAX_MOMENT);
};
}