1
1
use super :: SummaryStatisticsExt ;
2
2
use ndarray:: { ArrayBase , Data , Dimension } ;
3
- use num_traits:: { Float , FromPrimitive , Zero } ;
3
+ use num_integer:: IterBinomial ;
4
+ use num_traits:: { Float , FromPrimitive , ToPrimitive , Zero } ;
4
5
use std:: ops:: { Add , Div } ;
5
6
6
7
impl < A , S , D > SummaryStatisticsExt < A , S , D > for ArrayBase < S , D >
@@ -40,67 +41,65 @@ where
40
41
where
41
42
A : Float + FromPrimitive ,
42
43
{
43
- let central_moments = self . central_moments ( 4 ) ;
44
- central_moments . map ( |moments| moments [ 4 ] / moments [ 2 ] . powi ( 2 ) )
44
+ let central_moments = self . central_moments ( 4 ) ? ;
45
+ Some ( central_moments [ 4 ] / central_moments [ 2 ] . powi ( 2 ) )
45
46
}
46
47
47
48
fn skewness ( & self ) -> Option < A >
48
49
where
49
50
A : Float + FromPrimitive ,
50
51
{
51
- let central_moments = self . central_moments ( 3 ) ;
52
- central_moments . map ( |moments| moments [ 3 ] / moments [ 2 ] . sqrt ( ) . powi ( 3 ) )
52
+ let central_moments = self . central_moments ( 3 ) ? ;
53
+ Some ( central_moments [ 3 ] / central_moments [ 2 ] . sqrt ( ) . powi ( 3 ) )
53
54
}
54
55
55
56
fn central_moment ( & self , order : usize ) -> Option < A >
56
57
where
57
58
A : Float + FromPrimitive ,
58
59
{
59
- let mean = self . mean ( ) ;
60
- match mean {
61
- None => None ,
62
- Some ( mean) => match order {
63
- 0 => Some ( A :: one ( ) ) ,
64
- 1 => Some ( A :: zero ( ) ) ,
65
- n => {
66
- let shifted_array = self . map ( |x| x. clone ( ) - mean) ;
67
- let shifted_moments = moments ( shifted_array, n) ;
68
- let correction_term = -shifted_moments[ 1 ] . clone ( ) ;
60
+ if self . is_empty ( ) {
61
+ return None ;
62
+ }
63
+ match order {
64
+ 0 => Some ( A :: one ( ) ) ,
65
+ 1 => Some ( A :: zero ( ) ) ,
66
+ n => {
67
+ let mean = self . mean ( ) . unwrap ( ) ;
68
+ let shifted_array = self . mapv ( |x| x - mean) ;
69
+ let shifted_moments = moments ( shifted_array, n) ;
70
+ let correction_term = -shifted_moments[ 1 ] ;
69
71
70
- let coefficients = central_moment_coefficients ( & shifted_moments) ;
71
- Some ( horner_method ( coefficients, correction_term) )
72
- }
73
- } ,
72
+ let coefficients = central_moment_coefficients ( & shifted_moments) ;
73
+ Some ( horner_method ( coefficients, correction_term) )
74
+ }
74
75
}
75
76
}
76
77
77
78
fn central_moments ( & self , order : usize ) -> Option < Vec < A > >
78
79
where
79
80
A : Float + FromPrimitive ,
80
81
{
81
- let mean = self . mean ( ) ;
82
- match mean {
83
- None => None ,
84
- Some ( mean ) => {
85
- match order {
86
- 0 => Some ( vec ! [ A :: one( ) ] ) ,
87
- 1 => Some ( vec ! [ A :: one ( ) , A :: zero ( ) ] ) ,
88
- n => {
89
- // We only perform this operations once, and then reuse their
90
- // result to compute all the required moments
91
- let shifted_array = self . map ( |x| x. clone ( ) - mean) ;
92
- let shifted_moments = moments ( shifted_array, n) ;
93
- let correction_term = -shifted_moments[ 1 ] . clone ( ) ;
82
+ if self . is_empty ( ) {
83
+ return None ;
84
+ }
85
+ match order {
86
+ 0 => Some ( vec ! [ A :: one ( ) ] ) ,
87
+ 1 => Some ( vec ! [ A :: one( ) , A :: zero ( ) ] ) ,
88
+ n => {
89
+ // We only perform these operations once, and then reuse their
90
+ // result to compute all the required moments
91
+ let mean = self . mean ( ) . unwrap ( ) ;
92
+ let shifted_array = self . mapv ( |x| x - mean) ;
93
+ let shifted_moments = moments ( shifted_array, n) ;
94
+ let correction_term = -shifted_moments[ 1 ] ;
94
95
95
- let mut central_moments = vec ! [ A :: one( ) , A :: zero( ) ] ;
96
- for k in 2 ..=n {
97
- let coefficients = central_moment_coefficients ( & shifted_moments[ ..=k] ) ;
98
- let central_moment = horner_method ( coefficients, correction_term) ;
99
- central_moments. push ( central_moment)
100
- }
101
- Some ( central_moments)
102
- }
96
+ let mut central_moments = vec ! [ A :: one( ) , A :: zero( ) ] ;
97
+ for k in 2 ..=n {
98
+ let coefficients = central_moment_coefficients ( & shifted_moments[ ..=k] ) ;
99
+ let central_moment = horner_method ( coefficients, correction_term) ;
100
+ central_moments. push ( central_moment)
103
101
}
102
+ Some ( central_moments)
104
103
}
105
104
}
106
105
}
@@ -126,6 +125,9 @@ where
126
125
{
127
126
let n_elements =
128
127
A :: from_usize ( a. len ( ) ) . expect ( "Converting number of elements to `A` must not fail" ) ;
128
+ let order = order
129
+ . to_i32 ( )
130
+ . expect ( "Moment order must not overflow `i32`." ) ;
129
131
130
132
// When k=0, we are raising each element to the 0th power
131
133
// No need to waste CPU cycles going through the array
@@ -137,7 +139,7 @@ where
137
139
}
138
140
139
141
for k in 2 ..=order {
140
- moments. push ( a. map ( |x| x. powi ( k as i32 ) ) . sum ( ) / n_elements)
142
+ moments. push ( a. map ( |x| x. powi ( k) ) . sum ( ) / n_elements)
141
143
}
142
144
moments
143
145
}
@@ -152,34 +154,12 @@ where
152
154
A : Float + FromPrimitive ,
153
155
{
154
156
let order = moments. len ( ) ;
155
- moments
156
- . iter ( )
157
- . rev ( )
158
- . enumerate ( )
159
- . map ( |( k, moment) | A :: from_usize ( binomial_coefficient ( order, k) ) . unwrap ( ) * * moment)
157
+ IterBinomial :: new ( order)
158
+ . zip ( moments. iter ( ) . rev ( ) )
159
+ . map ( |( binom, & moment) | A :: from_usize ( binom) . unwrap ( ) * moment)
160
160
. collect ( )
161
161
}
162
162
163
- /// Returns the binomial coefficient "n over k".
164
- ///
165
- /// **Panics** if k > n.
166
- fn binomial_coefficient ( n : usize , k : usize ) -> usize {
167
- if k > n {
168
- panic ! (
169
- "Tried to compute the binomial coefficient of {0} over {1}, \
170
- but {1} is strictly greater than {0}!"
171
- )
172
- }
173
- // BC(n, k) = BC(n, n-k)
174
- let k = if k > n - k { n - k } else { k } ;
175
- let mut result = 1 ;
176
- for i in 0 ..k {
177
- result = result * ( n - i) ;
178
- result = result / ( i + 1 ) ;
179
- }
180
- result
181
- }
182
-
183
163
/// Uses [Horner's method] to evaluate a polynomial with a single indeterminate.
184
164
///
185
165
/// Coefficients are expected to be sorted by ascending order
@@ -270,30 +250,32 @@ mod tests {
270
250
}
271
251
272
252
#[ test]
273
- fn test_central_order_moment_with_empty_array_of_floats ( ) {
253
+ fn test_central_moment_with_empty_array_of_floats ( ) {
274
254
let a: Array1 < f64 > = array ! [ ] ;
275
- assert ! ( a. central_moment( 1 ) . is_none( ) ) ;
276
- assert ! ( a. central_moments( 1 ) . is_none( ) ) ;
255
+ for order in 0 ..=3 {
256
+ assert ! ( a. central_moment( order) . is_none( ) ) ;
257
+ assert ! ( a. central_moments( order) . is_none( ) ) ;
258
+ }
277
259
}
278
260
279
261
#[ test]
280
- fn test_zeroth_central_order_moment_is_one ( ) {
262
+ fn test_zeroth_central_moment_is_one ( ) {
281
263
let n = 50 ;
282
264
let bound: f64 = 200. ;
283
265
let a = Array :: random ( n, Uniform :: new ( -bound. abs ( ) , bound. abs ( ) ) ) ;
284
266
assert_eq ! ( a. central_moment( 0 ) . unwrap( ) , 1. ) ;
285
267
}
286
268
287
269
#[ test]
288
- fn test_first_central_order_moment_is_zero ( ) {
270
+ fn test_first_central_moment_is_zero ( ) {
289
271
let n = 50 ;
290
272
let bound: f64 = 200. ;
291
273
let a = Array :: random ( n, Uniform :: new ( -bound. abs ( ) , bound. abs ( ) ) ) ;
292
274
assert_eq ! ( a. central_moment( 1 ) . unwrap( ) , 0. ) ;
293
275
}
294
276
295
277
#[ test]
296
- fn test_central_order_moments ( ) {
278
+ fn test_central_moments ( ) {
297
279
let a: Array1 < f64 > = array ! [
298
280
0.07820559 , 0.5026185 , 0.80935324 , 0.39384033 , 0.9483038 , 0.62516215 , 0.90772261 ,
299
281
0.87329831 , 0.60267392 , 0.2960298 , 0.02810356 , 0.31911966 , 0.86705506 , 0.96884832 ,
@@ -324,7 +306,7 @@ mod tests {
324
306
}
325
307
326
308
#[ test]
327
- fn test_bulk_central_order_moments ( ) {
309
+ fn test_bulk_central_moments ( ) {
328
310
// Test that the bulk method is coherent with the non-bulk method
329
311
let n = 50 ;
330
312
let bound: f64 = 200. ;
0 commit comments