@@ -5,37 +5,61 @@ use itertools::izip;
5
5
use ndarray:: { ArrayBase , Axis , Data , Ix1 , Ix2 } ;
6
6
use std:: ops:: Range ;
7
7
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:
11
16
///
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:
15
17
/// ```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 }
24
35
/// ```
36
+ ///
25
37
/// while the next one can't:
38
+ ///
26
39
/// ```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 }
35
56
/// ```
36
57
///
37
58
/// # Examples
38
59
///
60
+ /// Basic usage, building a `Grid` via [`GridBuilder`], with optimal grid layout determined by
61
+ /// a given [`strategy`], and genrating a [`histogram`]:
62
+ ///
39
63
/// ```
40
64
/// use ndarray::{Array, array};
41
65
/// use ndarray_stats::{
@@ -44,17 +68,14 @@ use std::ops::Range;
44
68
/// };
45
69
/// use noisy_float::types::{N64, n64};
46
70
///
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
48
72
/// let observations = Array::from_shape_vec(
49
73
/// (12, 1),
50
74
/// vec![1, 4, 5, 2, 100, 20, 50, 65, 27, 40, 45, 23],
51
75
/// ).unwrap();
52
76
///
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
55
78
/// 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);
58
79
///
59
80
/// let histogram = observations.histogram(grid);
60
81
///
@@ -63,19 +84,22 @@ use std::ops::Range;
63
84
/// let expected = array![4, 3, 3, 1, 0, 1];
64
85
/// assert_eq!(histogram_matrix, expected.into_dyn());
65
86
/// ```
87
+ ///
88
+ /// [`histogram`]: trait.HistogramExt.html
89
+ /// [`GridBuilder`]: struct.GridBuilder.html
90
+ /// [`strategy`]: strategies/index.html
66
91
#[ derive( Clone , Debug , Eq , PartialEq ) ]
67
92
pub struct Grid < A : Ord > {
68
93
projections : Vec < Bins < A > > ,
69
94
}
70
95
71
96
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 .
73
98
///
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.
76
101
///
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`].
79
103
///
80
104
/// [`GridBuilder`]: struct.GridBuilder.html
81
105
fn from ( projections : Vec < Bins < A > > ) -> Self {
@@ -84,7 +108,7 @@ impl<A: Ord> From<Vec<Bins<A>>> for Grid<A> {
84
108
}
85
109
86
110
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.
88
112
///
89
113
/// # Examples
90
114
///
@@ -101,7 +125,7 @@ impl<A: Ord> Grid<A> {
101
125
self . projections . len ( )
102
126
}
103
127
104
- /// Returns the number of bins along each coordinate axis.
128
+ /// Returns the numbers of bins along each coordinate axis.
105
129
///
106
130
/// # Examples
107
131
///
@@ -120,19 +144,19 @@ impl<A: Ord> Grid<A> {
120
144
self . projections . iter ( ) . map ( |e| e. len ( ) ) . collect ( )
121
145
}
122
146
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.
124
148
pub fn projections ( & self ) -> & [ Bins < A > ] {
125
149
& self . projections
126
150
}
127
151
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.
130
154
///
131
155
/// Returns `None` if the point is outside the grid.
132
156
///
133
157
/// # Panics
134
158
///
135
- /// Panics if `point.len()` does not equal `self.ndim()` .
159
+ /// Panics if dimensionality of the point doesn't equal the grid's .
136
160
///
137
161
/// # Examples
138
162
///
@@ -147,18 +171,19 @@ impl<A: Ord> Grid<A> {
147
171
/// let bins = Bins::new(edges);
148
172
/// let square_grid = Grid::from(vec![bins.clone(), bins.clone()]);
149
173
///
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
151
175
/// assert_eq!(
152
176
/// square_grid.index_of(&array![n64(0.), n64(-0.7)]),
153
177
/// Some(vec![1, 0]),
154
178
/// );
155
- /// // ` 1.` is outside of the grid, return `None`
179
+ /// // Returns `None`, as ` 1.` is outside the grid since bins are right exclusive
156
180
/// assert_eq!(
157
181
/// square_grid.index_of(&array![n64(0.), n64(1.)]),
158
182
/// None,
159
183
/// );
160
184
/// ```
161
- /// A panic upon incompatible `point` length:
185
+ ///
186
+ /// A panic upon dimensionality mismatch:
162
187
///
163
188
/// ```should_panic
164
189
/// # use ndarray::array;
@@ -167,8 +192,8 @@ impl<A: Ord> Grid<A> {
167
192
/// # let edges = Edges::from(vec![n64(-1.), n64(0.), n64(1.)]);
168
193
/// # let bins = Bins::new(edges);
169
194
/// # let square_grid = Grid::from(vec![bins.clone(), bins.clone()]);
195
+ /// // the point has 3 dimensions, the grid expected 2 dimensions
170
196
/// assert_eq!(
171
- /// // the point has 3 dimensions, the grid expected 2 dimensions
172
197
/// square_grid.index_of(&array![n64(0.), n64(-0.7), n64(0.5)]),
173
198
/// Some(vec![1, 0, 1]),
174
199
/// );
@@ -194,14 +219,15 @@ impl<A: Ord> Grid<A> {
194
219
}
195
220
196
221
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.
200
225
///
201
226
/// # Panics
202
227
///
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()`.
205
231
///
206
232
/// # Examples
207
233
///
@@ -211,15 +237,16 @@ impl<A: Ord + Clone> Grid<A> {
211
237
/// use ndarray::array;
212
238
/// use ndarray_stats::histogram::{Edges, Bins, Grid};
213
239
///
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 ]);
216
242
/// let bins_x = Bins::new(edges_x);
217
243
/// let bins_y = Bins::new(edges_y);
218
244
/// let square_grid = Grid::from(vec![bins_x, bins_y]);
219
245
///
246
+ /// // Query the 0-th bin on x-axis, and 1-st bin on y-axis
220
247
/// assert_eq!(
221
248
/// square_grid.index(&[0, 1]),
222
- /// vec![0..1, 4..5 ],
249
+ /// vec![0..1, 3..4 ],
223
250
/// );
224
251
/// ```
225
252
///
@@ -228,15 +255,15 @@ impl<A: Ord + Clone> Grid<A> {
228
255
/// ```should_panic
229
256
/// # use ndarray::array;
230
257
/// # 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 ]);
233
260
/// # let bins_x = Bins::new(edges_x);
234
261
/// # let bins_y = Bins::new(edges_y);
235
262
/// # let square_grid = Grid::from(vec![bins_x, bins_y]);
263
+ /// // out-of-bound on y-axis
236
264
/// assert_eq!(
237
- /// // out-of-bound on `edges_y`
238
265
/// square_grid.index(&[0, 2]),
239
- /// vec![0..1, 1..2 ],
266
+ /// vec![0..1, 3..4 ],
240
267
/// );
241
268
/// ```
242
269
pub fn index ( & self , index : & [ usize ] ) -> Vec < Range < A > > {
@@ -254,8 +281,32 @@ impl<A: Ord + Clone> Grid<A> {
254
281
}
255
282
}
256
283
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
+ /// ```
259
310
///
260
311
/// [`Grid`]: struct.Grid.html
261
312
/// [`histogram`]: trait.HistogramExt.html
@@ -269,18 +320,22 @@ where
269
320
A : Ord ,
270
321
B : BinsBuildingStrategy < Elem = A > ,
271
322
{
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)`.
275
325
///
276
326
/// # Errors
277
327
///
278
328
/// It returns [`BinsBuildError`] if it is not possible to build a [`Grid`] given
279
329
/// the observed data according to the chosen [`strategy`].
280
330
///
331
+ /// # Examples
332
+ ///
333
+ /// See [Trait-level examples] for basic usage.
334
+ ///
281
335
/// [`Grid`]: struct.Grid.html
282
336
/// [`strategy`]: strategies/index.html
283
337
/// [`BinsBuildError`]: errors/enum.BinsBuildError.html
338
+ /// [Trait-level examples]: struct.GridBuilder.html#examples
284
339
pub fn from_array < S > ( array : & ArrayBase < S , Ix2 > ) -> Result < Self , BinsBuildError >
285
340
where
286
341
S : Data < Elem = A > ,
@@ -292,8 +347,12 @@ where
292
347
Ok ( Self { bin_builders } )
293
348
}
294
349
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.
297
356
///
298
357
/// [`Grid`]: struct.Grid.html
299
358
/// [`strategy`]: strategies/index.html
0 commit comments