22
22
23
23
var isPositiveInteger = require ( '@stdlib/assert/is-positive-integer' ) . isPrimitive ;
24
24
var isNumber = require ( '@stdlib/assert/is-number' ) . isPrimitive ;
25
+ var isnan = require ( '@stdlib/math/base/assert/is-nan' ) ;
25
26
26
27
27
28
// MAIN //
@@ -164,6 +165,8 @@ function incrmvariance( W, mean ) {
164
165
* // returns 28.0
165
166
*/
166
167
function accumulator1 ( x ) {
168
+ var k ;
169
+ var v ;
167
170
if ( arguments . length === 0 ) {
168
171
if ( N === 0 ) {
169
172
return null ;
@@ -179,9 +182,14 @@ function incrmvariance( W, mean ) {
179
182
// Update the index for managing the circular buffer:
180
183
i = ( i + 1 ) % W ;
181
184
182
- // Determine if we should update the initial window...
183
- if ( N < W ) {
184
- buf [ i ] = x ;
185
+ // Case: incoming value is NaN, the sliding second moment is automatically NaN...
186
+ if ( isnan ( x ) ) {
187
+ N = W ; // explicitly set to avoid `N < W` branch
188
+ M2 = NaN ;
189
+ }
190
+ // Case: initial window...
191
+ else if ( N < W ) {
192
+ buf [ i ] = x ; // update buffer
185
193
N += 1 ;
186
194
delta = x - mu ;
187
195
mu += delta / N ;
@@ -191,18 +199,42 @@ function incrmvariance( W, mean ) {
191
199
}
192
200
return M2 / ( N - 1 ) ;
193
201
}
194
- // N = W = 1
195
- if ( N === 1 ) {
202
+ // Case: N = W = 1
203
+ else if ( N === 1 ) {
196
204
return 0.0 ;
197
205
}
198
- // Update the existing window...
199
- tmp = buf [ i ] ;
206
+ // Case: outgoing value is NaN, and, thus, we need to compute the accumulated values...
207
+ else if ( isnan ( buf [ i ] ) ) {
208
+ N = 1 ;
209
+ mu = x ;
210
+ M2 = 0.0 ;
211
+ for ( k = 0 ; k < W ; k ++ ) {
212
+ if ( k !== i ) {
213
+ v = buf [ k ] ;
214
+ if ( isnan ( v ) ) {
215
+ N = W ; // explicitly set to avoid `N < W` branch
216
+ M2 = NaN ;
217
+ break ; // second moment is automatically NaN, so no need to continue
218
+ }
219
+ N += 1 ;
220
+ delta = v - mu ;
221
+ mu += delta / N ;
222
+ M2 += delta * ( v - mu ) ;
223
+ }
224
+ }
225
+ }
226
+ // Case: neither the current second moment nor the incoming value are NaN, so we need to update the accumulated values...
227
+ else if ( isnan ( M2 ) === false ) {
228
+ tmp = buf [ i ] ;
229
+ delta = x - tmp ;
230
+ d1 = tmp - mu ;
231
+ mu += delta / W ;
232
+ d2 = x - mu ;
233
+ M2 += delta * ( d1 + d2 ) ;
234
+ }
235
+ // Case: the current second moment is NaN, so nothing to do until the buffer no longer contains NaN values...
236
+
200
237
buf [ i ] = x ;
201
- delta = x - tmp ;
202
- d1 = tmp - mu ;
203
- mu += delta / W ;
204
- d2 = x - mu ;
205
- M2 += delta * ( d1 + d2 ) ;
206
238
return M2 / n ;
207
239
}
208
240
@@ -214,6 +246,7 @@ function incrmvariance( W, mean ) {
214
246
* @returns {(number|null) } unbiased sample variance or null
215
247
*/
216
248
function accumulator2 ( x ) {
249
+ var k ;
217
250
if ( arguments . length === 0 ) {
218
251
if ( N === 0 ) {
219
252
return null ;
@@ -226,18 +259,42 @@ function incrmvariance( W, mean ) {
226
259
// Update the index for managing the circular buffer:
227
260
i = ( i + 1 ) % W ;
228
261
229
- // Determine if we should update the initial window...
230
- if ( N < W ) {
231
- buf [ i ] = x ;
262
+ // Case: incoming value is NaN, the sliding second moment is automatically NaN...
263
+ if ( isnan ( x ) ) {
264
+ N = W ; // explicitly set to avoid `N < W` branch
265
+ M2 = NaN ;
266
+ }
267
+ // Case: initial window...
268
+ else if ( N < W ) {
269
+ buf [ i ] = x ; // update buffer
232
270
N += 1 ;
233
271
delta = x - mu ;
234
272
M2 += delta * delta ;
235
273
return M2 / N ;
236
274
}
237
- // Update the existing window...
238
- tmp = buf [ i ] ;
275
+ // Case: outgoing value is NaN, and, thus, we need to compute the accumulated values...
276
+ else if ( isnan ( buf [ i ] ) ) {
277
+ M2 = 0.0 ;
278
+ for ( k = 0 ; k < W ; k ++ ) {
279
+ if ( k !== i ) {
280
+ if ( isnan ( buf [ k ] ) ) {
281
+ N = W ; // explicitly set to avoid `N < W` branch
282
+ M2 = NaN ;
283
+ break ; // second moment is automatically NaN, so no need to continue
284
+ }
285
+ delta = buf [ k ] - mu ;
286
+ M2 += delta * delta ;
287
+ }
288
+ }
289
+ }
290
+ // Case: neither the current second moment nor the incoming value are NaN, so we need to update the accumulated values...
291
+ else if ( isnan ( M2 ) === false ) {
292
+ tmp = buf [ i ] ;
293
+ M2 += ( x - tmp ) * ( x - mu + tmp - mu ) ;
294
+ }
295
+ // Case: the current second moment is NaN, so nothing to do until the buffer no longer contains NaN values...
296
+
239
297
buf [ i ] = x ;
240
- M2 += ( x - tmp ) * ( x - mu + tmp - mu ) ;
241
298
return M2 / W ;
242
299
}
243
300
}
0 commit comments