Skip to content

Commit 223ce08

Browse files
committed
histogram/grid.rs: Rewriting some docs
- More documentations are added. - Also adjusted some formatting.
1 parent 03c780f commit 223ce08

File tree

1 file changed

+120
-61
lines changed

1 file changed

+120
-61
lines changed

src/histogram/grid.rs

Lines changed: 120 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,61 @@ use itertools::izip;
55
use ndarray::{ArrayBase, Axis, Data, Ix1, Ix2};
66
use std::ops::Range;
77

8-
/// A `Grid` is a partition of a rectangular region of an *n*-dimensional
9-
/// space—e.g. [*a*<sub>0</sub>, *b*<sub>0</sub>) × ⋯ × [*a*<sub>*n*−1</sub>,
10-
/// *b*<sub>*n*−1</sub>)—into a collection of rectangular *n*-dimensional bins.
8+
/// An orthogonal partition of a rectangular region in an *n*-dimensional space, e.g.
9+
/// [*a*<sub>0</sub>, *b*<sub>0</sub>) × ⋯ × [*a*<sub>*n*−1</sub>, *b*<sub>*n*−1</sub>),
10+
/// represented as a collection of rectangular *n*-dimensional bins.
11+
///
12+
/// The grid is **solely determined by the Cartesian product of its projections** on each coordinate
13+
/// axis. Therefore, each element in the product set should correspond to a sub-region in the grid.
14+
///
15+
/// For example, this partition can be represented as a `Grid` struct:
1116
///
12-
/// The grid is **fully determined by its 1-dimensional projections** on the
13-
/// coordinate axes. For example, this is a partition that can be represented
14-
/// as a `Grid` struct:
1517
/// ```text
16-
/// +---+-------+-+
17-
/// | | | |
18-
/// +---+-------+-+
19-
/// | | | |
20-
/// | | | |
21-
/// | | | |
22-
/// | | | |
23-
/// +---+-------+-+
18+
///
19+
/// g +---+-------+---+
20+
/// | 3 | 4 | 5 |
21+
/// f +---+-------+---+
22+
/// | | | |
23+
/// | 0 | 1 | 2 |
24+
/// | | | |
25+
/// e +---+-------+---+
26+
/// a b c d
27+
///
28+
/// R0: [a, b) × [e, f)
29+
/// R1: [b, c) × [e, f)
30+
/// R2: [c, d) × [e, f)
31+
/// R3: [a, b) × [f, g)
32+
/// R4: [b, d) × [f, g)
33+
/// R5: [c, d) × [f, g)
34+
/// Grid: { [a, b), [b, c), [c, d) } × { [e, f), [f, g) } == { R0, R1, R2, R3, R4, R5 }
2435
/// ```
36+
///
2537
/// while the next one can't:
38+
///
2639
/// ```text
27-
/// +---+-------+-+
28-
/// | | | |
29-
/// | +-------+-+
30-
/// | | |
31-
/// | | |
32-
/// | | |
33-
/// | | |
34-
/// +---+-------+-+
40+
/// g +---+-----+---+
41+
/// | | 2 | 3 |
42+
/// (f) | +-----+---+
43+
/// | 0 | |
44+
/// | | 1 |
45+
/// | | |
46+
/// e +---+-----+---+
47+
/// a b c d
48+
///
49+
/// R0: [a, b) × [e, g)
50+
/// R1: [b, d) × [e, f)
51+
/// R2: [b, c) × [f, g)
52+
/// R3: [c, d) × [f, g)
53+
/// // 'f', as long as 'R1', 'R2', or 'R3', doesn't appear on LHS
54+
/// // [b, c) × [e, g), [c, d) × [e, g) doesn't appear on RHS
55+
/// Grid: { [a, b), [b, c), [c, d) } × { [e, g) } != { R0, R1, R2, R3 }
3556
/// ```
3657
///
3758
/// # Examples
3859
///
60+
/// Basic usage, building a `Grid` via [`GridBuilder`], with optimal grid layout determined by
61+
/// a given [`strategy`], and genrating a [`histogram`]:
62+
///
3963
/// ```
4064
/// use ndarray::{Array, array};
4165
/// use ndarray_stats::{
@@ -44,17 +68,14 @@ use std::ops::Range;
4468
/// };
4569
/// use noisy_float::types::{N64, n64};
4670
///
47-
/// // 1-dimensional observations, as a (n_observations, 1) 2-d matrix
71+
/// // 1-dimensional observations, as a (n_observations, n_dimension) 2-d matrix
4872
/// let observations = Array::from_shape_vec(
4973
/// (12, 1),
5074
/// vec![1, 4, 5, 2, 100, 20, 50, 65, 27, 40, 45, 23],
5175
/// ).unwrap();
5276
///
53-
/// // The optimal grid layout is inferred from the data,
54-
/// // specifying a strategy (Auto in this case)
77+
/// // The optimal grid layout is inferred from the data, given a chosen strategy, Auto in this case
5578
/// let grid = GridBuilder::<Auto<usize>>::from_array(&observations).unwrap().build();
56-
/// let expected_grid = Grid::from(vec![Bins::new(Edges::from(vec![1, 20, 39, 58, 77, 96, 115]))]);
57-
/// assert_eq!(grid, expected_grid);
5879
///
5980
/// let histogram = observations.histogram(grid);
6081
///
@@ -63,19 +84,22 @@ use std::ops::Range;
6384
/// let expected = array![4, 3, 3, 1, 0, 1];
6485
/// assert_eq!(histogram_matrix, expected.into_dyn());
6586
/// ```
87+
///
88+
/// [`histogram`]: trait.HistogramExt.html
89+
/// [`GridBuilder`]: struct.GridBuilder.html
90+
/// [`strategy`]: strategies/index.html
6691
#[derive(Clone, Debug, Eq, PartialEq)]
6792
pub struct Grid<A: Ord> {
6893
projections: Vec<Bins<A>>,
6994
}
7095

7196
impl<A: Ord> From<Vec<Bins<A>>> for Grid<A> {
72-
/// Get a `Grid` instance from a `Vec<Bins<A>>`.
97+
/// Converts a `Vec<Bins<A>>` into a `Grid<A>`, consuming the vector of bins.
7398
///
74-
/// The `i`-th element in `Vec<Bins<A>>` represents the 1-dimensional
75-
/// projection of the bin grid on the `i`-th axis.
99+
/// The `i`-th element in `Vec<Bins<A>>` represents the projection of the bin grid onto the
100+
/// `i`-th axis.
76101
///
77-
/// Alternatively, a `Grid` can be built directly from data using a
78-
/// [`GridBuilder`].
102+
/// Alternatively, a `Grid` can be built directly from data using a [`GridBuilder`].
79103
///
80104
/// [`GridBuilder`]: struct.GridBuilder.html
81105
fn from(projections: Vec<Bins<A>>) -> Self {
@@ -84,7 +108,7 @@ impl<A: Ord> From<Vec<Bins<A>>> for Grid<A> {
84108
}
85109

86110
impl<A: Ord> Grid<A> {
87-
/// Returns `n`, the number of dimensions of the region partitioned by the grid.
111+
/// Returns the number of dimensions of the region partitioned by the grid.
88112
///
89113
/// # Examples
90114
///
@@ -101,7 +125,7 @@ impl<A: Ord> Grid<A> {
101125
self.projections.len()
102126
}
103127

104-
/// Returns the number of bins along each coordinate axis.
128+
/// Returns the numbers of bins along each coordinate axis.
105129
///
106130
/// # Examples
107131
///
@@ -120,19 +144,19 @@ impl<A: Ord> Grid<A> {
120144
self.projections.iter().map(|e| e.len()).collect()
121145
}
122146

123-
/// Returns the grid projections on the coordinate axes as a slice of immutable references.
147+
/// Returns the grid projections on each coordinate axis as a slice of immutable references.
124148
pub fn projections(&self) -> &[Bins<A>] {
125149
&self.projections
126150
}
127151

128-
/// Returns the index of the *n*-dimensional bin containing the point, if
129-
/// one exists.
152+
/// Returns an `n-dimensional` index, of bins along each axis that contains the point, if one
153+
/// exists.
130154
///
131155
/// Returns `None` if the point is outside the grid.
132156
///
133157
/// # Panics
134158
///
135-
/// Panics if `point.len()` does not equal `self.ndim()`.
159+
/// Panics if dimensionality of the point doesn't equal the grid's.
136160
///
137161
/// # Examples
138162
///
@@ -147,18 +171,19 @@ impl<A: Ord> Grid<A> {
147171
/// let bins = Bins::new(edges);
148172
/// let square_grid = Grid::from(vec![bins.clone(), bins.clone()]);
149173
///
150-
/// // `0.` and `-0.7` falls in 1-st and 0-th bin respectively
174+
/// // (0., -0.7) falls in 1st and 0th bin respectively
151175
/// assert_eq!(
152176
/// square_grid.index_of(&array![n64(0.), n64(-0.7)]),
153177
/// Some(vec![1, 0]),
154178
/// );
155-
/// // `1.` is outside of the grid, return `None`
179+
/// // Returns `None`, as `1.` is outside the grid since bins are right exclusive
156180
/// assert_eq!(
157181
/// square_grid.index_of(&array![n64(0.), n64(1.)]),
158182
/// None,
159183
/// );
160184
/// ```
161-
/// A panic upon incompatible `point` length:
185+
///
186+
/// A panic upon dimensionality mismatch:
162187
///
163188
/// ```should_panic
164189
/// # use ndarray::array;
@@ -167,8 +192,8 @@ impl<A: Ord> Grid<A> {
167192
/// # let edges = Edges::from(vec![n64(-1.), n64(0.), n64(1.)]);
168193
/// # let bins = Bins::new(edges);
169194
/// # let square_grid = Grid::from(vec![bins.clone(), bins.clone()]);
195+
/// // the point has 3 dimensions, the grid expected 2 dimensions
170196
/// assert_eq!(
171-
/// // the point has 3 dimensions, the grid expected 2 dimensions
172197
/// square_grid.index_of(&array![n64(0.), n64(-0.7), n64(0.5)]),
173198
/// Some(vec![1, 0, 1]),
174199
/// );
@@ -194,14 +219,15 @@ impl<A: Ord> Grid<A> {
194219
}
195220

196221
impl<A: Ord + Clone> Grid<A> {
197-
/// Given `i=(i_0, ..., i_{n-1})`, an `n`-dimensional index, it returns
198-
/// `I_{i_0}x...xI_{i_{n-1}}`, an `n`-dimensional bin, where `I_{i_j}` is
199-
/// the `i_j`-th interval on the `j`-th projection of the grid on the coordinate axes.
222+
/// Given an `n`-dimensional index, `i = (i_0, ..., i_{n-1})`, returns an `n`-dimensional bin,
223+
/// `I_{i_0} x ... x I_{i_{n-1}}`, where `I_{i_j}` is the `i_j`-th interval on the `j`-th
224+
/// projection of the grid on the coordinate axes.
200225
///
201226
/// # Panics
202227
///
203-
/// Panics if at least one among `(i_0, ..., i_{n-1})` is out of bounds on the respective
204-
/// coordinate axis - i.e. if there exists `j` such that `i_j >= self.projections[j].len()`.
228+
/// Panics if at least one in the index, `(i_0, ..., i_{n-1})`, is out of bound on the
229+
/// corresponding coordinate axis, i.e. if there exists `j` s.t.
230+
/// `i_j >= self.projections[j].len()`.
205231
///
206232
/// # Examples
207233
///
@@ -211,15 +237,16 @@ impl<A: Ord + Clone> Grid<A> {
211237
/// use ndarray::array;
212238
/// use ndarray_stats::histogram::{Edges, Bins, Grid};
213239
///
214-
/// let edges_x = Edges::from(vec![0, 1, 2]);
215-
/// let edges_y = Edges::from(vec![3, 4, 5]);
240+
/// let edges_x = Edges::from(vec![0, 1]);
241+
/// let edges_y = Edges::from(vec![2, 3, 4]);
216242
/// let bins_x = Bins::new(edges_x);
217243
/// let bins_y = Bins::new(edges_y);
218244
/// let square_grid = Grid::from(vec![bins_x, bins_y]);
219245
///
246+
/// // Query the 0-th bin on x-axis, and 1-st bin on y-axis
220247
/// assert_eq!(
221248
/// square_grid.index(&[0, 1]),
222-
/// vec![0..1, 4..5],
249+
/// vec![0..1, 3..4],
223250
/// );
224251
/// ```
225252
///
@@ -228,15 +255,15 @@ impl<A: Ord + Clone> Grid<A> {
228255
/// ```should_panic
229256
/// # use ndarray::array;
230257
/// # use ndarray_stats::histogram::{Edges, Bins, Grid};
231-
/// # let edges_x = Edges::from(vec![0, 1, 2]);
232-
/// # let edges_y = Edges::from(vec![3, 4, 5]);
258+
/// # let edges_x = Edges::from(vec![0, 1]);
259+
/// # let edges_y = Edges::from(vec![2, 3, 4]);
233260
/// # let bins_x = Bins::new(edges_x);
234261
/// # let bins_y = Bins::new(edges_y);
235262
/// # let square_grid = Grid::from(vec![bins_x, bins_y]);
263+
/// // out-of-bound on y-axis
236264
/// assert_eq!(
237-
/// // out-of-bound on `edges_y`
238265
/// square_grid.index(&[0, 2]),
239-
/// vec![0..1, 1..2],
266+
/// vec![0..1, 3..4],
240267
/// );
241268
/// ```
242269
pub fn index(&self, index: &[usize]) -> Vec<Range<A>> {
@@ -254,8 +281,32 @@ impl<A: Ord + Clone> Grid<A> {
254281
}
255282
}
256283

257-
/// Given a [`strategy`] and some observations, returns a [`Grid`] instance for [`histogram`]
258-
/// computation.
284+
/// A builder used to create [`Grid`] instances for [`histogram`] computations.
285+
///
286+
/// # Examples
287+
///
288+
/// Basic usage, creating a `Grid` with some observations and a given [`strategy`]:
289+
///
290+
/// ```
291+
/// use ndarray::{Array, array};
292+
/// use noisy_float::types::{N64, n64};
293+
/// use ndarray_stats::histogram::{
294+
/// strategies::Auto, Bins, Edges, Grid, GridBuilder, Histogram,
295+
/// };
296+
///
297+
/// // 1-dimensional observations, as a (n_observations, n_dimension) 2-d matrix
298+
/// let observations = Array::from_shape_vec(
299+
/// (12, 1),
300+
/// vec![1, 4, 5, 2, 100, 20, 50, 65, 27, 40, 45, 23],
301+
/// ).unwrap();
302+
///
303+
/// // The optimal grid layout is inferred from the data, given a chosen strategy, Auto in this case
304+
/// let grid = GridBuilder::<Auto<usize>>::from_array(&observations).unwrap().build();
305+
/// // Equivalently, build a Grid directly
306+
/// let expected_grid = Grid::from(vec![Bins::new(Edges::from(vec![1, 20, 39, 58, 77, 96, 115]))]);
307+
///
308+
/// assert_eq!(grid, expected_grid);
309+
/// ```
259310
///
260311
/// [`Grid`]: struct.Grid.html
261312
/// [`histogram`]: trait.HistogramExt.html
@@ -269,18 +320,22 @@ where
269320
A: Ord,
270321
B: BinsBuildingStrategy<Elem = A>,
271322
{
272-
/// Given some observations in a 2-dimensional array with shape `(n_observations, n_dimension)`
273-
/// it returns a `GridBuilder` instance that has learned the required parameter
274-
/// to build a [`Grid`] according to the specified [`strategy`].
323+
/// Returns a `GridBuilder` for building a [`Grid`] with a given [`strategy`] and some
324+
/// observations in a 2-dimensionalarray with shape `(n_observations, n_dimension)`.
275325
///
276326
/// # Errors
277327
///
278328
/// It returns [`BinsBuildError`] if it is not possible to build a [`Grid`] given
279329
/// the observed data according to the chosen [`strategy`].
280330
///
331+
/// # Examples
332+
///
333+
/// See [Trait-level examples] for basic usage.
334+
///
281335
/// [`Grid`]: struct.Grid.html
282336
/// [`strategy`]: strategies/index.html
283337
/// [`BinsBuildError`]: errors/enum.BinsBuildError.html
338+
/// [Trait-level examples]: struct.GridBuilder.html#examples
284339
pub fn from_array<S>(array: &ArrayBase<S, Ix2>) -> Result<Self, BinsBuildError>
285340
where
286341
S: Data<Elem = A>,
@@ -292,8 +347,12 @@ where
292347
Ok(Self { bin_builders })
293348
}
294349

295-
/// Returns a [`Grid`] instance, built accordingly to the specified [`strategy`]
296-
/// using the parameters inferred from observations in [`from_array`].
350+
/// Returns a [`Grid`] instance, with building parameters infered in [`from_array`], according
351+
/// to the specified [`strategy`] and observations provided.
352+
///
353+
/// # Examples
354+
///
355+
/// See [Trait-level examples] for basic usage.
297356
///
298357
/// [`Grid`]: struct.Grid.html
299358
/// [`strategy`]: strategies/index.html

0 commit comments

Comments
 (0)