From b1596317b74c416c966ff8eeb97af0e80e54da91 Mon Sep 17 00:00:00 2001 From: Kirill Dubovikov Date: Sat, 21 Mar 2020 21:06:05 +0300 Subject: [PATCH 1/5] added implementation of `var` and `std` methods that are a single-dimensional versions of `var_axis` and `std_axis` methods --- src/numeric/impl_numeric.rs | 112 ++++++++++++++++++++++++++++++++++++ tests/numeric.rs | 68 ++++++++++++++++++++++ 2 files changed, 180 insertions(+) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 6fc4f6cf4..5cf6cff1b 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -13,6 +13,9 @@ use crate::imp_prelude::*; use crate::itertools::enumerate; use crate::numeric_util; +use crate::{FoldWhile, Zip}; +use num_traits::real::Real; + /// # Numerical Methods for Arrays impl ArrayBase where @@ -111,6 +114,115 @@ where sum } + /// Return variance of elements in the array. + /// + /// The variance is computed using the [Welford one-pass + /// algorithm](https://www.jstor.org/stable/1266577). + /// + /// The parameter `ddof` specifies the "delta degrees of freedom". For + /// example, to calculate the population variance, use `ddof = 0`, or to + /// calculate the sample variance, use `ddof = 1`. + /// + /// The variance is defined as: + /// + /// ```text + /// 1 n + /// variance = ―――――――― ∑ (xᵢ - x̅)² + /// n - ddof i=1 + /// ``` + /// + /// where + /// + /// ```text + /// 1 n + /// x̅ = ― ∑ xᵢ + /// n i=1 + /// ``` + /// + /// and `n` is the length of the axis. + /// + /// **Panics** if `ddof` is less than zero or greater than `n` + /// + /// # Example + /// + /// ``` + /// use ndarray::array; + /// use approx::assert_abs_diff_eq; + /// + /// let a = array![1., -4.32, 1.14, 0.32]; + /// let var = a.var(1.); + /// assert_abs_diff_eq!(var, 6.7331, epsilon = 1e-4); + /// ``` + pub fn var(&self, ddof: A) -> A + where + A: Float + FromPrimitive + { + let zero = A::from_usize(0).expect("Converting 0 to `A` must not fail."); + let n = A::from_usize(self.len()).expect("Converting length to `A` must not fail."); + assert!( + !(ddof < zero || ddof > n), + "`ddof` must not be less than zero or greater than the length of \ + the axis", + ); + let dof = n - ddof; + let mut mean = A::zero(); + let mut sum_sq = A::zero(); + for (i, &x) in self.into_iter().enumerate() { + let count = A::from_usize(i + 1).expect("Converting index to `A` must not fail."); + let delta = x - mean; + mean = mean + delta / count; + sum_sq = (x - mean).mul_add(delta, sum_sq); + } + sum_sq / dof + } + + /// Return standard deviation of elements in the array. + /// + /// The standard deviation is computed from the variance using + /// the [Welford one-pass algorithm](https://www.jstor.org/stable/1266577). + /// + /// The parameter `ddof` specifies the "delta degrees of freedom". For + /// example, to calculate the population standard deviation, use `ddof = 0`, + /// or to calculate the sample standard deviation, use `ddof = 1`. + /// + /// The standard deviation is defined as: + /// + /// ```text + /// ⎛ 1 n ⎞ + /// stddev = sqrt ⎜ ―――――――― ∑ (xᵢ - x̅)²⎟ + /// ⎝ n - ddof i=1 ⎠ + /// ``` + /// + /// where + /// + /// ```text + /// 1 n + /// x̅ = ― ∑ xᵢ + /// n i=1 + /// ``` + /// + /// and `n` is the length of the array. + /// + /// **Panics** if `ddof` is less than zero or greater than `n` + /// + /// # Example + /// + /// ``` + /// use ndarray::array; + /// use approx::assert_abs_diff_eq; + /// + /// let a = array![1., -4.32, 1.14, 0.32]; + /// let stddev = a.std(1.); + /// assert_abs_diff_eq!(stddev, 6.7331, epsilon = 1e-4); + /// ``` + pub fn std(&self, ddof: A) -> A + where + A: Float + FromPrimitive + { + self.var(ddof).sqrt() + } + + /// Return sum along `axis`. /// /// ``` diff --git a/tests/numeric.rs b/tests/numeric.rs index 7c6f1441e..cfb9e51d7 100644 --- a/tests/numeric.rs +++ b/tests/numeric.rs @@ -64,6 +64,74 @@ fn sum_mean_empty() { assert_eq!(a, None); } +#[test] +fn var() { + let a = array![1., -4.32, 1.14, 0.32]; + assert_abs_diff_eq!(a.var(0.), 5.049875, epsilon = 1e-8); + +} + +#[test] +#[should_panic] +fn var_negative_ddof() { + let a = array![1., 2., 3.]; + a.var(-1.); +} + +#[test] +#[should_panic] +fn var_too_large_ddof() { + let a = array![1., 2., 3.]; + a.var(4.); +} + +#[test] +fn var_nan_ddof() { + let a = Array2::::zeros((2, 3)); + let v = a.var(::std::f64::NAN); + assert!(v.is_nan()); +} + +#[test] +fn var_empty_arr() { + let a: Array1 = array![]; + assert!(a.var(0.0).is_nan()); +} + +#[test] +fn std() { + let a = array![1., -4.32, 1.14, 0.32]; + assert_abs_diff_eq!(a.std(0.), 2.24719, epsilon = 1e-5); + +} + +#[test] +#[should_panic] +fn std_negative_ddof() { + let a = array![1., 2., 3.]; + a.std(-1.); +} + +#[test] +#[should_panic] +fn std_too_large_ddof() { + let a = array![1., 2., 3.]; + a.std(4.); +} + +#[test] +fn std_nan_ddof() { + let a = Array2::::zeros((2, 3)); + let v = a.std(::std::f64::NAN); + assert!(v.is_nan()); +} + +#[test] +fn std_empty_arr() { + let a: Array1 = array![]; + assert!(a.std(0.0).is_nan()); +} + #[test] #[cfg(feature = "approx")] fn var_axis() { From 85f9ba9e0c08cd52cdd7daec4c85343f91c18341 Mon Sep 17 00:00:00 2001 From: Kirill Dubovikov Date: Sat, 21 Mar 2020 21:11:02 +0300 Subject: [PATCH 2/5] fixed documentation --- src/numeric/impl_numeric.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 5cf6cff1b..24db298e7 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -139,7 +139,7 @@ where /// n i=1 /// ``` /// - /// and `n` is the length of the axis. + /// and `n` is the length of the array. /// /// **Panics** if `ddof` is less than zero or greater than `n` /// From ead01894bc4d8e05d992b8d088bb596956c521d8 Mon Sep 17 00:00:00 2001 From: Kirill Dubovikov Date: Sat, 21 Mar 2020 21:18:41 +0300 Subject: [PATCH 3/5] fixed std doctest --- src/numeric/impl_numeric.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 24db298e7..b84ed6ae6 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -213,7 +213,7 @@ where /// /// let a = array![1., -4.32, 1.14, 0.32]; /// let stddev = a.std(1.); - /// assert_abs_diff_eq!(stddev, 6.7331, epsilon = 1e-4); + /// assert_abs_diff_eq!(stddev, 2.59483, epsilon = 1e-4); /// ``` pub fn std(&self, ddof: A) -> A where From 28c797e9c364f8800f51e071b4e0045788de870b Mon Sep 17 00:00:00 2001 From: Kirill Dubovikov Date: Sat, 21 Mar 2020 21:42:58 +0300 Subject: [PATCH 4/5] =?UTF-8?q?removed=20num=5Ftraits::real::Real=20which?= =?UTF-8?q?=20was=20added=20as=20autoimport=20by=20intellij=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/numeric/impl_numeric.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index b84ed6ae6..f6af73e51 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -14,7 +14,6 @@ use crate::itertools::enumerate; use crate::numeric_util; use crate::{FoldWhile, Zip}; -use num_traits::real::Real; /// # Numerical Methods for Arrays impl ArrayBase From 0e292f05f72a8b48a8621f8f97801d6ae0e2d1b7 Mon Sep 17 00:00:00 2001 From: Kirill Dubovikov Date: Sat, 21 Mar 2020 21:52:00 +0300 Subject: [PATCH 5/5] fixed formatting errors --- src/numeric/impl_numeric.rs | 5 ++--- src/private.rs | 2 +- tests/numeric.rs | 2 -- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index f6af73e51..a3858f829 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -154,7 +154,7 @@ where /// ``` pub fn var(&self, ddof: A) -> A where - A: Float + FromPrimitive + A: Float + FromPrimitive, { let zero = A::from_usize(0).expect("Converting 0 to `A` must not fail."); let n = A::from_usize(self.len()).expect("Converting length to `A` must not fail."); @@ -216,12 +216,11 @@ where /// ``` pub fn std(&self, ddof: A) -> A where - A: Float + FromPrimitive + A: Float + FromPrimitive, { self.var(ddof).sqrt() } - /// Return sum along `axis`. /// /// ``` diff --git a/src/private.rs b/src/private.rs index ea13164e4..552b31497 100644 --- a/src/private.rs +++ b/src/private.rs @@ -21,5 +21,5 @@ macro_rules! private_impl { fn __private__(&self) -> crate::private::PrivateMarker { crate::private::PrivateMarker } - } + }; } diff --git a/tests/numeric.rs b/tests/numeric.rs index cfb9e51d7..13f8433c5 100644 --- a/tests/numeric.rs +++ b/tests/numeric.rs @@ -68,7 +68,6 @@ fn sum_mean_empty() { fn var() { let a = array![1., -4.32, 1.14, 0.32]; assert_abs_diff_eq!(a.var(0.), 5.049875, epsilon = 1e-8); - } #[test] @@ -102,7 +101,6 @@ fn var_empty_arr() { fn std() { let a = array![1., -4.32, 1.14, 0.32]; assert_abs_diff_eq!(a.std(0.), 2.24719, epsilon = 1e-5); - } #[test]