1
1
use ndarray:: prelude:: * ;
2
2
use std:: ops:: { Index , Range } ;
3
3
4
- /// `Edges` is a sorted collection of `A` elements used
5
- /// to represent the boundaries of intervals ([`Bins`]) on
6
- /// a 1-dimensional axis.
4
+ /// A sorted collection of type `A` elements used to represent the boundaries of intervals, i.e.
5
+ /// [`Bins`] on a 1-dimensional axis.
7
6
///
8
- /// [`Bins`]: struct.Bins.html
9
- /// # Example:
7
+ /// **Note** that all intervals are left-closed and right-open. See examples below.
8
+ ///
9
+ /// # Examples
10
10
///
11
11
/// ```
12
- /// use ndarray_stats::histogram::{Edges, Bins };
12
+ /// use ndarray_stats::histogram::{Bins, Edges };
13
13
/// use noisy_float::types::n64;
14
14
///
15
15
/// let unit_edges = Edges::from(vec![n64(0.), n64(1.)]);
16
16
/// let unit_interval = Bins::new(unit_edges);
17
- /// // left inclusive
17
+ /// // left-closed
18
18
/// assert_eq!(
19
19
/// unit_interval.range_of(&n64(0.)).unwrap(),
20
20
/// n64(0.)..n64(1.),
21
21
/// );
22
- /// // right exclusive
22
+ /// // right-open
23
23
/// assert_eq!(
24
24
/// unit_interval.range_of(&n64(1.)),
25
25
/// None
26
26
/// );
27
27
/// ```
28
+ ///
29
+ /// [`Bins`]: struct.Bins.html
28
30
#[ derive( Clone , Debug , Eq , PartialEq ) ]
29
31
pub struct Edges < A : Ord > {
30
32
edges : Vec < A > ,
31
33
}
32
34
33
35
impl < A : Ord > From < Vec < A > > for Edges < A > {
34
- /// Get an `Edges` instance from a `Vec<A>`:
35
- /// the vector will be sorted in increasing order
36
- /// using an unstable sorting algorithm and duplicates
37
- /// will be removed.
36
+ /// Converts a `Vec<A>` into an `Edges<A>`, consuming the edges.
37
+ /// The vector will be sorted in increasing order using an unstable sorting algorithm, with
38
+ /// duplicates removed.
38
39
///
39
- /// # Example:
40
+ /// # Current implementation
41
+ ///
42
+ /// The current sorting algorithm is the same as [`std::slice::sort_unstable()`][sort],
43
+ /// which is based on [pattern-defeating quicksort][pdqsort].
44
+ ///
45
+ /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not allocate)
46
+ /// , and O(n log n) worst-case.
47
+ ///
48
+ /// # Examples
40
49
///
41
50
/// ```
42
51
/// use ndarray::array;
@@ -49,6 +58,9 @@ impl<A: Ord> From<Vec<A>> for Edges<A> {
49
58
/// 15
50
59
/// );
51
60
/// ```
61
+ ///
62
+ /// [sort]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.sort_unstable
63
+ /// [pdqsort]: https://github.com/orlp/pdqsort
52
64
fn from ( mut edges : Vec < A > ) -> Self {
53
65
// sort the array in-place
54
66
edges. sort_unstable ( ) ;
@@ -59,11 +71,19 @@ impl<A: Ord> From<Vec<A>> for Edges<A> {
59
71
}
60
72
61
73
impl < A : Ord + Clone > From < Array1 < A > > for Edges < A > {
62
- /// Get an `Edges` instance from a `Array1 <A>`:
63
- /// the array elements will be sorted in increasing order
64
- /// using an unstable sorting algorithm and duplicates will be removed.
74
+ /// Converts an `Array1<A>` into an `Edges <A>`, consuming the 1-dimensional array.
75
+ /// The array will be sorted in increasing order using an unstable sorting algorithm, with
76
+ /// duplicates removed.
65
77
///
66
- /// # Example:
78
+ /// # Current implementation
79
+ ///
80
+ /// The current sorting algorithm is the same as [`std::slice::sort_unstable()`][sort],
81
+ /// which is based on [pattern-defeating quicksort][pdqsort].
82
+ ///
83
+ /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not allocate)
84
+ /// , and O(n log n) worst-case.
85
+ ///
86
+ /// # Examples
67
87
///
68
88
/// ```
69
89
/// use ndarray_stats::histogram::Edges;
@@ -75,6 +95,9 @@ impl<A: Ord + Clone> From<Array1<A>> for Edges<A> {
75
95
/// 10
76
96
/// );
77
97
/// ```
98
+ ///
99
+ /// [sort]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.sort_unstable
100
+ /// [pdqsort]: https://github.com/orlp/pdqsort
78
101
fn from ( edges : Array1 < A > ) -> Self {
79
102
let edges = edges. to_vec ( ) ;
80
103
Self :: from ( edges)
@@ -84,11 +107,13 @@ impl<A: Ord + Clone> From<Array1<A>> for Edges<A> {
84
107
impl < A : Ord > Index < usize > for Edges < A > {
85
108
type Output = A ;
86
109
87
- /// Get the `i`-th edge.
110
+ /// Returns a reference to the `i`-th edge in `self` .
88
111
///
89
- /// **Panics** if the index `i` is out of bounds.
112
+ /// # Panics
90
113
///
91
- /// # Example:
114
+ /// Panics if the index `i` is out of bounds.
115
+ ///
116
+ /// # Examples
92
117
///
93
118
/// ```
94
119
/// use ndarray_stats::histogram::Edges;
@@ -105,9 +130,9 @@ impl<A: Ord> Index<usize> for Edges<A> {
105
130
}
106
131
107
132
impl < A : Ord > Edges < A > {
108
- /// Number of edges in `self`.
133
+ /// Returns the number of edges in `self`.
109
134
///
110
- /// # Example:
135
+ /// # Examples
111
136
///
112
137
/// ```
113
138
/// use ndarray_stats::histogram::Edges;
@@ -125,25 +150,25 @@ impl<A: Ord> Edges<A> {
125
150
126
151
/// Returns `true` if `self` contains no edges.
127
152
///
128
- /// # Example:
153
+ /// # Examples
129
154
///
130
155
/// ```
131
156
/// use ndarray_stats::histogram::Edges;
132
157
/// use noisy_float::types::{N64, n64};
133
158
///
134
159
/// let edges = Edges::<N64>::from(vec![]);
135
160
/// assert_eq!(edges.is_empty(), true);
161
+ ///
136
162
/// let edges = Edges::from(vec![n64(0.), n64(2.), n64(5.)]);
137
163
/// assert_eq!(edges.is_empty(), false);
138
164
/// ```
139
165
pub fn is_empty ( & self ) -> bool {
140
166
self . edges . is_empty ( )
141
167
}
142
168
143
- /// Borrow an immutable reference to the edges as a 1-dimensional
144
- /// array view.
169
+ /// Returns an immutable 1-dimensional array view of edges.
145
170
///
146
- /// # Example:
171
+ /// # Examples
147
172
///
148
173
/// ```
149
174
/// use ndarray::array;
@@ -159,21 +184,26 @@ impl<A: Ord> Edges<A> {
159
184
ArrayView1 :: from ( & self . edges )
160
185
}
161
186
162
- /// Given `value`, it returns an option:
163
- /// - `Some((left, right))`, where `right=left+1`, if there are two consecutive edges in
164
- /// `self` such that `self[left] <= value < self[right]`;
187
+ /// Returns indices of two consecutive `edges` in `self`, if the interval they represent
188
+ /// contains the given `value`, or returns `None` otherwise.
189
+ ///
190
+ /// That is to say, it returns
191
+ /// - `Some((left, right))`, where `left` and `right` are the indices of two consecutive edges
192
+ /// in `self` and `right == left + 1`, if `self[left] <= value < self[right]`;
165
193
/// - `None`, otherwise.
166
194
///
167
- /// # Example:
195
+ /// # Examples
168
196
///
169
197
/// ```
170
198
/// use ndarray_stats::histogram::Edges;
171
199
///
172
200
/// let edges = Edges::from(vec![0, 2, 3]);
201
+ /// // `1` is in the interval [0, 2), whose indices are (0, 1)
173
202
/// assert_eq!(
174
203
/// edges.indices_of(&1),
175
204
/// Some((0, 1))
176
205
/// );
206
+ /// // `5` is not in any of intervals
177
207
/// assert_eq!(
178
208
/// edges.indices_of(&5),
179
209
/// None
@@ -193,17 +223,17 @@ impl<A: Ord> Edges<A> {
193
223
}
194
224
}
195
225
226
+ /// Returns an iterator over the `edges` in `self`.
196
227
pub fn iter ( & self ) -> impl Iterator < Item = & A > {
197
228
self . edges . iter ( )
198
229
}
199
230
}
200
231
201
- /// `Bins` is a sorted collection of non-overlapping
202
- /// 1-dimensional intervals.
232
+ /// A sorted collection of non-overlapping 1-dimensional intervals.
203
233
///
204
- /// All intervals are left-inclusive and right-exclusive .
234
+ /// **Note** that all intervals are left-closed and right-open .
205
235
///
206
- /// # Example:
236
+ /// # Examples
207
237
///
208
238
/// ```
209
239
/// use ndarray_stats::histogram::{Edges, Bins};
@@ -228,16 +258,17 @@ pub struct Bins<A: Ord> {
228
258
}
229
259
230
260
impl < A : Ord > Bins < A > {
231
- /// Given a collection of [`Edges`], it returns the corresponding `Bins` instance.
261
+ /// Returns a `Bins` instance where each bin corresponds to two consecutive members of the given
262
+ /// [`Edges`], consuming the edges.
232
263
///
233
264
/// [`Edges`]: struct.Edges.html
234
265
pub fn new ( edges : Edges < A > ) -> Self {
235
266
Bins { edges }
236
267
}
237
268
238
- /// Returns the number of bins.
269
+ /// Returns the number of bins in `self` .
239
270
///
240
- /// # Example:
271
+ /// # Examples
241
272
///
242
273
/// ```
243
274
/// use ndarray_stats::histogram::{Edges, Bins};
@@ -257,70 +288,89 @@ impl<A: Ord> Bins<A> {
257
288
}
258
289
}
259
290
260
- /// Returns `true` if the number of bins is zero, or in other words, if the
261
- /// number of edges is 0 or 1.
291
+ /// Returns `true` if the number of bins is zero, i.e. if the number of edges is 0 or 1.
262
292
///
263
- /// # Example:
293
+ /// # Examples
264
294
///
265
295
/// ```
266
296
/// use ndarray_stats::histogram::{Edges, Bins};
267
297
/// use noisy_float::types::{N64, n64};
268
298
///
299
+ /// // At least 2 edges is needed to represent 1 interval
300
+ /// let edges = Edges::from(vec![n64(0.), n64(1.), n64(3.)]);
301
+ /// let bins = Bins::new(edges);
302
+ /// assert_eq!(bins.is_empty(), false);
303
+ ///
304
+ /// // No valid interval == Empty
269
305
/// let edges = Edges::<N64>::from(vec![]);
270
306
/// let bins = Bins::new(edges);
271
307
/// assert_eq!(bins.is_empty(), true);
272
308
/// let edges = Edges::from(vec![n64(0.)]);
273
309
/// let bins = Bins::new(edges);
274
310
/// assert_eq!(bins.is_empty(), true);
275
- /// let edges = Edges::from(vec![n64(0.), n64(1.), n64(3.)]);
276
- /// let bins = Bins::new(edges);
277
- /// assert_eq!(bins.is_empty(), false);
278
311
/// ```
279
312
pub fn is_empty ( & self ) -> bool {
280
313
self . len ( ) == 0
281
314
}
282
315
283
- /// Given `value`, it returns:
284
- /// - `Some(i)`, if the `i`-th bin in `self` contains `value`;
285
- /// - `None`, if `value` does not belong to any of the bins in `self`.
316
+ /// Returns the index of the bin in `self` that contains the given `value`,
317
+ /// or returns `None` if `value` does not belong to any bins in `self`.
286
318
///
287
- /// # Example:
319
+ /// # Examples
320
+ ///
321
+ /// Basic usage:
288
322
///
289
323
/// ```
290
324
/// use ndarray_stats::histogram::{Edges, Bins};
291
325
///
292
326
/// let edges = Edges::from(vec![0, 2, 4, 6]);
293
327
/// let bins = Bins::new(edges);
294
328
/// let value = 1;
329
+ /// // The first bin [0, 2) contains `1`
295
330
/// assert_eq!(
296
331
/// bins.index_of(&1),
297
332
/// Some(0)
298
333
/// );
334
+ /// // No bin contains 100
335
+ /// assert_eq!(
336
+ /// bins.index_of(&100),
337
+ /// None
338
+ /// )
339
+ /// ```
340
+ ///
341
+ /// Chaining [`Bins::index`] and [`Bins::index_of`] to get the boundaries of the bin containing
342
+ /// the value:
343
+ ///
344
+ /// ```
345
+ /// # use ndarray_stats::histogram::{Edges, Bins};
346
+ /// # let edges = Edges::from(vec![0, 2, 4, 6]);
347
+ /// # let bins = Bins::new(edges);
348
+ /// # let value = 1;
299
349
/// assert_eq!(
300
- /// bins.index(bins.index_of(&1).unwrap()),
301
- /// 0..2
350
+ /// // using `Option::map` to avoid panic on index out-of-bounds
351
+ /// bins.index_of(&1).map(|i| bins.index(i)),
352
+ /// Some(0..2)
302
353
/// );
303
354
/// ```
304
355
pub fn index_of ( & self , value : & A ) -> Option < usize > {
305
356
self . edges . indices_of ( value) . map ( |t| t. 0 )
306
357
}
307
358
308
- /// Given `value`, it returns:
309
- /// - `Some(left_edge..right_edge)`, if there exists a bin in `self` such that
310
- /// `left_edge <= value < right_edge`;
311
- /// - `None`, otherwise.
359
+ /// Returns a range as the bin which contains the given `value`, or returns `None` otherwise.
312
360
///
313
- /// # Example:
361
+ /// # Examples
314
362
///
315
363
/// ```
316
364
/// use ndarray_stats::histogram::{Edges, Bins};
317
365
///
318
366
/// let edges = Edges::from(vec![0, 2, 4, 6]);
319
367
/// let bins = Bins::new(edges);
368
+ /// // [0, 2) contains `1`
320
369
/// assert_eq!(
321
370
/// bins.range_of(&1),
322
371
/// Some(0..2)
323
372
/// );
373
+ /// // `10` is not in any interval
324
374
/// assert_eq!(
325
375
/// bins.range_of(&10),
326
376
/// None
@@ -337,11 +387,13 @@ impl<A: Ord> Bins<A> {
337
387
} )
338
388
}
339
389
340
- /// Get the `i`-th bin.
390
+ /// Returns a range as the bin at the given `index` position.
391
+ ///
392
+ /// # Panics
341
393
///
342
- /// ** Panics** if `index` is out of bounds.
394
+ /// Panics if `index` is out of bounds.
343
395
///
344
- /// # Example:
396
+ /// # Examples
345
397
///
346
398
/// ```
347
399
/// use ndarray_stats::histogram::{Edges, Bins};
@@ -401,7 +453,7 @@ mod edges_tests {
401
453
}
402
454
403
455
#[ quickcheck]
404
- fn edges_are_right_exclusive ( v : Vec < i32 > ) -> bool {
456
+ fn edges_are_right_open ( v : Vec < i32 > ) -> bool {
405
457
let edges = Edges :: from ( v) ;
406
458
let view = edges. as_array_view ( ) ;
407
459
if view. len ( ) == 0 {
@@ -413,7 +465,7 @@ mod edges_tests {
413
465
}
414
466
415
467
#[ quickcheck]
416
- fn edges_are_left_inclusive ( v : Vec < i32 > ) -> bool {
468
+ fn edges_are_left_closed ( v : Vec < i32 > ) -> bool {
417
469
let edges = Edges :: from ( v) ;
418
470
match edges. len ( ) {
419
471
1 => true ,
@@ -445,7 +497,7 @@ mod bins_tests {
445
497
446
498
#[ test]
447
499
#[ should_panic]
448
- fn get_panics_for_out_of_bound_indexes ( ) {
500
+ fn get_panics_for_out_of_bounds_indexes ( ) {
449
501
let edges = Edges :: from ( vec ! [ 0 ] ) ;
450
502
let bins = Bins :: new ( edges) ;
451
503
// we need at least two edges to make a valid bin!
0 commit comments