Skip to content

Means #20

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 10 commits into from
Jan 10, 2019
Merged
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
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ addons:
- libssl-dev
cache: cargo
rust:
- 1.30.0
- 1.31.0
- stable
- beta
- nightly
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -24,3 +24,4 @@ itertools = { version = "0.7.0", default-features = false }
[dev-dependencies]
quickcheck = "0.7"
ndarray-rand = "0.9"
approx = "0.3"
7 changes: 6 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -37,15 +37,20 @@ extern crate ndarray_rand;
#[cfg(test)]
#[macro_use(quickcheck)]
extern crate quickcheck;
#[cfg(test)]
#[macro_use(abs_diff_eq)]
extern crate approx;

pub use maybe_nan::{MaybeNan, MaybeNanExt};
pub use quantile::{interpolate, QuantileExt, Quantile1dExt};
pub use sort::Sort1dExt;
pub use correlation::CorrelationExt;
pub use histogram::HistogramExt;
pub use summary_statistics::SummaryStatisticsExt;

mod maybe_nan;
mod quantile;
mod sort;
mod correlation;
pub mod histogram;
mod summary_statistics;
pub mod histogram;
97 changes: 97 additions & 0 deletions src/summary_statistics/means.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use ndarray::{Data, Dimension, ArrayBase};
use num_traits::{FromPrimitive, Float, Zero};
use std::ops::{Add, Div};
use super::SummaryStatisticsExt;


impl<A, S, D> SummaryStatisticsExt<A, S, D> for ArrayBase<S, D>
where
S: Data<Elem = A>,
D: Dimension,
{
fn mean(&self) -> Option<A>
where
A: Clone + FromPrimitive + Add<Output=A> + Div<Output=A> + Zero
{
let n_elements = self.len();
if n_elements == 0 {
None
} else {
let n_elements = A::from_usize(n_elements)
.expect("Converting number of elements to `A` must not fail.");
Some(self.sum() / n_elements)
}
}

fn harmonic_mean(&self) -> Option<A>
where
A: Float + FromPrimitive,
{
self.map(|x| x.recip()).mean().map(|x| x.recip())
}

fn geometric_mean(&self) -> Option<A>
where
A: Float + FromPrimitive,
{
self.map(|x| x.ln()).mean().map(|x| x.exp())
}
}

#[cfg(test)]
mod tests {
use super::SummaryStatisticsExt;
use std::f64;
use noisy_float::types::N64;
use ndarray::Array1;

#[test]
fn test_means_with_nan_values() {
let a = array![f64::NAN, 1.];
assert!(a.mean().unwrap().is_nan());
assert!(a.harmonic_mean().unwrap().is_nan());
assert!(a.geometric_mean().unwrap().is_nan());
}

#[test]
fn test_means_with_empty_array_of_floats() {
let a: Array1<f64> = array![];
assert!(a.mean().is_none());
assert!(a.harmonic_mean().is_none());
assert!(a.geometric_mean().is_none());
}

#[test]
fn test_means_with_empty_array_of_noisy_floats() {
let a: Array1<N64> = array![];
assert!(a.mean().is_none());
assert!(a.harmonic_mean().is_none());
assert!(a.geometric_mean().is_none());
}

#[test]
fn test_means_with_array_of_floats() {
let a: Array1<f64> = array![
0.99889651, 0.0150731 , 0.28492482, 0.83819218, 0.48413156,
0.80710412, 0.41762936, 0.22879429, 0.43997224, 0.23831807,
0.02416466, 0.6269962 , 0.47420614, 0.56275487, 0.78995021,
0.16060581, 0.64635041, 0.34876609, 0.78543249, 0.19938356,
0.34429457, 0.88072369, 0.17638164, 0.60819363, 0.250392 ,
0.69912532, 0.78855523, 0.79140914, 0.85084218, 0.31839879,
0.63381769, 0.22421048, 0.70760302, 0.99216018, 0.80199153,
0.19239188, 0.61356023, 0.31505352, 0.06120481, 0.66417377,
0.63608897, 0.84959691, 0.43599069, 0.77867775, 0.88267754,
0.83003623, 0.67016118, 0.67547638, 0.65220036, 0.68043427
];
// Computed using NumPy
let expected_mean = 0.5475494059146699;
// Computed using SciPy
let expected_harmonic_mean = 0.21790094950226022;
// Computed using SciPy
let expected_geometric_mean = 0.4345897639796527;

abs_diff_eq!(a.mean().unwrap(), expected_mean, epsilon = f64::EPSILON);
abs_diff_eq!(a.harmonic_mean().unwrap(), expected_harmonic_mean, epsilon = f64::EPSILON);
abs_diff_eq!(a.geometric_mean().unwrap(), expected_geometric_mean, epsilon = f64::EPSILON);
}
}
66 changes: 66 additions & 0 deletions src/summary_statistics/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//! Summary statistics (e.g. mean, variance, etc.).
use ndarray::{Data, Dimension};
use num_traits::{FromPrimitive, Float, Zero};
use std::ops::{Add, Div};

/// Extension trait for `ArrayBase` providing methods
/// to compute several summary statistics (e.g. mean, variance, etc.).
pub trait SummaryStatisticsExt<A, S, D>
where
S: Data<Elem = A>,
D: Dimension,
{
/// Returns the [`arithmetic mean`] x̅ of all elements in the array:
///
/// ```text
/// 1 n
/// x̅ = ― ∑ xᵢ
/// n i=1
/// ```
///
/// If the array is empty, `None` is returned.
///
/// **Panics** if `A::from_usize()` fails to convert the number of elements in the array.
///
/// [`arithmetic mean`]: https://en.wikipedia.org/wiki/Arithmetic_mean
fn mean(&self) -> Option<A>
where
A: Clone + FromPrimitive + Add<Output=A> + Div<Output=A> + Zero;

/// Returns the [`harmonic mean`] `HM(X)` of all elements in the array:
///
/// ```text
/// ⎛ n ⎞⁻¹
/// HM(X) = n ⎜ ∑ xᵢ⁻¹⎟
/// ⎝i=1 ⎠
/// ```
///
/// If the array is empty, `None` is returned.
///
/// **Panics** if `A::from_usize()` fails to convert the number of elements in the array.
///
/// [`harmonic mean`]: https://en.wikipedia.org/wiki/Harmonic_mean
fn harmonic_mean(&self) -> Option<A>
where
A: Float + FromPrimitive;

/// Returns the [`geometric mean`] `GM(X)` of all elements in the array:
///
/// ```text
/// ⎛ n ⎞¹⁄ₙ
/// GM(X) = ⎜ ∏ xᵢ⎟
/// ⎝i=1 ⎠
/// ```
///
/// If the array is empty, `None` is returned.
///
/// **Panics** if `A::from_usize()` fails to convert the number of elements in the array.
///
/// [`geometric mean`]: https://en.wikipedia.org/wiki/Geometric_mean
fn geometric_mean(&self) -> Option<A>
where
A: Float + FromPrimitive;

}

mod means;