|
1 | 1 | /** |
2 | 2 | * @license Apache-2.0 |
3 | 3 | * |
4 | | -* Copyright (c) 2018 The Stdlib Authors. |
| 4 | +* Copyright (c) 2025 The Stdlib Authors. |
5 | 5 | * |
6 | 6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | 7 | * you may not use this file except in compliance with the License. |
|
20 | 20 |
|
21 | 21 | // MODULES // |
22 | 22 |
|
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' ); |
| 23 | +var isnan = require('@stdlib/math/base/assert/is-nan'); |
| 24 | +var incrcv = require('./../../cv/lib/main'); |
27 | 25 |
|
28 | 26 |
|
29 | 27 | // MAIN // |
30 | 28 |
|
31 | 29 | /** |
32 | | -* Returns an accumulator function which incrementally computes the coefficient of variation (CV). |
| 30 | +* Returns an accumulator function which incrementally computes the coefficient of variation (CV), ignoring NaN values. |
33 | 31 | * |
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 |
72 | 32 | * @returns {Function} accumulator function |
73 | 33 | * |
74 | 34 | * @example |
75 | | -* var accumulator = incrcv(); |
| 35 | +* var accumulator = incrnancv(); |
76 | 36 | * |
77 | 37 | * var cv = accumulator(); |
78 | 38 | * // returns null |
79 | 39 | * |
80 | | -* cv = accumulator( 2.0 ); |
| 40 | +* cv = accumulator(2.0); |
| 41 | +* // returns 0.0 |
| 42 | +* |
| 43 | +* cv = accumulator(NaN); |
81 | 44 | * // returns 0.0 |
82 | 45 | * |
83 | | -* cv = accumulator( 1.0 ); |
| 46 | +* cv = accumulator(1.0); |
84 | 47 | * // returns ~0.47 |
85 | 48 | * |
86 | 49 | * cv = accumulator(); |
87 | 50 | * // returns ~0.47 |
88 | | -* |
89 | | -* @example |
90 | | -* var accumulator = incrcv( 3.14 ); |
91 | 51 | */ |
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 ) ); |
| 52 | +function incrnancv() { |
| 53 | + var cv = incrcv(); |
| 54 | + function accumulator(x) { |
| 55 | + if (arguments.length === 0) { |
| 56 | + return cv(); |
103 | 57 | } |
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; |
| 58 | + if (isnan(x)) { |
| 59 | + return cv(); |
133 | 60 | } |
134 | | - return sqrt( M2/(N-1) ) / mu; |
| 61 | + return cv(x); |
135 | 62 | } |
136 | 63 |
|
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 | | - } |
| 64 | + return accumulator; |
156 | 65 | } |
157 | 66 |
|
158 | 67 |
|
159 | 68 | // EXPORTS // |
160 | 69 |
|
161 | | -module.exports = incrcv; |
| 70 | +module.exports = incrnancv; |
0 commit comments