From b9f88e9c3b4b194314bba713ac17231f6c1ea89e Mon Sep 17 00:00:00 2001 From: Son <leson.phung@gmail.com> Date: Sat, 9 Mar 2019 13:33:44 +1100 Subject: [PATCH 1/8] A rough impl for argmin --- src/quantile.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/quantile.rs b/src/quantile.rs index 5c9b01d2..484b8e17 100644 --- a/src/quantile.rs +++ b/src/quantile.rs @@ -182,6 +182,13 @@ where S: Data<Elem = A>, D: Dimension, { + /// Finds the indices of the minimum values along an axis. + /// + /// Returns `None` if the array is empty. + fn argmin(&self) -> Option<Vec<D::Pattern>> + where + A: PartialOrd; + /// Finds the elementwise minimum of the array. /// /// Returns `None` if any of the pairwise orderings tested by the function @@ -278,6 +285,22 @@ where S: Data<Elem = A>, D: Dimension, { + fn argmin(&self) -> Option<Vec<D::Pattern>> + where + A: PartialOrd, + { + let min = self.min()?; + let mut args = Vec::<D::Pattern>::new(); + + self.indexed_iter().for_each(|(pattern, elem)| { + if elem.partial_cmp(min) == Some(cmp::Ordering::Equal) { + args.push(pattern) + } + }); + + Some(args) + } + fn min(&self) -> Option<&A> where A: PartialOrd, From b69a9d02a7335ba13f62f40ddf087862167fe07b Mon Sep 17 00:00:00 2001 From: Son <leson.phung@gmail.com> Date: Sat, 9 Mar 2019 21:13:04 +1100 Subject: [PATCH 2/8] A tests for argmin --- tests/quantile.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/quantile.rs b/tests/quantile.rs index 66d3c64c..af69a0e6 100644 --- a/tests/quantile.rs +++ b/tests/quantile.rs @@ -8,6 +8,24 @@ use ndarray_stats::{ Quantile1dExt, QuantileExt, }; +#[test] +fn test_argmin() { + let a = array![[1, 5, 3], [2, 0, 6]]; + assert_eq!(a.argmin(), Some(vec![(1, 1)])); + + let a = array![[1., 5., 3.], [2., 0., 6.]]; + assert_eq!(a.argmin(), Some(vec![(1, 1)])); + + let a = array![[1., 5., 3.], [2., ::std::f64::NAN, 6.]]; + assert_eq!(a.argmin(), None); +} + +#[test] +fn test_argmin_multiple_values() { + let a = array![[1, 5, 3], [2, 0, 6], [0, 1, 2]]; + assert_eq!(a.argmin(), Some(vec![(1, 1), (2, 0)])); +} + #[test] fn test_min() { let a = array![[1, 5, 3], [2, 0, 6]]; From 3cba684bd9e61a753a3552be54d3272d68668a17 Mon Sep 17 00:00:00 2001 From: Son <leson.phung@gmail.com> Date: Sat, 9 Mar 2019 21:21:42 +1100 Subject: [PATCH 3/8] Add argmax --- src/quantile.rs | 33 ++++++++++++++++++++++++++++++++- tests/quantile.rs | 18 ++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/quantile.rs b/src/quantile.rs index 484b8e17..ec52dda2 100644 --- a/src/quantile.rs +++ b/src/quantile.rs @@ -182,7 +182,11 @@ where S: Data<Elem = A>, D: Dimension, { - /// Finds the indices of the minimum values along an axis. + /// Finds the indices of the minimum values of the array. + /// + /// Returns `None` if any of the pairwise orderings tested by the function + /// are undefined. (For example, this occurs if there are any + /// floating-point NaN values in the array.) /// /// Returns `None` if the array is empty. fn argmin(&self) -> Option<Vec<D::Pattern>> @@ -210,6 +214,17 @@ where A: MaybeNan, A::NotNan: Ord; + /// Finds the indices of the maximum values of the array. + /// + /// Returns `None` if any of the pairwise orderings tested by the function + /// are undefined. (For example, this occurs if there are any + /// floating-point NaN values in the array.) + /// + /// Returns `None` if the array is empty. + fn argmax(&self) -> Option<Vec<D::Pattern>> + where + A: PartialOrd; + /// Finds the elementwise maximum of the array. /// /// Returns `None` if any of the pairwise orderings tested by the function @@ -326,6 +341,22 @@ where })) } + fn argmax(&self) -> Option<Vec<D::Pattern>> + where + A: PartialOrd, + { + let max = self.max()?; + let mut args = Vec::<D::Pattern>::new(); + + self.indexed_iter().for_each(|(pattern, elem)| { + if elem.partial_cmp(max) == Some(cmp::Ordering::Equal) { + args.push(pattern) + } + }); + + Some(args) + } + fn max(&self) -> Option<&A> where A: PartialOrd, diff --git a/tests/quantile.rs b/tests/quantile.rs index af69a0e6..555501d3 100644 --- a/tests/quantile.rs +++ b/tests/quantile.rs @@ -53,6 +53,24 @@ fn test_min_skipnan_all_nan() { assert!(a.min_skipnan().is_nan()); } +#[test] +fn test_argmax() { + let a = array![[1, 5, 3], [2, 0, 6]]; + assert_eq!(a.argmax(), Some(vec![(1, 2)])); + + let a = array![[1., 5., 3.], [2., 0., 6.]]; + assert_eq!(a.argmax(), Some(vec![(1, 2)])); + + let a = array![[1., 5., 3.], [2., ::std::f64::NAN, 6.]]; + assert_eq!(a.argmax(), None); +} + +#[test] +fn test_argmax_multiple_values() { + let a = array![[1, 6, 3], [2, 6, 6], [6, 1, 2]]; + assert_eq!(a.argmax(), Some(vec![(0, 1), (1, 1), (1, 2), (2, 0)])); +} + #[test] fn test_max() { let a = array![[1, 5, 7], [2, 0, 6]]; From 0f23e3731f54f12c237ea3586e9a442bf743df8e Mon Sep 17 00:00:00 2001 From: Son <leson.phung@gmail.com> Date: Sat, 9 Mar 2019 22:41:11 +1100 Subject: [PATCH 4/8] Make the implementation closer to numpy and Julia. --- src/quantile.rs | 54 ++++++++++++++++++++++++++++------------------- tests/quantile.rs | 20 ++++-------------- 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/quantile.rs b/src/quantile.rs index ec52dda2..1f8bfe3e 100644 --- a/src/quantile.rs +++ b/src/quantile.rs @@ -182,14 +182,14 @@ where S: Data<Elem = A>, D: Dimension, { - /// Finds the indices of the minimum values of the array. + /// Finds the first index of the minimum value of the array. /// /// Returns `None` if any of the pairwise orderings tested by the function /// are undefined. (For example, this occurs if there are any /// floating-point NaN values in the array.) /// /// Returns `None` if the array is empty. - fn argmin(&self) -> Option<Vec<D::Pattern>> + fn argmin(&self) -> Option<D::Pattern> where A: PartialOrd; @@ -214,14 +214,14 @@ where A: MaybeNan, A::NotNan: Ord; - /// Finds the indices of the maximum values of the array. + /// Finds the first index of the maximum value of the array. /// /// Returns `None` if any of the pairwise orderings tested by the function /// are undefined. (For example, this occurs if there are any /// floating-point NaN values in the array.) /// /// Returns `None` if the array is empty. - fn argmax(&self) -> Option<Vec<D::Pattern>> + fn argmax(&self) -> Option<D::Pattern> where A: PartialOrd; @@ -300,20 +300,25 @@ where S: Data<Elem = A>, D: Dimension, { - fn argmin(&self) -> Option<Vec<D::Pattern>> + fn argmin(&self) -> Option<D::Pattern> where A: PartialOrd, { - let min = self.min()?; - let mut args = Vec::<D::Pattern>::new(); + let min = self.first()?; + let mut pattern_min = self.dim(); - self.indexed_iter().for_each(|(pattern, elem)| { - if elem.partial_cmp(min) == Some(cmp::Ordering::Equal) { - args.push(pattern) - } - }); + self.indexed_iter() + .fold(Some(min), |acc, (pattern, elem)| { + match elem.partial_cmp(acc?)? { + cmp::Ordering::Less => { + pattern_min = pattern; + Some(elem) + } + _ => acc, + } + })?; - Some(args) + Some(pattern_min) } fn min(&self) -> Option<&A> @@ -341,20 +346,25 @@ where })) } - fn argmax(&self) -> Option<Vec<D::Pattern>> + fn argmax(&self) -> Option<D::Pattern> where A: PartialOrd, { - let max = self.max()?; - let mut args = Vec::<D::Pattern>::new(); + let max = self.first()?; + let mut pattern_max = self.dim(); - self.indexed_iter().for_each(|(pattern, elem)| { - if elem.partial_cmp(max) == Some(cmp::Ordering::Equal) { - args.push(pattern) - } - }); + self.indexed_iter() + .fold(Some(max), |acc, (pattern, elem)| { + match elem.partial_cmp(acc?)? { + cmp::Ordering::Greater => { + pattern_max = pattern; + Some(elem) + } + _ => acc, + } + })?; - Some(args) + Some(pattern_max) } fn max(&self) -> Option<&A> diff --git a/tests/quantile.rs b/tests/quantile.rs index 555501d3..e77403c2 100644 --- a/tests/quantile.rs +++ b/tests/quantile.rs @@ -11,21 +11,15 @@ use ndarray_stats::{ #[test] fn test_argmin() { let a = array![[1, 5, 3], [2, 0, 6]]; - assert_eq!(a.argmin(), Some(vec![(1, 1)])); + assert_eq!(a.argmin(), Some((1, 1))); let a = array![[1., 5., 3.], [2., 0., 6.]]; - assert_eq!(a.argmin(), Some(vec![(1, 1)])); + assert_eq!(a.argmin(), Some((1, 1))); let a = array![[1., 5., 3.], [2., ::std::f64::NAN, 6.]]; assert_eq!(a.argmin(), None); } -#[test] -fn test_argmin_multiple_values() { - let a = array![[1, 5, 3], [2, 0, 6], [0, 1, 2]]; - assert_eq!(a.argmin(), Some(vec![(1, 1), (2, 0)])); -} - #[test] fn test_min() { let a = array![[1, 5, 3], [2, 0, 6]]; @@ -56,21 +50,15 @@ fn test_min_skipnan_all_nan() { #[test] fn test_argmax() { let a = array![[1, 5, 3], [2, 0, 6]]; - assert_eq!(a.argmax(), Some(vec![(1, 2)])); + assert_eq!(a.argmax(), Some((1, 2))); let a = array![[1., 5., 3.], [2., 0., 6.]]; - assert_eq!(a.argmax(), Some(vec![(1, 2)])); + assert_eq!(a.argmax(), Some((1, 2))); let a = array![[1., 5., 3.], [2., ::std::f64::NAN, 6.]]; assert_eq!(a.argmax(), None); } -#[test] -fn test_argmax_multiple_values() { - let a = array![[1, 6, 3], [2, 6, 6], [6, 1, 2]]; - assert_eq!(a.argmax(), Some(vec![(0, 1), (1, 1), (1, 2), (2, 0)])); -} - #[test] fn test_max() { let a = array![[1, 5, 7], [2, 0, 6]]; From dd218f303d6883da2ae5c3519e43cf36f3c97934 Mon Sep 17 00:00:00 2001 From: Son <leson.phung@gmail.com> Date: Sat, 9 Mar 2019 23:09:08 +1100 Subject: [PATCH 5/8] Correctly initialize pattern --- src/quantile.rs | 4 ++-- tests/quantile.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/quantile.rs b/src/quantile.rs index 1f8bfe3e..c15678f3 100644 --- a/src/quantile.rs +++ b/src/quantile.rs @@ -305,7 +305,7 @@ where A: PartialOrd, { let min = self.first()?; - let mut pattern_min = self.dim(); + let mut pattern_min = D::zeros(self.ndim()).into_pattern(); self.indexed_iter() .fold(Some(min), |acc, (pattern, elem)| { @@ -351,7 +351,7 @@ where A: PartialOrd, { let max = self.first()?; - let mut pattern_max = self.dim(); + let mut pattern_max = D::zeros(self.ndim()).into_pattern(); self.indexed_iter() .fold(Some(max), |acc, (pattern, elem)| { diff --git a/tests/quantile.rs b/tests/quantile.rs index e77403c2..cf88dc1d 100644 --- a/tests/quantile.rs +++ b/tests/quantile.rs @@ -10,8 +10,8 @@ use ndarray_stats::{ #[test] fn test_argmin() { - let a = array![[1, 5, 3], [2, 0, 6]]; - assert_eq!(a.argmin(), Some((1, 1))); + let a = array![[0, 5, 3], [2, 0, 6]]; + assert_eq!(a.argmin(), Some((0, 0))); let a = array![[1., 5., 3.], [2., 0., 6.]]; assert_eq!(a.argmin(), Some((1, 1))); @@ -49,8 +49,8 @@ fn test_min_skipnan_all_nan() { #[test] fn test_argmax() { - let a = array![[1, 5, 3], [2, 0, 6]]; - assert_eq!(a.argmax(), Some((1, 2))); + let a = array![[7, 5, 3], [2, 0, 6]]; + assert_eq!(a.argmax(), Some((0, 0))); let a = array![[1., 5., 3.], [2., 0., 6.]]; assert_eq!(a.argmax(), Some((1, 2))); From 9e41f77f54c111e638101715a664f04b3da4c0bc Mon Sep 17 00:00:00 2001 From: Son <leson.phung@gmail.com> Date: Sun, 10 Mar 2019 21:51:00 +1100 Subject: [PATCH 6/8] Simplify argmin argmax and add a test case --- src/quantile.rs | 44 ++++++++++++++++++-------------------------- tests/quantile.rs | 14 ++++++++++---- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/quantile.rs b/src/quantile.rs index c15678f3..637ca3e7 100644 --- a/src/quantile.rs +++ b/src/quantile.rs @@ -304,21 +304,17 @@ where where A: PartialOrd, { - let min = self.first()?; - let mut pattern_min = D::zeros(self.ndim()).into_pattern(); + let mut current_min = self.first()?; + let mut current_pattern_min = D::zeros(self.ndim()).into_pattern(); - self.indexed_iter() - .fold(Some(min), |acc, (pattern, elem)| { - match elem.partial_cmp(acc?)? { - cmp::Ordering::Less => { - pattern_min = pattern; - Some(elem) - } - _ => acc, - } - })?; + for (pattern, elem) in self.indexed_iter() { + if elem.partial_cmp(current_min)? == cmp::Ordering::Less { + current_pattern_min = pattern; + current_min = elem + } + } - Some(pattern_min) + Some(current_pattern_min) } fn min(&self) -> Option<&A> @@ -350,21 +346,17 @@ where where A: PartialOrd, { - let max = self.first()?; - let mut pattern_max = D::zeros(self.ndim()).into_pattern(); + let mut current_max = self.first()?; + let mut current_pattern_max = D::zeros(self.ndim()).into_pattern(); - self.indexed_iter() - .fold(Some(max), |acc, (pattern, elem)| { - match elem.partial_cmp(acc?)? { - cmp::Ordering::Greater => { - pattern_max = pattern; - Some(elem) - } - _ => acc, - } - })?; + for (pattern, elem) in self.indexed_iter() { + if elem.partial_cmp(current_max)? == cmp::Ordering::Greater { + current_pattern_max = pattern; + current_max = elem + } + } - Some(pattern_max) + Some(current_pattern_max) } fn max(&self) -> Option<&A> diff --git a/tests/quantile.rs b/tests/quantile.rs index cf88dc1d..62c091a9 100644 --- a/tests/quantile.rs +++ b/tests/quantile.rs @@ -10,14 +10,17 @@ use ndarray_stats::{ #[test] fn test_argmin() { - let a = array![[0, 5, 3], [2, 0, 6]]; - assert_eq!(a.argmin(), Some((0, 0))); + let a = array![[1, 5, 3], [2, 0, 6]]; + assert_eq!(a.argmin(), Some((1, 1))); let a = array![[1., 5., 3.], [2., 0., 6.]]; assert_eq!(a.argmin(), Some((1, 1))); let a = array![[1., 5., 3.], [2., ::std::f64::NAN, 6.]]; assert_eq!(a.argmin(), None); + + let a = array![[1, 0, 3], [2, 0, 6]]; + assert_eq!(a.argmin(), Some((0, 1))); } #[test] @@ -49,14 +52,17 @@ fn test_min_skipnan_all_nan() { #[test] fn test_argmax() { - let a = array![[7, 5, 3], [2, 0, 6]]; - assert_eq!(a.argmax(), Some((0, 0))); + let a = array![[1, 5, 3], [2, 0, 6]]; + assert_eq!(a.argmax(), Some((1, 2))); let a = array![[1., 5., 3.], [2., 0., 6.]]; assert_eq!(a.argmax(), Some((1, 2))); let a = array![[1., 5., 3.], [2., ::std::f64::NAN, 6.]]; assert_eq!(a.argmax(), None); + + let a = array![[1, 5, 6], [2, 0, 6]]; + assert_eq!(a.argmax(), Some((0, 2))); } #[test] From f9bd4b4afbc4866efc7438c770832cf8907ab7f6 Mon Sep 17 00:00:00 2001 From: Son <leson.phung@gmail.com> Date: Sun, 10 Mar 2019 21:58:32 +1100 Subject: [PATCH 7/8] Add a test case of empty array --- tests/quantile.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/quantile.rs b/tests/quantile.rs index 62c091a9..4e799d2f 100644 --- a/tests/quantile.rs +++ b/tests/quantile.rs @@ -21,6 +21,9 @@ fn test_argmin() { let a = array![[1, 0, 3], [2, 0, 6]]; assert_eq!(a.argmin(), Some((0, 1))); + + let a: Array2<i32> = array![[], []]; + assert_eq!(a.argmin(), None); } #[test] @@ -63,6 +66,9 @@ fn test_argmax() { let a = array![[1, 5, 6], [2, 0, 6]]; assert_eq!(a.argmax(), Some((0, 2))); + + let a: Array2<i32> = array![[], []]; + assert_eq!(a.argmax(), None); } #[test] From a9b3826956578cf0286c79ca5545ba6c394d7ea0 Mon Sep 17 00:00:00 2001 From: Son <leson.phung@gmail.com> Date: Sun, 10 Mar 2019 22:20:50 +1100 Subject: [PATCH 8/8] Add examples for docs --- src/quantile.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/quantile.rs b/src/quantile.rs index 637ca3e7..a396edde 100644 --- a/src/quantile.rs +++ b/src/quantile.rs @@ -189,6 +189,20 @@ where /// floating-point NaN values in the array.) /// /// Returns `None` if the array is empty. + /// + /// # Example + /// + /// ``` + /// extern crate ndarray; + /// extern crate ndarray_stats; + /// + /// use ndarray::array; + /// use ndarray_stats::QuantileExt; + /// + /// let a = array![[1., 3., 5.], + /// [2., 0., 6.]]; + /// assert_eq!(a.argmin(), Some((1, 1))); + /// ``` fn argmin(&self) -> Option<D::Pattern> where A: PartialOrd; @@ -221,6 +235,20 @@ where /// floating-point NaN values in the array.) /// /// Returns `None` if the array is empty. + /// + /// # Example + /// + /// ``` + /// extern crate ndarray; + /// extern crate ndarray_stats; + /// + /// use ndarray::array; + /// use ndarray_stats::QuantileExt; + /// + /// let a = array![[1., 3., 7.], + /// [2., 5., 6.]]; + /// assert_eq!(a.argmax(), Some((0, 2))); + /// ``` fn argmax(&self) -> Option<D::Pattern> where A: PartialOrd;