use crate::prelude::*;
fn moment_precomputed_mean(s: &Series, moment: usize, mean: f64) -> PolarsResult<Option<f64>> {
let out = match moment {
0 => Some(1.0),
1 => Some(0.0),
_ => {
let mut n_list = vec![moment];
let mut current_n = moment;
while current_n > 2 {
if current_n % 2 == 1 {
current_n = (current_n - 1) / 2
} else {
current_n /= 2
}
n_list.push(current_n)
}
let a_zero_mean = s.cast(&DataType::Float64)? - mean;
let mut s = if n_list.pop().unwrap() == 1 {
#[allow(clippy::redundant_clone)]
a_zero_mean.clone()
} else {
&a_zero_mean * &a_zero_mean
};
for n in n_list.iter().rev() {
s = &s * &s;
if n % 2 == 1 {
s = &s * &a_zero_mean;
}
}
s.mean()
},
};
Ok(out)
}
impl Series {
pub fn skew(&self, bias: bool) -> PolarsResult<Option<f64>> {
let mean = match self.mean() {
Some(mean) => mean,
None => return Ok(None),
};
let m2 = moment_precomputed_mean(self, 2, mean)?.unwrap();
let m3 = moment_precomputed_mean(self, 3, mean)?.unwrap();
let out = m3 / m2.powf(1.5);
if !bias {
let n = (self.len() - self.null_count()) as f64;
Ok(Some(((n - 1.0) * n).sqrt() / (n - 2.0) * out))
} else {
Ok(Some(out))
}
}
pub fn kurtosis(&self, fisher: bool, bias: bool) -> PolarsResult<Option<f64>> {
let mean = match self.mean() {
Some(mean) => mean,
None => return Ok(None),
};
let m2 = moment_precomputed_mean(self, 2, mean)?.unwrap();
let m4 = moment_precomputed_mean(self, 4, mean)?.unwrap();
let out = if !bias {
let n = (self.len() - self.null_count()) as f64;
3.0 + 1.0 / (n - 2.0) / (n - 3.0)
* ((n.powf(2.0) - 1.0) * m4 / m2.powf(2.0) - 3.0 * (n - 1.0).powf(2.0))
} else {
m4 / m2.powf(2.0)
};
if fisher {
Ok(Some(out - 3.0))
} else {
Ok(Some(out))
}
}
}
#[cfg(test)]
mod test {
use super::*;
impl Series {
fn moment(&self, moment: usize) -> PolarsResult<Option<f64>> {
match self.mean() {
Some(mean) => moment_precomputed_mean(self, moment, mean),
None => Ok(None),
}
}
}
#[test]
fn test_moment_compute() -> PolarsResult<()> {
let s = Series::new("", &[1, 2, 3, 4, 5, 23]);
assert_eq!(s.moment(0)?, Some(1.0));
assert_eq!(s.moment(1)?, Some(0.0));
assert!((s.moment(2)?.unwrap() - 57.22222222222223).abs() < 0.00001);
assert!((s.moment(3)?.unwrap() - 724.0740740740742).abs() < 0.00001);
Ok(())
}
#[test]
fn test_skew() -> PolarsResult<()> {
let s = Series::new("", &[1, 2, 3, 4, 5, 23]);
let s2 = Series::new("", &[Some(1), Some(2), Some(3), None, Some(1)]);
assert!((s.skew(false)?.unwrap() - 2.2905330058490514).abs() < 0.0001);
assert!((s.skew(true)?.unwrap() - 1.6727687946848508).abs() < 0.0001);
assert!((s2.skew(false)?.unwrap() - 0.8545630383279711).abs() < 0.0001);
assert!((s2.skew(true)?.unwrap() - 0.49338220021815865).abs() < 0.0001);
Ok(())
}
#[test]
fn test_kurtosis() -> PolarsResult<()> {
let s = Series::new("", &[1, 2, 3, 4, 5, 23]);
assert!((s.kurtosis(true, true)?.unwrap() - 0.9945668771797536).abs() < 0.0001);
assert!((s.kurtosis(true, false)?.unwrap() - 5.400820058440946).abs() < 0.0001);
assert!((s.kurtosis(false, true)?.unwrap() - 3.994566877179754).abs() < 0.0001);
assert!((s.kurtosis(false, false)?.unwrap() - 8.400820058440946).abs() < 0.0001);
let s2 = Series::new(
"",
&[Some(1), Some(2), Some(3), None, Some(1), Some(2), Some(3)],
);
assert!((s2.kurtosis(true, true)?.unwrap() - (-1.5)).abs() < 0.0001);
assert!((s2.kurtosis(true, false)?.unwrap() - (-1.875)).abs() < 0.0001);
assert!((s2.kurtosis(false, true)?.unwrap() - 1.5).abs() < 0.0001);
assert!((s2.kurtosis(false, false)?.unwrap() - 1.125).abs() < 0.0001);
Ok(())
}
}