Skip to content
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

Means #20

Merged
merged 10 commits into from
Jan 10, 2019
Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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;
106 changes: 106 additions & 0 deletions src/summary_statistics/means.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use ndarray::{Data, Dimension, ArrayBase};
use num_traits::{FromPrimitive, Float, Zero};
use std::result::Result;
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) -> Result<A, &'static str>
LukeMathWalker marked this conversation as resolved.
Show resolved Hide resolved
where
A: Clone + FromPrimitive + Add<Output=A> + Div<Output=A> + PartialEq + Zero
LukeMathWalker marked this conversation as resolved.
Show resolved Hide resolved
{
let n_elements = self.len();
if n_elements == 0 {
Err("The mean of an empty array is not defined.")
} else {
let n_elements = A::from_usize(n_elements)
.expect("Converting number of elements to `A` must not fail.");
Ok(self.sum() / n_elements)
}
}

fn harmonic_mean(&self) -> Result<A, &'static str>
where
A: Float + FromPrimitive + PartialEq,
{
if self.len() == 0 {
LukeMathWalker marked this conversation as resolved.
Show resolved Hide resolved
Err("The harmonic mean of an empty array is not defined.")
} else {
Ok(self.map(|x| x.recip()).mean()?.recip())
}
}

fn geometric_mean(&self) -> Result<A, &'static str>
where
A: Float + FromPrimitive + PartialEq,
{
if self.len() == 0 {
LukeMathWalker marked this conversation as resolved.
Show resolved Hide resolved
Err("The geometric mean of an empty array is not defined.")
} else {
Ok(self.map(|x| x.ln()).mean()?.exp())
}
}
}

#[cfg(test)]
mod tests {
use super::*;
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_err());
assert!(a.harmonic_mean().is_err());
assert!(a.geometric_mean().is_err());
}

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

#[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);
}
}
61 changes: 61 additions & 0 deletions src/summary_statistics/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! Summary statistics (e.g. mean, variance, etc.).
use ndarray::{Data, Dimension};
use num_traits::{FromPrimitive, Float, Zero};
use std::result::Result;
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, an `Err` is returned.
///
/// [`arithmetic mean`]: https://en.wikipedia.org/wiki/Arithmetic_mean
fn mean(&self) -> Result<A, &'static str>
where
A: Clone + FromPrimitive + Add<Output=A> + Div<Output=A> + PartialEq + Zero;

/// Returns the [`harmonic mean`] `HM(X)` of all elements in the array:
///
/// ```text
/// n
LukeMathWalker marked this conversation as resolved.
Show resolved Hide resolved
/// HM(X) = n ( ∑ xᵢ⁻¹)⁻¹
/// i=1
/// ```
///
/// If the array is empty, an `Err` is returned.
///
/// [`harmonic mean`]: https://en.wikipedia.org/wiki/Harmonic_mean
fn harmonic_mean(&self) -> Result<A, &'static str>
where
A: Float + FromPrimitive + PartialEq;

/// Returns the [`geometric mean`] `GM(X)` of all elements in the array:
///
/// ```text
/// n
LukeMathWalker marked this conversation as resolved.
Show resolved Hide resolved
/// GM(X) = ( Π xᵢ)^(1/n)
/// i=1
/// ```
///
/// If the array is empty, an `Err` is returned.
///
/// [`geometric mean`]: https://en.wikipedia.org/wiki/Geometric_mean
fn geometric_mean(&self) -> Result<A, &'static str>
where
A: Float + FromPrimitive + PartialEq;
}

mod means;