Skip to content

Replace min/max, make quantile_mut return None for empty arrays, and fix docs #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 16 additions & 17 deletions src/histogram/strategies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,15 @@ impl<T> BinsBuildingStrategy for Sqrt<T>
{
type Elem = T;

/// **Panics** if the array is constant or if `a.len()==0` and division by 0 panics for `T`.
/// **Panics** if the array is constant or if `a.len()==0`.
fn from_array<S>(a: &ArrayBase<S, Ix1>) -> Self
where
S: Data<Elem=Self::Elem>
{
let n_elems = a.len();
let n_bins = (n_elems as f64).sqrt().round() as usize;
let min = a.min().clone();
let max = a.max().clone();
let min = a.min().unwrap().clone();
let max = a.max().unwrap().clone();
let bin_width = compute_bin_width(min.clone(), max.clone(), n_bins);
let builder = EquiSpaced::new(bin_width, min, max);
Self { builder }
Expand Down Expand Up @@ -220,15 +220,15 @@ impl<T> BinsBuildingStrategy for Rice<T>
{
type Elem = T;

/// **Panics** if the array is constant or if `a.len()==0` and division by 0 panics for `T`.
/// **Panics** if the array is constant or if `a.len()==0`.
fn from_array<S>(a: &ArrayBase<S, Ix1>) -> Self
where
S: Data<Elem=Self::Elem>
{
let n_elems = a.len();
let n_bins = (2. * (n_elems as f64).powf(1./3.)).round() as usize;
let min = a.min().clone();
let max = a.max().clone();
let min = a.min().unwrap().clone();
let max = a.max().unwrap().clone();
let bin_width = compute_bin_width(min.clone(), max.clone(), n_bins);
let builder = EquiSpaced::new(bin_width, min, max);
Self { builder }
Expand Down Expand Up @@ -259,15 +259,15 @@ impl<T> BinsBuildingStrategy for Sturges<T>
{
type Elem = T;

/// **Panics** if the array is constant or if `a.len()==0` and division by 0 panics for `T`.
/// **Panics** if the array is constant or if `a.len()==0`.
fn from_array<S>(a: &ArrayBase<S, Ix1>) -> Self
where
S: Data<Elem=Self::Elem>
{
let n_elems = a.len();
let n_bins = (n_elems as f64).log2().round() as usize + 1;
let min = a.min().clone();
let max = a.max().clone();
let min = a.min().unwrap().clone();
let max = a.max().unwrap().clone();
let bin_width = compute_bin_width(min.clone(), max.clone(), n_bins);
let builder = EquiSpaced::new(bin_width, min, max);
Self { builder }
Expand Down Expand Up @@ -298,21 +298,21 @@ impl<T> BinsBuildingStrategy for FreedmanDiaconis<T>
{
type Elem = T;

/// **Panics** if `IQR==0` or if `a.len()==0` and division by 0 panics for `T`.
/// **Panics** if `IQR==0` or if `a.len()==0`.
fn from_array<S>(a: &ArrayBase<S, Ix1>) -> Self
where
S: Data<Elem=Self::Elem>
{
let n_points = a.len();

let mut a_copy = a.to_owned();
let first_quartile = a_copy.quantile_mut::<Nearest>(0.25);
let third_quartile = a_copy.quantile_mut::<Nearest>(0.75);
let first_quartile = a_copy.quantile_mut::<Nearest>(0.25).unwrap();
let third_quartile = a_copy.quantile_mut::<Nearest>(0.75).unwrap();
let iqr = third_quartile - first_quartile;

let bin_width = FreedmanDiaconis::compute_bin_width(n_points, iqr);
let min = a_copy.min().clone();
let max = a_copy.max().clone();
let min = a_copy.min().unwrap().clone();
let max = a_copy.max().unwrap().clone();
let builder = EquiSpaced::new(bin_width, min, max);
Self { builder }
}
Expand Down Expand Up @@ -349,8 +349,7 @@ impl<T> BinsBuildingStrategy for Auto<T>
{
type Elem = T;

/// **Panics** if `IQR==0`, the array is constant or if
/// `a.len()==0` and division by 0 panics for `T`.
/// **Panics** if `IQR==0`, the array is constant, or `a.len()==0`.
fn from_array<S>(a: &ArrayBase<S, Ix1>) -> Self
where
S: Data<Elem=Self::Elem>
Expand Down Expand Up @@ -403,7 +402,7 @@ impl<T> Auto<T>
///
/// `bin_width = (max - min)/n`
///
/// **Panics** if division by 0 panics for `T`.
/// **Panics** if `n_bins == 0` and division by 0 panics for `T`.
fn compute_bin_width<T>(min: T, max: T, n_bins: usize) -> T
where
T: Ord + Clone + FromPrimitive + NumOps + Zero,
Expand Down
68 changes: 19 additions & 49 deletions src/quantile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,21 +175,14 @@ where
S: Data<Elem = A>,
D: Dimension,
{
/// Finds the elementwise minimum of the array.
///
/// **Panics** if the array is empty.
fn min(&self) -> &A
where
A: Ord;

/// Finds the elementwise minimum 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.)
///
/// Additionally, returns `None` if the array is empty.
fn min_partialord(&self) -> Option<&A>
fn min(&self) -> Option<&A>
where
A: PartialOrd;

Expand All @@ -203,21 +196,14 @@ where
A: MaybeNan,
A::NotNan: Ord;

/// Finds the elementwise maximum of the array.
///
/// **Panics** if the array is empty.
fn max(&self) -> &A
where
A: Ord;

/// Finds the elementwise maximum 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.)
///
/// Additionally, returns `None` if the array is empty.
fn max_partialord(&self) -> Option<&A>
fn max(&self) -> Option<&A>
where
A: PartialOrd;

Expand Down Expand Up @@ -259,8 +245,8 @@ where
/// - worst case: O(`m`^2);
/// where `m` is the number of elements in the array.
///
/// **Panics** if `axis` is out of bounds or if `q` is not between
/// `0.` and `1.` (inclusive).
/// **Panics** if `axis` is out of bounds, if the axis has length 0, or if
/// `q` is not between `0.` and `1.` (inclusive).
fn quantile_axis_mut<I>(&mut self, axis: Axis, q: f64) -> Array<A, D::Smaller>
where
D: RemoveAxis,
Expand All @@ -285,22 +271,11 @@ where
S: Data<Elem = A>,
D: Dimension,
{
fn min(&self) -> &A
where
A: Ord,
{
let first = self
.iter()
.next()
.expect("Attempted to find min of empty array.");
self.fold(first, |acc, elem| if elem < acc { elem } else { acc })
}

fn min_partialord(&self) -> Option<&A>
fn min(&self) -> Option<&A>
where
A: PartialOrd,
{
let first = self.iter().next()?;
let first = self.first()?;
self.fold(Some(first), |acc, elem| match elem.partial_cmp(acc?)? {
cmp::Ordering::Less => Some(elem),
_ => acc,
Expand All @@ -312,7 +287,7 @@ where
A: MaybeNan,
A::NotNan: Ord,
{
let first = self.iter().next().and_then(|v| v.try_as_not_nan());
let first = self.first().and_then(|v| v.try_as_not_nan());
A::from_not_nan_ref_opt(self.fold_skipnan(first, |acc, elem| {
Some(match acc {
Some(acc) => acc.min(elem),
Expand All @@ -321,22 +296,11 @@ where
}))
}

fn max(&self) -> &A
where
A: Ord,
{
let first = self
.iter()
.next()
.expect("Attempted to find max of empty array.");
self.fold(first, |acc, elem| if elem > acc { elem } else { acc })
}

fn max_partialord(&self) -> Option<&A>
fn max(&self) -> Option<&A>
where
A: PartialOrd,
{
let first = self.iter().next()?;
let first = self.first()?;
self.fold(Some(first), |acc, elem| match elem.partial_cmp(acc?)? {
cmp::Ordering::Greater => Some(elem),
_ => acc,
Expand All @@ -348,7 +312,7 @@ where
A: MaybeNan,
A::NotNan: Ord,
{
let first = self.iter().next().and_then(|v| v.try_as_not_nan());
let first = self.first().and_then(|v| v.try_as_not_nan());
A::from_not_nan_ref_opt(self.fold_skipnan(first, |acc, elem| {
Some(match acc {
Some(acc) => acc.max(elem),
Expand Down Expand Up @@ -442,8 +406,10 @@ pub trait QuantileExt1d<A, S>
/// - worst case: O(`m`^2);
/// where `m` is the number of elements in the array.
///
/// Returns `None` if the array is empty.
///
/// **Panics** if `q` is not between `0.` and `1.` (inclusive).
fn quantile_mut<I>(&mut self, q: f64) -> A
fn quantile_mut<I>(&mut self, q: f64) -> Option<A>
where
A: Ord + Clone,
S: DataMut,
Expand All @@ -454,13 +420,17 @@ impl<A, S> QuantileExt1d<A, S> for ArrayBase<S, Ix1>
where
S: Data<Elem = A>,
{
fn quantile_mut<I>(&mut self, q: f64) -> A
fn quantile_mut<I>(&mut self, q: f64) -> Option<A>
where
A: Ord + Clone,
S: DataMut,
I: Interpolate<A>,
{
self.quantile_axis_mut::<I>(Axis(0), q).into_scalar()
if self.is_empty() {
None
} else {
Some(self.quantile_axis_mut::<I>(Axis(0), q).into_scalar())
}
}
}

32 changes: 20 additions & 12 deletions tests/quantile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@ use ndarray_stats::{
#[test]
fn test_min() {
let a = array![[1, 5, 3], [2, 0, 6]];
assert_eq!(a.min(), &0);
}
assert_eq!(a.min(), Some(&0));

#[test]
fn test_min_partialord() {
let a = array![[1., 5., 3.], [2., 0., 6.]];
assert_eq!(a.min_partialord(), Some(&0.));
assert_eq!(a.min(), Some(&0.));

let a = array![[1., 5., 3.], [2., ::std::f64::NAN, 6.]];
assert_eq!(a.min_partialord(), None);
assert_eq!(a.min(), None);
}

#[test]
Expand All @@ -41,16 +38,13 @@ fn test_min_skipnan_all_nan() {
#[test]
fn test_max() {
let a = array![[1, 5, 7], [2, 0, 6]];
assert_eq!(a.max(), &7);
}
assert_eq!(a.max(), Some(&7));

#[test]
fn test_max_partialord() {
let a = array![[1., 5., 7.], [2., 0., 6.]];
assert_eq!(a.max_partialord(), Some(&7.));
assert_eq!(a.max(), Some(&7.));

let a = array![[1., 5., 7.], [2., ::std::f64::NAN, 6.]];
assert_eq!(a.max_partialord(), None);
assert_eq!(a.max(), None);
}

#[test]
Expand All @@ -75,6 +69,20 @@ fn test_quantile_axis_mut_with_odd_axis_length() {
assert!(p == a.subview(Axis(0), 1));
}

#[test]
#[should_panic]
fn test_quantile_axis_mut_with_zero_axis_length() {
let mut a = Array2::<i32>::zeros((5, 0));
a.quantile_axis_mut::<Lower>(Axis(1), 0.5);
}

#[test]
fn test_quantile_axis_mut_with_empty_array() {
let mut a = Array2::<i32>::zeros((5, 0));
let p = a.quantile_axis_mut::<Lower>(Axis(0), 0.5);
assert_eq!(p.shape(), &[0]);
}

#[test]
fn test_quantile_axis_mut_with_even_axis_length() {
let mut b = arr2(&[[1, 3, 2, 10], [2, 4, 3, 11], [3, 5, 6, 12], [4, 6, 7, 13]]);
Expand Down