Skip to content

Histogram #8

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

Closed
wants to merge 72 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
89198b1
Added new submodule
LukeMathWalker Oct 1, 2018
1d019f1
Barebone trait HistogramExt added and exported
LukeMathWalker Oct 1, 2018
231d2bf
New module for bin functionalities
LukeMathWalker Oct 1, 2018
bedd5d3
Skeleton of the bin system
LukeMathWalker Oct 1, 2018
3bd8dc1
Bins have to be generic with respect to element type T
LukeMathWalker Oct 1, 2018
979ae11
Added barebone of histogram trait, both 1d and Nd
LukeMathWalker Oct 1, 2018
f3f02e0
As far as I can go with this structure
LukeMathWalker Oct 1, 2018
d2b29d1
Getting rid of Bin and Bins traits, for now
LukeMathWalker Oct 1, 2018
2806eaa
Added bounds to allow storage in HashMap
LukeMathWalker Oct 1, 2018
28ef955
Let's ignore code duplication for now - histogram 1d implemented
LukeMathWalker Oct 1, 2018
dbc4993
Restructured as submodules
LukeMathWalker Oct 1, 2018
a3358ea
Constructors have been implemented for BinNd and BinsNd
LukeMathWalker Oct 1, 2018
f6b88ad
Implemented Display for Bins in order to show proper error messages
LukeMathWalker Oct 1, 2018
c3ab8a8
ndim method has to be public
LukeMathWalker Oct 1, 2018
8d43d5e
Added Clone bound
LukeMathWalker Oct 1, 2018
eda210e
Implemented find method. Added clone bound
LukeMathWalker Oct 1, 2018
1ac42e4
Added contains method to Bin1d
LukeMathWalker Oct 1, 2018
0bea875
Taking a reference, instead of moving value
LukeMathWalker Oct 1, 2018
edfc413
Implemented contains for BinNd
LukeMathWalker Oct 1, 2018
af62d85
Added contains method implementation for Bin1d
LukeMathWalker Oct 1, 2018
3f0aacc
Using std::ops::Range* to implement Bin1d as an enum
LukeMathWalker Oct 2, 2018
bf9965e
Implemented find
LukeMathWalker Oct 2, 2018
641ed57
Fixed docs
LukeMathWalker Oct 2, 2018
4ea33b7
Unglobed import
LukeMathWalker Oct 2, 2018
e856951
Rexporting functions
LukeMathWalker Oct 2, 2018
782fb1b
Added example in docs
LukeMathWalker Oct 2, 2018
ac9d924
Added docstring to contains method
LukeMathWalker Oct 2, 2018
be08f35
Test time!
LukeMathWalker Oct 2, 2018
50db577
Testing Bins1d
LukeMathWalker Oct 2, 2018
de277ed
Minimizing trait bounds
LukeMathWalker Oct 2, 2018
eda63b2
Remove unneeded trait bound
LukeMathWalker Oct 2, 2018
28f28d9
Added docs + doc test + relaxed bound on array in contains
LukeMathWalker Oct 2, 2018
df4984a
Fixed typo.
LukeMathWalker Oct 2, 2018
185bcf6
Fixed typo
LukeMathWalker Oct 2, 2018
367d14c
Better formatting for docs
LukeMathWalker Oct 2, 2018
7f80e8f
Better wording
LukeMathWalker Oct 2, 2018
bd3c2de
Allow point to be any type of iterator as long as it has a length
LukeMathWalker Oct 2, 2018
a4e2cb6
Documented contains. Added a panic case
LukeMathWalker Oct 2, 2018
d431992
Added doc-test to contains for BinNd
LukeMathWalker Oct 2, 2018
e684d9e
Better formatting in docs
LukeMathWalker Oct 2, 2018
74bda89
Fixing wrong doc in ndim for BinsNd
LukeMathWalker Oct 2, 2018
87cdfc8
Added docstrings to ndim for BinNd
LukeMathWalker Oct 2, 2018
5438acf
Added correct docs to BinsNd::new
LukeMathWalker Oct 2, 2018
3ffb62e
Added one more panic case to BinsNd::new
LukeMathWalker Oct 2, 2018
c3b3a50
Formatting
LukeMathWalker Oct 2, 2018
4f5fbb9
Details on overlapping case for BinsNd::find
LukeMathWalker Oct 2, 2018
84c56e8
Improved docs for Bins1d
LukeMathWalker Oct 2, 2018
3e37267
Too much effort - let's use arrays
LukeMathWalker Oct 3, 2018
546dea5
Fixed doc
LukeMathWalker Oct 3, 2018
8325807
Added error message if it panics
LukeMathWalker Oct 3, 2018
d2fa588
Adding IDE-related files to .gitignore
LukeMathWalker Oct 3, 2018
ba80459
Fixed doc-test, added new test
LukeMathWalker Oct 3, 2018
f799c8b
Added new test for BinNd - dimension mismatch in contains
LukeMathWalker Oct 3, 2018
955c04b
Added test for contains w matching dimensions
LukeMathWalker Oct 3, 2018
addd56f
Rename test module
LukeMathWalker Oct 3, 2018
ac5520a
Added test for BinsNd - empty vec to new
LukeMathWalker Oct 3, 2018
e0a3cab
Fixed dim computation in BinsNd::new - added a test for it
LukeMathWalker Oct 3, 2018
03d79a1
Remove fmt::Debug bound on T given that is not used anymore
LukeMathWalker Oct 3, 2018
85bab2f
Added new test for find with mismatched dimensions
LukeMathWalker Oct 3, 2018
b61a6aa
Improved test
LukeMathWalker Oct 3, 2018
8306259
Added new test for BinsNd::find
LukeMathWalker Oct 3, 2018
d597f15
Histogram is now updated correctly
LukeMathWalker Oct 3, 2018
f42aa48
Added a test for histogram
LukeMathWalker Oct 3, 2018
2ac7357
Fixed doc for HistogramNdExt and added a test for the panic case
LukeMathWalker Oct 3, 2018
2758e01
Added docstring to Histogram1dExt::histogram
LukeMathWalker Oct 3, 2018
7713089
Hyperlinks everywhere
LukeMathWalker Oct 3, 2018
018840e
Hyperlinks everywhere
LukeMathWalker Oct 3, 2018
07803b8
Docstring for traits in histogram.mod
LukeMathWalker Oct 3, 2018
3616a97
Missing one / to show up in docs
LukeMathWalker Oct 3, 2018
aa46698
Encapsulated bins in bins module
LukeMathWalker Oct 3, 2018
2ec7af4
Fixed tests
LukeMathWalker Oct 3, 2018
d2f4beb
Added module docs
LukeMathWalker Oct 3, 2018
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/target
**/*.rs.bk
Cargo.lock

# IDE and similar
rusty-tags.vi
tags
.vscode/*
5 changes: 5 additions & 0 deletions src/histogram/bins/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub use self::one_dim::{Bin1d, Bins1d};
pub use self::n_dim::{BinNd, BinsNd};

mod one_dim;
mod n_dim;
265 changes: 265 additions & 0 deletions src/histogram/bins/n_dim.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
use ndarray::prelude::*;
use ndarray::Data;
use std::ops::Index;
use std::fmt;
use histogram::bins::Bin1d;

/// `n`-dimensional bin: `I_1xI_2x..xI_n` where
/// `I_k` is a one-dimensional interval ([`Bin1d`]).
///
/// It is instantiated by specifying the ordered sequence
/// of its 1-dimensional projections on the coordinate axes.
///
/// # Example
///
/// ```
/// #[macro_use(array)]
/// extern crate ndarray;
/// extern crate ndarray_stats;
/// extern crate noisy_float;
/// use noisy_float::types::n64;
/// use ndarray_stats::bins::{BinNd, Bin1d};
///
/// # fn main() {
/// let projections = vec![
/// Bin1d::RangeInclusive(n64(0.)..=n64(1.)),
/// Bin1d::RangeInclusive(n64(0.)..=n64(1.)),
/// ];
/// let unit_square = BinNd::new(projections);
/// let point = array![n64(0.5), n64(0.5)];
/// assert!(unit_square.contains(point.view()));
/// # }
/// ```
///
/// [`Bin1d`]: enum.Bin1d.html
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
pub struct BinNd<T> {
projections: Vec<Bin1d<T>>,
}

impl<T> fmt::Display for BinNd<T>
where
T: fmt::Debug
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let repr = self.projections.iter().map(
|p| format!("{}", p)
).collect::<Vec<String>>().join("x");
write!(f, "{}", repr)
}
}

impl<T> BinNd<T>
where
T: fmt::Debug
{
/// Creates a new instance of `BinNd` from the ordered sequence
/// of its 1-dimensional projections on the coordinate axes.
///
/// **Panics** if `projections` is empty.
pub fn new(projections: Vec<Bin1d<T>>) -> Self {
if projections.is_empty() {
panic!(
"The 1-dimensional projections of an n-dimensional
bin can't be empty!"
)
} else {
Self { projections }
}
}
}

impl<T> BinNd<T>
{
/// Return `n`, the number of dimensions.
pub fn ndim(&self) -> usize {
self.projections.len()
}
}

impl<'a, T: 'a> BinNd<T>
where
T: PartialOrd + fmt::Debug,
{
/// Return `true` if `point` is in the bin, `false` otherwise.
///
/// **Panics** if `point`'s dimensionality
/// (`point.len()`) is different from `self.ndim()`.
///
/// # Example
///
/// ```
/// #[macro_use(array)]
/// extern crate ndarray;
/// extern crate ndarray_stats;
/// extern crate noisy_float;
/// use noisy_float::types::n64;
/// use ndarray_stats::bins::{BinNd, Bin1d};
///
/// # fn main() {
/// let projections = vec![
/// Bin1d::RangeFrom(n64(0.)..),
/// Bin1d::RangeFrom(n64(0.)..),
/// ];
/// let first_quadrant = BinNd::new(projections);
/// let good_point = array![n64(1e6), n64(1e8)];
/// let bad_point = array![n64(-1.), n64(0.)];
/// assert!(first_quadrant.contains(good_point.view()));
/// assert!(!first_quadrant.contains(bad_point.view()));
/// # }
/// ```
pub fn contains<S>(&self, point: ArrayBase<S, Ix1>) -> bool
where
S: Data<Elem=T>,
{
assert_eq!(point.len(), self.ndim(),
"Dimensionalities do not match. Point {0:?} has {1} dimensions. \
Bin {2:?} has {3} dimensions", point, point.len(), self, self.ndim());
point.iter().zip(self.projections.iter()).
map(|(element, projection)| projection.contains(element)).
fold(true, |acc, v| acc & v)
}
}

/// `BinsNd` is a collection of sub-regions ([`BinNd`])
/// in an `n`-dimensional space.
///
/// It is not required (or enforced) that the sub-regions
/// in `self` must be not-overlapping.
///
/// [`BinNd`]: struct.BinNd.html
#[derive(Clone, Debug)]
pub struct BinsNd<T> {
bins: Vec<BinNd<T>>,
ndim: usize,
}

impl<T> BinsNd<T>
{
/// Creates a new instance of `BinsNd` from a vector
/// of `BinNd`.
///
/// **Panics** if `bins` is empty or if there are two bins in `bins`
/// with different dimensionality.
pub fn new(bins: Vec<BinNd<T>>) -> Self {
assert!(!bins.is_empty(), "The bins collection cannot be empty!");
// All bins must have the same number of dimensions!
let ndim = {
let ndim = bins.index(0).ndim();
let flag = &bins.iter().
map(|b| b.ndim() == ndim).
fold(true, |acc, new| acc & new);
// It would be better to print the bad bins as well!
assert!(flag, "There at least two bins with different \
number of dimensions");
ndim
};
Self { bins, ndim }
}

/// Return `n`, the number of dimensions.
pub fn ndim(&self) -> usize {
self.ndim
}
}

impl<'a, T: 'a> BinsNd<T>
where
T: PartialOrd + Clone + fmt::Debug,
{
/// Given a point `P`, it returns an `Option`:
/// - `Some(B)`, if `P` belongs to the `Bin` `B`;
/// - `None`, if `P` does not belong to any `Bin` in `self`.
///
/// If more than one bin in `self` contains `P`, no assumptions
/// can be made on which bin will be returned by `find`.
///
/// **Panics** if `P.ndim()` is different from `Bins.ndim()`.
pub fn find<S>(&self, point: ArrayBase<S, Ix1>) -> Option<BinNd<T>>
where
S: Data<Elem=T>,
{
for bin in self.bins.iter() {
if bin.contains(point.view()){
return Some((*bin).clone())
}
}
None
}
}

#[cfg(test)]
mod bin_nd_tests {
use super::*;

#[test]
#[should_panic]
fn new_w_empty_vec() {
let _: BinNd<i32> = BinNd::new(vec![]);
}

#[test]
#[should_panic]
fn contains_w_dimensions_mismatch() {
let bin2d = BinNd::new(
vec![Bin1d::Range(0..1), Bin1d::Range(2..4)]
);
let point1d = array![1];
bin2d.contains(point1d.view());
}

#[test]
fn contains_w_matching_dimensions() {
let bin2d = BinNd::new(
vec![Bin1d::Range(0..1), Bin1d::Range(2..4)]
);
let point2d = array![0, 3];
bin2d.contains(point2d.view());
}
}

mod bins_nd_tests {
use super::*;

#[test]
#[should_panic]
fn new_w_empty_vec() {
let _: BinsNd<i32> = BinsNd::new(vec![]);
}

#[test]
#[should_panic]
fn new_w_bins_of_different_dimensions() {
let bin1d = BinNd::new(vec![Bin1d::Range(0..5)]);
let bin2d = BinNd::new(vec![Bin1d::Range(0..5),
Bin1d::RangeFrom(2..)]);
assert_eq!(bin1d.ndim(), 1);
assert_eq!(bin2d.ndim(), 2);
let _: BinsNd<i32> = BinsNd::new(
vec![bin1d, bin2d],
);
}

#[test]
#[should_panic]
fn find_w_mismatched_dimensions() {
let bin2d = BinNd::new(vec![Bin1d::Range(0..5),
Bin1d::RangeFrom(2..)]);
assert_eq!(bin2d.ndim(), 2);
let point3d = array![1, 2, 3];
assert_eq!(point3d.len(), 3);
let bins = BinsNd::new(vec![bin2d]);
bins.find(point3d);
}

#[test]
fn find_w_matching_dimensions() {
let bin = BinNd::new(vec![Bin1d::Range(0..5),
Bin1d::RangeFrom(2..)]);
let bins = BinsNd::new(vec![bin.clone()]);
let p = array![1, 2];
let q = array![1, 1];
assert_eq!(bins.find(p), Some(bin));
assert_eq!(bins.find(q), None);
}
}
Loading