From 1ec4e42b87dc0438938ee86d75c8c371a83bcd4d Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sat, 1 Sep 2018 19:20:26 +0100 Subject: [PATCH 01/12] Implementing interpolation strategies as trait bounds. --- src/numeric/impl_numeric.rs | 53 ++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 9664c49ef..d49bb8cf6 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -22,10 +22,55 @@ use { /// Used to choose the interpolation strategy in [`percentile_axis_mut`]. /// /// [`percentile_axis_mut`]: struct.ArrayBase.html#method.percentile_axis_mut -pub enum InterpolationStrategy { - Lower, - Nearest, - Higher, +pub trait Interpolate { + fn needs_lower(q: f64, len: usize) -> bool; + fn needs_upper(q: f64, len: usize) -> bool; + fn interpolate(lower: Option, upper: Option, q: f64, len: usize) -> T; +} + +struct Upper; +struct Lower; +struct Nearest; + +impl Interpolate for Upper { + fn needs_lower(q: f64, len: usize) -> bool { + false + } + fn needs_upper(q: f64, len: usize) -> bool { + true + } + fn interpolate(lower: Option, upper: Option, q: f64, len: usize) -> T { + upper.unwrap() + } +} + +impl Interpolate for Lower { + fn needs_lower(q: f64, len: usize) -> bool { + true + } + fn needs_upper(q: f64, len: usize) -> bool { + false + } + fn interpolate(lower: Option, upper: Option, q: f64, len: usize) -> T { + lower.unwrap() + } +} + +impl Interpolate for Nearest { + fn needs_lower(q: f64, len: usize) -> bool { + let float_percentile_index = ((len - 1) as f64) * q; + (float_percentile_index.round() - float_percentile_index) <= 0. + } + fn needs_upper(q: f64, len: usize) -> bool { + !Self::needs_lower(q, len) + } + fn interpolate(lower: Option, upper: Option, q: f64, len: usize) -> T { + if Self::needs_lower(q, len) { + lower.unwrap() + } else { + upper.unwrap() + } + } } /// Numerical methods for arrays. From 63bbb07526ab8775b959fc28c40b7afdb2f463bf Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sat, 1 Sep 2018 19:28:15 +0100 Subject: [PATCH 02/12] Providing some default methods. --- src/numeric/impl_numeric.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index d49bb8cf6..1f05e9112 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -23,6 +23,17 @@ use { /// /// [`percentile_axis_mut`]: struct.ArrayBase.html#method.percentile_axis_mut pub trait Interpolate { + fn float_percentile_index(q: f64, len: usize) -> f64 { + ((len - 1) as f64) * q + } + + fn lower_index(q: f64, len: usize) -> usize { + Self::float_percentile_index(q, len).floor() as usize + } + + fn upper_index(q: f64, len: usize) -> usize { + Self::float_percentile_index(q, len).ceil() as usize + } fn needs_lower(q: f64, len: usize) -> bool; fn needs_upper(q: f64, len: usize) -> bool; fn interpolate(lower: Option, upper: Option, q: f64, len: usize) -> T; @@ -58,8 +69,7 @@ impl Interpolate for Lower { impl Interpolate for Nearest { fn needs_lower(q: f64, len: usize) -> bool { - let float_percentile_index = ((len - 1) as f64) * q; - (float_percentile_index.round() - float_percentile_index) <= 0. + ((Self::lower_index(q, len) as f64) - Self::float_percentile_index(q, len)) <= 0. } fn needs_upper(q: f64, len: usize) -> bool { !Self::needs_lower(q, len) From 3d1a196fc7cdd981dafc3c65620af12cdd2d9e53 Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sat, 1 Sep 2018 19:50:44 +0100 Subject: [PATCH 03/12] Mysterious type annotation error. --- src/numeric/impl_numeric.rs | 31 +++++++++++++++++++++++-------- src/numeric/mod.rs | 1 - 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 1f05e9112..0ed0bf480 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -69,7 +69,8 @@ impl Interpolate for Lower { impl Interpolate for Nearest { fn needs_lower(q: f64, len: usize) -> bool { - ((Self::lower_index(q, len) as f64) - Self::float_percentile_index(q, len)) <= 0. + let lower = Self::lower_index(q, len); + ((lower as f64) - Self::float_percentile_index(q, len)) <= 0. } fn needs_upper(q: f64, len: usize) -> bool { !Self::needs_lower(q, len) @@ -209,19 +210,33 @@ impl ArrayBase /// /// **Panics** if `axis` is out of bounds or if `q` is not between /// `0.` and `1.` (inclusive). - pub fn percentile_axis_mut(&mut self, axis: Axis, q: f64, interpolation_strategy: InterpolationStrategy) -> Array + pub fn percentile_axis_mut(&mut self, axis: Axis, q: f64) -> Array where D: RemoveAxis, A: Ord + Clone + Zero, S: DataMut, + I: Interpolate>, { assert!((0. <= q) && (q <= 1.)); - let float_percentile_index = ((self.len_of(axis) - 1) as f64) * q; - let percentile_index = match interpolation_strategy { - InterpolationStrategy::Lower => float_percentile_index.floor() as usize, - InterpolationStrategy::Nearest => float_percentile_index.round() as usize, - InterpolationStrategy::Higher => float_percentile_index.ceil() as usize, + let mut lower = None; + let mut upper = None; + let axis_len = self.len_of(axis); + if I::needs_lower(q, axis_len) { + lower = Some( + self.map_axis_mut( + axis, + |mut x| x.sorted_get_mut(I::lower_index(q, axis_len)) + ) + ); }; - self.map_axis_mut(axis, |mut x| x.sorted_get_mut(percentile_index)) + if I::needs_upper(q, axis_len) { + upper = Some( + self.map_axis_mut( + axis, + |mut x| x.sorted_get_mut(I::upper_index(q, axis_len)) + ) + ); + }; + I::interpolate(lower, upper, q, axis_len) } /// Return variance along `axis`. diff --git a/src/numeric/mod.rs b/src/numeric/mod.rs index 88ff33649..443d209ef 100644 --- a/src/numeric/mod.rs +++ b/src/numeric/mod.rs @@ -1,3 +1,2 @@ pub mod impl_numeric; -pub use self::impl_numeric::InterpolationStrategy; From 10a72d0adc7bc3404953f34b1993009243a9f162 Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sun, 2 Sep 2018 11:11:41 +0100 Subject: [PATCH 04/12] Fixed. --- src/numeric/impl_numeric.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 0ed0bf480..080e06766 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -69,14 +69,14 @@ impl Interpolate for Lower { impl Interpolate for Nearest { fn needs_lower(q: f64, len: usize) -> bool { - let lower = Self::lower_index(q, len); - ((lower as f64) - Self::float_percentile_index(q, len)) <= 0. + let lower = >::lower_index(q, len); + ((lower as f64) - >::float_percentile_index(q, len)) <= 0. } fn needs_upper(q: f64, len: usize) -> bool { - !Self::needs_lower(q, len) + !>::needs_lower(q, len) } fn interpolate(lower: Option, upper: Option, q: f64, len: usize) -> T { - if Self::needs_lower(q, len) { + if >::needs_lower(q, len) { lower.unwrap() } else { upper.unwrap() From f8b7713c3be5f3c4451e2c5f5c7270e6fa195540 Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sun, 2 Sep 2018 12:21:39 +0100 Subject: [PATCH 05/12] Making Lower, Upper, Nearest public. --- src/numeric/impl_numeric.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 080e06766..3e4eea6fa 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -39,9 +39,9 @@ pub trait Interpolate { fn interpolate(lower: Option, upper: Option, q: f64, len: usize) -> T; } -struct Upper; -struct Lower; -struct Nearest; +pub struct Upper; +pub struct Lower; +pub struct Nearest; impl Interpolate for Upper { fn needs_lower(q: f64, len: usize) -> bool { From e7d1f617a998e82b31d26dcf8b0172cc96f82852 Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sun, 2 Sep 2018 12:21:59 +0100 Subject: [PATCH 06/12] Removed empty line. --- src/numeric/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/numeric/mod.rs b/src/numeric/mod.rs index 443d209ef..bc959a241 100644 --- a/src/numeric/mod.rs +++ b/src/numeric/mod.rs @@ -1,2 +1 @@ - pub mod impl_numeric; From cec96cafae978b1e7d60315f97412a628086785d Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sun, 2 Sep 2018 12:33:27 +0100 Subject: [PATCH 07/12] Re-exporting functions, structs and traits. --- src/numeric/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/numeric/mod.rs b/src/numeric/mod.rs index bc959a241..30024d902 100644 --- a/src/numeric/mod.rs +++ b/src/numeric/mod.rs @@ -1 +1,3 @@ -pub mod impl_numeric; +pub use self::impl_numeric::*; + +mod impl_numeric; From c786eacf4a7cf3bf3b4a00c6e584321f125b4ee8 Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sun, 2 Sep 2018 12:33:37 +0100 Subject: [PATCH 08/12] Fixed import. --- tests/array.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/array.rs b/tests/array.rs index 1369c1be6..51cc36209 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -8,7 +8,7 @@ extern crate itertools; use ndarray::{Slice, SliceInfo, SliceOrIndex}; use ndarray::prelude::*; -use ndarray::numeric::InterpolationStrategy; +use ndarray::numeric::{Lower, Nearest, Upper}; use ndarray::{ rcarr2, arr3, From 744a427748cede98c3556dc8feefe1ff6be7c035 Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sun, 2 Sep 2018 12:39:32 +0100 Subject: [PATCH 09/12] Fixed test cases. --- tests/array.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/array.rs b/tests/array.rs index 51cc36209..75a2610fa 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -8,7 +8,7 @@ extern crate itertools; use ndarray::{Slice, SliceInfo, SliceOrIndex}; use ndarray::prelude::*; -use ndarray::numeric::{Lower, Nearest, Upper}; +use ndarray::numeric::{Lower}; use ndarray::{ rcarr2, arr3, @@ -1759,7 +1759,7 @@ fn test_percentile_axis_mut_with_odd_axis_length() { [3, 5, 6, 12] ] ); - let p = a.percentile_axis_mut(Axis(0), 0.5, InterpolationStrategy::Lower); + let p = a.percentile_axis_mut::(Axis(0), 0.5); assert!(p == a.subview(Axis(0), 1)); } @@ -1773,20 +1773,20 @@ fn test_percentile_axis_mut_with_even_axis_length() { [4, 6, 7, 13] ] ); - let q = b.percentile_axis_mut(Axis(0), 0.5, InterpolationStrategy::Lower); + let q = b.percentile_axis_mut::(Axis(0), 0.5); assert!(q == b.subview(Axis(0), 1)); } #[test] fn test_percentile_axis_mut_to_get_minimum() { let mut b = arr2(&[[1, 3, 22, 10]]); - let q = b.percentile_axis_mut(Axis(1), 0., InterpolationStrategy::Lower); + let q = b.percentile_axis_mut::(Axis(1), 0.); assert!(q == arr1(&[1])); } #[test] fn test_percentile_axis_mut_to_get_maximum() { let mut b = arr1(&[1, 3, 22, 10]); - let q = b.percentile_axis_mut(Axis(0), 1., InterpolationStrategy::Lower); + let q = b.percentile_axis_mut::(Axis(0), 1.); assert!(q == arr0(22)); } From bdc8c3a8715fb19966f3ca37e6ba93ed80ee4bb3 Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sun, 2 Sep 2018 12:47:12 +0100 Subject: [PATCH 10/12] Updated docs. --- src/impl_1d.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/impl_1d.rs b/src/impl_1d.rs index b9bdb61f2..e69162259 100644 --- a/src/impl_1d.rs +++ b/src/impl_1d.rs @@ -27,13 +27,18 @@ impl ArrayBase } } - /// Return the element that would occupy the `i`-th position if the array - /// were sorted in increasing order. + /// Return the element that would occupy the `i`-th position if + /// the array were sorted in increasing order. /// /// The array is shuffled **in place** to retrieve the desired element: /// no copy of the array is allocated. - /// No assumptions should be made on the ordering of elements - /// after this computation. + /// After the shuffling, all elements with an index smaller than `i` + /// are smaller than the desired element, while all elements with + /// an index greater or equal than `i` are greater than or equal + /// to the desired element. + /// + /// No other assumptions should be made on the ordering of the + /// elements after this computation. /// /// Complexity ([quickselect](https://en.wikipedia.org/wiki/Quickselect)): /// - average case: O(`n`); From b5264454b0027f9da62bab8e9e61da59f0320710 Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sun, 2 Sep 2018 12:48:35 +0100 Subject: [PATCH 11/12] Fixed docs. --- 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 3e4eea6fa..32896035d 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -19,7 +19,7 @@ use { Zip, }; -/// Used to choose the interpolation strategy in [`percentile_axis_mut`]. +/// Used to provide an interpolation strategy to [`percentile_axis_mut`]. /// /// [`percentile_axis_mut`]: struct.ArrayBase.html#method.percentile_axis_mut pub trait Interpolate { From 1b39c6133973d7aade0f9aac9c30c0951fe88450 Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sun, 2 Sep 2018 12:49:50 +0100 Subject: [PATCH 12/12] Fixed warnings on unused variables. --- src/numeric/impl_numeric.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 32896035d..6ad427fcb 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -44,25 +44,25 @@ pub struct Lower; pub struct Nearest; impl Interpolate for Upper { - fn needs_lower(q: f64, len: usize) -> bool { + fn needs_lower(_q: f64, _len: usize) -> bool { false } - fn needs_upper(q: f64, len: usize) -> bool { + fn needs_upper(_q: f64, _len: usize) -> bool { true } - fn interpolate(lower: Option, upper: Option, q: f64, len: usize) -> T { + fn interpolate(_lower: Option, upper: Option, _q: f64, _len: usize) -> T { upper.unwrap() } } impl Interpolate for Lower { - fn needs_lower(q: f64, len: usize) -> bool { + fn needs_lower(_q: f64, _len: usize) -> bool { true } - fn needs_upper(q: f64, len: usize) -> bool { + fn needs_upper(_q: f64, _len: usize) -> bool { false } - fn interpolate(lower: Option, upper: Option, q: f64, len: usize) -> T { + fn interpolate(lower: Option, _upper: Option, _q: f64, _len: usize) -> T { lower.unwrap() } }