Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
bfb0db7
Use Option as return type where things might fail
LukeMathWalker Jan 29, 2019
4057a72
Test suite aligned with docs
LukeMathWalker Jan 29, 2019
fa1eb34
Equispaced does not panic anymore
Jan 30, 2019
669a33f
Fixed some tests
Jan 30, 2019
0e5eb6b
Fixed FD tests
Jan 30, 2019
04cf0a7
Fixed wrong condition in IF
Jan 30, 2019
4f429bc
Fixed wrong test
Jan 30, 2019
4e74c48
Added new test for EquiSpaced and fixed old one
Jan 30, 2019
56b7e45
Fixed doc tests
Jan 30, 2019
12906fd
Fix docs.
LukeMathWalker Feb 2, 2019
9d1862f
Fix docs.
LukeMathWalker Feb 2, 2019
64789d6
Fix docs.
LukeMathWalker Feb 2, 2019
fe150d1
Fmt
LukeMathWalker Mar 26, 2019
facd4c4
Merge master
LukeMathWalker Mar 26, 2019
b28c35a
Create StrategyError
LukeMathWalker Mar 26, 2019
c06f382
Fmt
LukeMathWalker Mar 26, 2019
4a24f5a
Return Result. Fix Equispaced, Sqrt and Rice
LukeMathWalker Mar 26, 2019
f708a17
Fix Rice
LukeMathWalker Mar 26, 2019
58788db
Fixed Sturges
LukeMathWalker Mar 26, 2019
3014f77
Fix strategies
LukeMathWalker Mar 26, 2019
17e5efc
Fix match
LukeMathWalker Mar 26, 2019
63abed5
Tests compile
LukeMathWalker Mar 26, 2019
4a4b489
Fix assertion
LukeMathWalker Mar 26, 2019
f692887
Fmt
LukeMathWalker Mar 26, 2019
a8ad4b1
Add more error types
jturner314 Mar 31, 2019
29f56f3
Rename StrategyError to BinsBuildError
jturner314 Apr 1, 2019
bca2dc9
Make GridBuilder::from_array return Result
jturner314 Apr 1, 2019
f41b317
Make BinsBuildError enum non-exhaustive
jturner314 Apr 1, 2019
308e0e7
Merge pull request #4 from jturner314/histogram-error-handling
LukeMathWalker Apr 1, 2019
c280c6b
Use lazy OR operator.
LukeMathWalker Apr 1, 2019
6481509
Merge remote-tracking branch 'origin/histogram-error-handling' into h…
LukeMathWalker Apr 1, 2019
701842d
Use lazy OR operator.
LukeMathWalker Apr 1, 2019
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
Prev Previous commit
Next Next commit
Fmt
  • Loading branch information
LukeMathWalker committed Mar 26, 2019
commit fe150d1965e7fc2415b58cd4e0000fb45e64d3ab
96 changes: 41 additions & 55 deletions src/correlation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,15 @@ where
{
let observation_axis = Axis(1);
let n_observations = A::from_usize(self.len_of(observation_axis)).unwrap();
let dof =
if ddof >= n_observations {
panic!("`ddof` needs to be strictly smaller than the \
number of observations provided for each \
random variable!")
} else {
n_observations - ddof
};
let dof = if ddof >= n_observations {
panic!(
"`ddof` needs to be strictly smaller than the \
number of observations provided for each \
random variable!"
)
} else {
n_observations - ddof
};
let mean = self.mean_axis(observation_axis);
let denoised = self - &mean.insert_axis(observation_axis);
let covariance = denoised.dot(&denoised.t());
Expand All @@ -156,7 +157,9 @@ where
// observation per random variable (or no observations at all)
let ddof = -A::one();
let cov = self.cov(ddof);
let std = self.std_axis(observation_axis, ddof).insert_axis(observation_axis);
let std = self
.std_axis(observation_axis, ddof)
.insert_axis(observation_axis);
let std_matrix = std.dot(&std.t());
// element-wise division
cov / std_matrix
Expand All @@ -167,10 +170,10 @@ where
mod cov_tests {
use super::*;
use ndarray::array;
use ndarray_rand::RandomExt;
use quickcheck::quickcheck;
use rand;
use rand::distributions::Uniform;
use ndarray_rand::RandomExt;

quickcheck! {
fn constant_random_variables_have_zero_covariance_matrix(value: f64) -> bool {
Expand Down Expand Up @@ -200,10 +203,7 @@ mod cov_tests {
fn test_invalid_ddof() {
let n_random_variables = 3;
let n_observations = 4;
let a = Array::random(
(n_random_variables, n_observations),
Uniform::new(0., 10.)
);
let a = Array::random((n_random_variables, n_observations), Uniform::new(0., 10.));
let invalid_ddof = (n_observations as f64) + rand::random::<f64>().abs();
a.cov(invalid_ddof);
}
Expand Down Expand Up @@ -235,55 +235,46 @@ mod cov_tests {
#[test]
fn test_covariance_for_random_array() {
let a = array![
[ 0.72009497, 0.12568055, 0.55705966, 0.5959984 , 0.69471457],
[ 0.56717131, 0.47619486, 0.21526298, 0.88915366, 0.91971245],
[ 0.59044195, 0.10720363, 0.76573717, 0.54693675, 0.95923036],
[ 0.24102952, 0.131347, 0.11118028, 0.21451351, 0.30515539],
[ 0.26952473, 0.93079841, 0.8080893 , 0.42814155, 0.24642258]
[0.72009497, 0.12568055, 0.55705966, 0.5959984, 0.69471457],
[0.56717131, 0.47619486, 0.21526298, 0.88915366, 0.91971245],
[0.59044195, 0.10720363, 0.76573717, 0.54693675, 0.95923036],
[0.24102952, 0.131347, 0.11118028, 0.21451351, 0.30515539],
[0.26952473, 0.93079841, 0.8080893, 0.42814155, 0.24642258]
];
let numpy_covariance = array![
[ 0.05786248, 0.02614063, 0.06446215, 0.01285105, -0.06443992],
[ 0.02614063, 0.08733569, 0.02436933, 0.01977437, -0.06715555],
[ 0.06446215, 0.02436933, 0.10052129, 0.01393589, -0.06129912],
[ 0.01285105, 0.01977437, 0.01393589, 0.00638795, -0.02355557],
[-0.06443992, -0.06715555, -0.06129912, -0.02355557, 0.09909855]
[0.05786248, 0.02614063, 0.06446215, 0.01285105, -0.06443992],
[0.02614063, 0.08733569, 0.02436933, 0.01977437, -0.06715555],
[0.06446215, 0.02436933, 0.10052129, 0.01393589, -0.06129912],
[0.01285105, 0.01977437, 0.01393589, 0.00638795, -0.02355557],
[
-0.06443992,
-0.06715555,
-0.06129912,
-0.02355557,
0.09909855
]
];
assert_eq!(a.ndim(), 2);
assert!(
a.cov(1.).all_close(
&numpy_covariance,
1e-8
)
);
assert!(a.cov(1.).all_close(&numpy_covariance, 1e-8));
}

#[test]
#[should_panic]
// We lose precision, hence the failing assert
fn test_covariance_for_badly_conditioned_array() {
let a: Array2<f64> = array![
[ 1e12 + 1., 1e12 - 1.],
[ 1e-6 + 1e-12, 1e-6 - 1e-12],
];
let expected_covariance = array![
[2., 2e-12], [2e-12, 2e-24]
];
assert!(
a.cov(1.).all_close(
&expected_covariance,
1e-24
)
);
let a: Array2<f64> = array![[1e12 + 1., 1e12 - 1.], [1e-6 + 1e-12, 1e-6 - 1e-12],];
let expected_covariance = array![[2., 2e-12], [2e-12, 2e-24]];
assert!(a.cov(1.).all_close(&expected_covariance, 1e-24));
}
}

#[cfg(test)]
mod pearson_correlation_tests {
use super::*;
use ndarray::array;
use ndarray_rand::RandomExt;
use quickcheck::quickcheck;
use rand::distributions::Uniform;
use ndarray_rand::RandomExt;

quickcheck! {
fn output_matrix_is_symmetric(bound: f64) -> bool {
Expand Down Expand Up @@ -337,19 +328,14 @@ mod pearson_correlation_tests {
[0.26979716, 0.20887228, 0.95454999, 0.96290785]
];
let numpy_corrcoeff = array![
[ 1. , 0.38089376, 0.08122504, -0.59931623, 0.1365648 ],
[ 0.38089376, 1. , 0.80918429, -0.52615195, 0.38954398],
[ 0.08122504, 0.80918429, 1. , 0.07134906, -0.17324776],
[-0.59931623, -0.52615195, 0.07134906, 1. , -0.8743213 ],
[ 0.1365648 , 0.38954398, -0.17324776, -0.8743213 , 1. ]
[1., 0.38089376, 0.08122504, -0.59931623, 0.1365648],
[0.38089376, 1., 0.80918429, -0.52615195, 0.38954398],
[0.08122504, 0.80918429, 1., 0.07134906, -0.17324776],
[-0.59931623, -0.52615195, 0.07134906, 1., -0.8743213],
[0.1365648, 0.38954398, -0.17324776, -0.8743213, 1.]
];
assert_eq!(a.ndim(), 2);
assert!(
a.pearson_correlation().all_close(
&numpy_corrcoeff,
1e-7
)
);
assert!(a.pearson_correlation().all_close(&numpy_corrcoeff, 1e-7));
}

}
35 changes: 14 additions & 21 deletions src/histogram/bins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ pub struct Edges<A: Ord> {
}

impl<A: Ord> From<Vec<A>> for Edges<A> {

/// Get an `Edges` instance from a `Vec<A>`:
/// the vector will be sorted in increasing order
/// using an unstable sorting algorithm and duplicates
Expand Down Expand Up @@ -89,7 +88,7 @@ impl<A: Ord + Clone> From<Array1<A>> for Edges<A> {
}
}

impl<A: Ord> Index<usize> for Edges<A>{
impl<A: Ord> Index<usize> for Edges<A> {
type Output = A;

/// Get the `i`-th edge.
Expand Down Expand Up @@ -182,13 +181,11 @@ impl<A: Ord> Edges<A> {
match self.edges.binary_search(value) {
Ok(i) if i == n_edges - 1 => None,
Ok(i) => Some((i, i + 1)),
Err(i) => {
match i {
0 => None,
j if j == n_edges => None,
j => Some((j - 1, j)),
}
}
Err(i) => match i {
0 => None,
j if j == n_edges => None,
j => Some((j - 1, j)),
},
}
}

Expand Down Expand Up @@ -309,18 +306,14 @@ impl<A: Ord> Bins<A> {
/// );
/// ```
pub fn range_of(&self, value: &A) -> Option<Range<A>>
where
A: Clone,
where
A: Clone,
{
let edges_indexes = self.edges.indices_of(value);
edges_indexes.map(
|(left, right)| {
Range {
start: self.edges[left].clone(),
end: self.edges[right].clone(),
}
}
)
edges_indexes.map(|(left, right)| Range {
start: self.edges[left].clone(),
end: self.edges[right].clone(),
})
}

/// Get the `i`-th bin.
Expand All @@ -341,7 +334,7 @@ impl<A: Ord> Bins<A> {
/// );
/// ```
pub fn index(&self, index: usize) -> Range<A>
where
where
A: Clone,
{
// It was not possible to implement this functionality
Expand All @@ -350,7 +343,7 @@ impl<A: Ord> Bins<A> {
// Index, in fact, forces you to return a reference.
Range {
start: self.edges[index].clone(),
end: self.edges[index+1].clone(),
end: self.edges[index + 1].clone(),
}
}
}
Expand Down
31 changes: 20 additions & 11 deletions src/histogram/grid.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::bins::Bins;
use super::strategies::BinsBuildingStrategy;
use std::ops::Range;
use itertools::izip;
use ndarray::{ArrayBase, Data, Ix1, Ix2, Axis};
use ndarray::{ArrayBase, Axis, Data, Ix1, Ix2};
use std::ops::Range;

/// A `Grid` is a partition of a rectangular region of an *n*-dimensional
/// space—e.g. [*a*<sub>0</sub>, *b*<sub>0</sub>) × ⋯ × [*a*<sub>*n*−1</sub>,
Expand Down Expand Up @@ -72,7 +72,6 @@ pub struct Grid<A: Ord> {
}

impl<A: Ord> From<Vec<Bins<A>>> for Grid<A> {

/// Get a `Grid` instance from a `Vec<Bins<A>>`.
///
/// The `i`-th element in `Vec<Bins<A>>` represents the 1-dimensional
Expand Down Expand Up @@ -113,9 +112,14 @@ impl<A: Ord> Grid<A> {
where
S: Data<Elem = A>,
{
assert_eq!(point.len(), self.ndim(),
"Dimension mismatch: the point has {:?} dimensions, the grid \
expected {:?} dimensions.", point.len(), self.ndim());
assert_eq!(
point.len(),
self.ndim(),
"Dimension mismatch: the point has {:?} dimensions, the grid \
expected {:?} dimensions.",
point.len(),
self.ndim()
);
point
.iter()
.zip(self.projections.iter())
Expand All @@ -132,9 +136,14 @@ impl<A: Ord + Clone> Grid<A> {
/// **Panics** if at least one among `(i_0, ..., i_{n-1})` is out of bounds on the respective
/// coordinate axis - i.e. if there exists `j` such that `i_j >= self.projections[j].len()`.
pub fn index(&self, index: &[usize]) -> Vec<Range<A>> {
assert_eq!(index.len(), self.ndim(),
"Dimension mismatch: the index has {0:?} dimensions, the grid \
expected {1:?} dimensions.", index.len(), self.ndim());
assert_eq!(
index.len(),
self.ndim(),
"Dimension mismatch: the index has {0:?} dimensions, the grid \
expected {1:?} dimensions.",
index.len(),
self.ndim()
);
izip!(&self.projections, index)
.map(|(bins, &i)| bins.index(i))
.collect()
Expand Down Expand Up @@ -167,13 +176,13 @@ where
/// [`strategy`]: strategies/index.html
pub fn from_array<S>(array: &ArrayBase<S, Ix2>) -> Option<Self>
where
S: Data<Elem=A>,
S: Data<Elem = A>,
{
let bin_builders: Option<Vec<B>> = array
.axis_iter(Axis(1))
.map(|data| B::from_array(&data))
.collect();
bin_builders.map(|b| Self { bin_builders: b})
bin_builders.map(|b| Self { bin_builders: b })
}

/// Returns a [`Grid`] instance, built accordingly to the specified [`strategy`]
Expand Down
25 changes: 12 additions & 13 deletions src/histogram/histograms.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::errors::BinNotFound;
use super::grid::Grid;
use ndarray::prelude::*;
use ndarray::Data;
use super::grid::Grid;
use super::errors::BinNotFound;

/// Histogram data structure.
pub struct Histogram<A: Ord> {
Expand Down Expand Up @@ -58,8 +58,8 @@ impl<A: Ord> Histogram<A> {
Some(bin_index) => {
self.counts[&*bin_index] += 1;
Ok(())
},
None => Err(BinNotFound)
}
None => Err(BinNotFound),
}
}

Expand All @@ -82,8 +82,8 @@ impl<A: Ord> Histogram<A> {

/// Extension trait for `ArrayBase` providing methods to compute histograms.
pub trait HistogramExt<A, S>
where
S: Data<Elem = A>,
where
S: Data<Elem = A>,
{
/// Returns the [histogram](https://en.wikipedia.org/wiki/Histogram)
/// for a 2-dimensional array of points `M`.
Expand Down Expand Up @@ -145,17 +145,16 @@ pub trait HistogramExt<A, S>
/// # }
/// ```
fn histogram(&self, grid: Grid<A>) -> Histogram<A>
where
A: Ord;
where
A: Ord;
}

impl<A, S> HistogramExt<A, S> for ArrayBase<S, Ix2>
where
S: Data<Elem = A>,
A: Ord,
where
S: Data<Elem = A>,
A: Ord,
{
fn histogram(&self, grid: Grid<A>) -> Histogram<A>
{
fn histogram(&self, grid: Grid<A>) -> Histogram<A> {
let mut histogram = Histogram::new(grid);
for point in self.axis_iter(Axis(0)) {
let _ = histogram.add_observation(&point);
Expand Down
10 changes: 5 additions & 5 deletions src/histogram/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! Histogram functionalities.
pub use self::histograms::{Histogram, HistogramExt};
pub use self::bins::{Edges, Bins};
pub use self::bins::{Bins, Edges};
pub use self::grid::{Grid, GridBuilder};
pub use self::histograms::{Histogram, HistogramExt};

mod histograms;
mod bins;
pub mod strategies;
mod grid;
pub mod errors;
mod grid;
mod histograms;
pub mod strategies;
Loading