Skip to content

Commit 62781dc

Browse files
committed
verify
1 parent e65cb4d commit 62781dc

File tree

1 file changed

+111
-20
lines changed
  • lib/node_modules/@stdlib/stats/incr/cv/lib

1 file changed

+111
-20
lines changed
Lines changed: 111 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @license Apache-2.0
33
*
4-
* Copyright (c) 2025 The Stdlib Authors.
4+
* Copyright (c) 2018 The Stdlib Authors.
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.
@@ -20,51 +20,142 @@
2020

2121
// MODULES //
2222

23-
var isnan = require('@stdlib/math/base/assert/is-nan');
24-
var incrcv = require('./../../cv/lib/main');
23+
var isNumber = require( '@stdlib/assert/is-number' ).isPrimitive;
24+
var isnan = require( '@stdlib/math/base/assert/is-nan' );
25+
var format = require( '@stdlib/string/format' );
26+
var sqrt = require( '@stdlib/math/base/special/sqrt' );
2527

2628

2729
// MAIN //
2830

2931
/**
30-
* Returns an accumulator function which incrementally computes the coefficient of variation (CV), ignoring NaN values.
32+
* Returns an accumulator function which incrementally computes the coefficient of variation (CV).
3133
*
34+
* ## Method
35+
*
36+
* - This implementation uses [Welford's method][algorithms-variance] for efficient computation, which can be derived as follows. Let
37+
*
38+
* ```tex
39+
* \begin{align*}
40+
* S_n &= n \sigma_n^2 \\
41+
* &= \sum_{i=1}^{n} (x_i - \mu_n)^2 \\
42+
* &= \biggl(\sum_{i=1}^{n} x_i^2 \biggr) - n\mu_n^2
43+
* \end{align*}
44+
* ```
45+
*
46+
* Accordingly,
47+
*
48+
* ```tex
49+
* \begin{align*}
50+
* S_n - S_{n-1} &= \sum_{i=1}^{n} x_i^2 - n\mu_n^2 - \sum_{i=1}^{n-1} x_i^2 + (n-1)\mu_{n-1}^2 \\
51+
* &= x_n^2 - n\mu_n^2 + (n-1)\mu_{n-1}^2 \\
52+
* &= x_n^2 - \mu_{n-1}^2 + n(\mu_{n-1}^2 - \mu_n^2) \\
53+
* &= x_n^2 - \mu_{n-1}^2 + n(\mu_{n-1} - \mu_n)(\mu_{n-1} + \mu_n) \\
54+
* &= x_n^2 - \mu_{n-1}^2 + (\mu_{n-1} - x_n)(\mu_{n-1} + \mu_n) \\
55+
* &= x_n^2 - \mu_{n-1}^2 + \mu_{n-1}^2 - x_n\mu_n - x_n\mu_{n-1} + \mu_n\mu_{n-1} \\
56+
* &= x_n^2 - x_n\mu_n - x_n\mu_{n-1} + \mu_n\mu_{n-1} \\
57+
* &= (x_n - \mu_{n-1})(x_n - \mu_n) \\
58+
* &= S_{n-1} + (x_n - \mu_{n-1})(x_n - \mu_n)
59+
* \end{align*}
60+
* ```
61+
*
62+
* where we use the identity
63+
*
64+
* ```tex
65+
* x_n - \mu_{n-1} = n (\mu_n - \mu_{n-1})
66+
* ```
67+
*
68+
* [algorithms-variance]: https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
69+
*
70+
* @param {number} [mean] - mean value
71+
* @throws {TypeError} must provide a number primitive
3272
* @returns {Function} accumulator function
3373
*
3474
* @example
35-
* var accumulator = incrnancv();
75+
* var accumulator = incrcv();
3676
*
3777
* var cv = accumulator();
3878
* // returns null
3979
*
40-
* cv = accumulator(2.0);
41-
* // returns 0.0
42-
*
43-
* cv = accumulator(NaN);
80+
* cv = accumulator( 2.0 );
4481
* // returns 0.0
4582
*
46-
* cv = accumulator(1.0);
83+
* cv = accumulator( 1.0 );
4784
* // returns ~0.47
4885
*
4986
* cv = accumulator();
5087
* // returns ~0.47
88+
*
89+
* @example
90+
* var accumulator = incrcv( 3.14 );
5191
*/
52-
function incrnancv() {
53-
var cv = incrcv();
54-
function accumulator(x) {
55-
if (arguments.length === 0) {
56-
return cv();
92+
function incrcv( mean ) {
93+
var delta;
94+
var mu;
95+
var M2;
96+
var N;
97+
98+
M2 = 0.0;
99+
N = 0;
100+
if ( arguments.length ) {
101+
if ( !isNumber( mean ) ) {
102+
throw new TypeError( format( 'invalid argument. Must provide a number. Value: `%s`.', mean ) );
57103
}
58-
if (isnan(x)) {
59-
return cv();
104+
mu = mean;
105+
return accumulator2;
106+
}
107+
mu = 0.0;
108+
return accumulator1;
109+
110+
/**
111+
* If provided a value, the accumulator function returns an updated accumulated value. If not provided a value, the accumulator function returns the current accumulated value.
112+
*
113+
* @private
114+
* @param {number} [x] - new value
115+
* @returns {(number|null)} accumulated value or null
116+
*/
117+
function accumulator1( x ) {
118+
if ( arguments.length === 0 ) {
119+
if ( N === 0 ) {
120+
return null;
121+
}
122+
if ( N === 1 ) {
123+
return ( isnan( M2 ) ) ? NaN : 0.0/mu;
124+
}
125+
return sqrt( M2/(N-1) ) / mu;
126+
}
127+
N += 1;
128+
delta = x - mu;
129+
mu += delta / N;
130+
M2 += delta * ( x - mu );
131+
if ( N < 2 ) {
132+
return ( isnan( M2 ) ) ? NaN : 0.0/mu;
60133
}
61-
return cv(x);
134+
return sqrt( M2/(N-1) ) / mu;
62135
}
63136

64-
return accumulator;
137+
/**
138+
* If provided a value, the accumulator function returns an updated accumulated value. If not provided a value, the accumulator function returns the current accumulated value.
139+
*
140+
* @private
141+
* @param {number} [x] - new value
142+
* @returns {(number|null)} accumulated value or null
143+
*/
144+
function accumulator2( x ) {
145+
if ( arguments.length === 0 ) {
146+
if ( N === 0 ) {
147+
return null;
148+
}
149+
return sqrt( M2/N ) / mu;
150+
}
151+
N += 1;
152+
delta = x - mu;
153+
M2 += delta * delta;
154+
return sqrt( M2/N ) / mu;
155+
}
65156
}
66157

67158

68159
// EXPORTS //
69160

70-
module.exports = incrnancv;
161+
module.exports = incrcv;

0 commit comments

Comments
 (0)