Skip to content

Commit 6b9a682

Browse files
committed
feat: add stats/incr/nanmcv
--- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown status: passed - task: lint_package_json status: passed - task: lint_repl_help status: na - task: lint_javascript_src status: passed - task: lint_javascript_cli status: na - task: lint_javascript_examples status: passed - task: lint_javascript_tests status: passed - task: lint_javascript_benchmarks status: passed - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: na - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: na - task: lint_typescript_declarations status: passed - task: lint_typescript_tests status: passed - task: lint_license_headers status: passed ---
1 parent 037800a commit 6b9a682

File tree

12 files changed

+1455
-0
lines changed

12 files changed

+1455
-0
lines changed
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
<!--
2+
3+
@license Apache-2.0
4+
5+
Copyright (c) 2025 The Stdlib Authors.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
19+
-->
20+
21+
# incrnanmcv
22+
23+
> Compute a moving [coefficient of variation][coefficient-of-variation] (CV) incrementally, ignoring `NaN` values.
24+
25+
<section class="intro">
26+
27+
For a window of size `W`, the [corrected sample standard deviation][standard-deviation] is defined as
28+
29+
<!-- <equation class="equation" label="eq:corrected_sample_standard_deviation" align="center" raw="s = \sqrt{\frac{1}{W-1} \sum_{i=0}^{W-1} ( x_i - \bar{x} )^2}" alt="Equation for the corrected sample standard deviation."> -->
30+
31+
```math
32+
s = \sqrt{\frac{1}{W-1} \sum_{i=0}^{W-1} ( x_i - \bar{x} )^2}
33+
```
34+
35+
<!-- <div class="equation" align="center" data-raw-text="s = \sqrt{\frac{1}{W-1} \sum_{i=0}^{W-1} ( x_i - \bar{x} )^2}" data-equation="eq:corrected_sample_standard_deviation">
36+
<img src="https://cdn.jsdelivr.net/gh/stdlib-js/stdlib@eed6b690d7c37249b04544b3f5fd36ad8eb3187f/lib/node_modules/@stdlib/stats/incr/nanmcv/docs/img/equation_corrected_sample_standard_deviation.svg" alt="Equation for the corrected sample standard deviation.">
37+
<br>
38+
</div> -->
39+
40+
<!-- </equation> -->
41+
42+
and the [arithmetic mean][arithmetic-mean] is defined as
43+
44+
<!-- <equation class="equation" label="eq:arithmetic_mean" align="center" raw="\bar{x} = \frac{1}{W} \sum_{i=0}^{W-1} x_i" alt="Equation for the arithmetic mean."> -->
45+
46+
```math
47+
\bar{x} = \frac{1}{W} \sum_{i=0}^{W-1} x_i
48+
```
49+
50+
<!-- <div class="equation" align="center" data-raw-text="\bar{x} = \frac{1}{W} \sum_{i=0}^{W-1} x_i" data-equation="eq:arithmetic_mean">
51+
<img src="https://cdn.jsdelivr.net/gh/stdlib-js/stdlib@4cf17e4e25cc2244d5154bd5d251f4bd023748da/lib/node_modules/@stdlib/stats/incr/nanmcv/docs/img/equation_arithmetic_mean.svg" alt="Equation for the arithmetic mean.">
52+
<br>
53+
</div> -->
54+
55+
<!-- </equation> -->
56+
57+
The [coefficient of variation][coefficient-of-variation] (also known as **relative standard deviation**, RSD) is defined as
58+
59+
<!-- <equation class="equation" label="eq:coefficient_of_variation" align="center" raw="c_v = \frac{s}{\bar{x}}" alt="Equation for the coefficient of variation (CV)."> -->
60+
61+
<div class="equation" align="center" data-raw-text="c_v = \frac{s}{\bar{x}}" data-equation="eq:coefficient_of_variation">
62+
<img src="https://cdn.jsdelivr.net/gh/stdlib-js/stdlib@eed6b690d7c37249b04544b3f5fd36ad8eb3187f/lib/node_modules/@stdlib/stats/incr/nanmcv/docs/img/equation_coefficient_of_variation.svg"Equation for the coefficient of variation (CV).">
63+
<br>
64+
</div>
65+
66+
<!-- </equation> -->
67+
68+
</section>
69+
70+
<!-- /.intro -->
71+
72+
<section class="usage">
73+
74+
## Usage
75+
76+
```javascript
77+
var incrnanmcv = require( '@stdlib/stats/incr/nanmcv' );
78+
```
79+
80+
#### incrnanmcv( window\[, mean] )
81+
82+
Returns an accumulator `function` which incrementally computes a moving [coefficient of variation][coefficient-of-variation]. The `window` parameter defines the number of values over which to compute the moving [coefficient of variation][coefficient-of-variation].
83+
84+
```javascript
85+
var accumulator = incrnanmcv( 3 );
86+
```
87+
88+
If the mean is already known, provide a `mean` argument.
89+
90+
```javascript
91+
var accumulator = incrnanmcv( 3, 5.0 );
92+
```
93+
94+
#### accumulator( \[x] )
95+
96+
If provided an input value `x`, the accumulator function returns an updated accumulated value. If not provided an input value `x`, the accumulator function returns the current accumulated value.
97+
98+
```javascript
99+
var incrnanmcv = require( '@stdlib/stats/incr/nanmcv' );
100+
101+
// Create an accumulator with a window size of 3:
102+
var accumulator = incrnanmcv( 3 );
103+
104+
var cv = accumulator();
105+
// returns null
106+
107+
// Fill the window (ignores NaN)...
108+
cv = accumulator( 2.0 ); // [2.0]
109+
// returns 0.0
110+
111+
cv = accumulator( NaN ); // still [2.0]
112+
// returns 0.0 (NaN ignored, window unchanged)
113+
114+
cv = accumulator( 3.0 ); // [2.0, 3.0]
115+
// returns ~0.28
116+
117+
cv = accumulator( 1.0 ); // [2.0, 3.0, 1.0]
118+
// returns ~0.50
119+
120+
// Window begins sliding only when valid number is added...
121+
cv = accumulator( NaN ); // still [2.0, 3.0, 1.0]
122+
// returns ~0.50 (no change)
123+
124+
cv = accumulator( 7.0 ); // [3.0, 1.0, 7.0]
125+
// returns ~0.83
126+
127+
cv = accumulator( 5.0 ); // [1.0, 7.0, 5.0]
128+
// returns ~0.71
129+
130+
cv = accumulator( NaN ); // still [1.0, 7.0, 5.0]
131+
// returns ~0.71
132+
133+
cv = accumulator();
134+
// returns ~0.71
135+
```
136+
137+
</section>
138+
139+
<!-- /.usage -->
140+
141+
<section class="notes">
142+
143+
## Notes
144+
145+
- Input values are not type checked. If provided a `NaN` value, the window remains same and the value is ignored and does not affect the accumulated result. Computations are performed only over valid numeric inputs currently within the moving window. If all window values are `NaN` (or if no valid numeric values have been provided), the accumulated value is `NaN`. If non-numeric inputs are possible, you are advised to type check and handle them accordingly before passing values to the accumulator function.
146+
- As `W` values are needed to fill the window buffer, the first `W-1` returned values are calculated from smaller sample sizes. Until the window is full, each returned value is calculated from all provided values.
147+
- The [coefficient of variation][coefficient-of-variation] is typically computed on nonnegative values. The measure may lack meaning for data which can assume both positive and negative values.
148+
- For small and moderately sized samples, the accumulated value tends to be too low and is thus a **biased** estimator. Provided the generating distribution is known (e.g., a normal distribution), you may want to adjust the accumulated value or use an alternative implementation providing an unbiased estimator.
149+
150+
</section>
151+
152+
<!-- /.notes -->
153+
154+
<section class="examples">
155+
156+
## Examples
157+
158+
<!-- eslint no-undef: "error" -->
159+
160+
```javascript
161+
var randu = require( '@stdlib/random/base/randu' );
162+
var incrnanmcv = require( '@stdlib/stats/incr/nanmcv' );
163+
164+
var accumulator;
165+
var v;
166+
var i;
167+
168+
// Initialize an accumulator with window size 5:
169+
accumulator = incrnanmcv( 5 );
170+
171+
// For each simulated datum, update the moving coefficient of variation...
172+
for ( i = 0; i < 100; i++ ) {
173+
// Introduce NaN values randomly:
174+
if ( randu() < 0.2 ) {
175+
v = NaN;
176+
} else {
177+
v = randu() * 100.0;
178+
}
179+
accumulator( v );
180+
}
181+
console.log( accumulator() );
182+
```
183+
184+
</section>
185+
186+
<!-- /.examples -->
187+
188+
<!-- Section for related `stdlib` packages. Do not manually edit this section, as it is automatically populated. -->
189+
190+
<section class="related">
191+
192+
* * *
193+
194+
## See Also
195+
196+
- <span class="package-name">[`@stdlib/stats/incr/cv`][@stdlib/stats/incr/cv]</span><span class="delimiter">: </span><span class="description">compute the coefficient of variation (CV) incrementally.</span>
197+
- <span class="package-name">[@stdlib/stats/incr/mcv][@stdlib/stats/incr/mcv]</span><span class="delimiter">: </span><span class="description">Compute moving coefficient of variation (CV) incrementally</span>
198+
- <span class="package-name">[`@stdlib/stats/incr/mmean`][@stdlib/stats/incr/mmean]</span><span class="delimiter">: </span><span class="description">compute a moving arithmetic mean incrementally.</span>
199+
- <span class="package-name">[`@stdlib/stats/incr/mstdev`][@stdlib/stats/incr/mstdev]</span><span class="delimiter">: </span><span class="description">compute a moving corrected sample standard deviation incrementally.</span>
200+
- <span class="package-name">[`@stdlib/stats/incr/mvmr`][@stdlib/stats/incr/mvmr]</span><span class="delimiter">: </span><span class="description">compute a moving variance-to-mean ratio (VMR) incrementally.</span>
201+
202+
</section>
203+
204+
<!-- /.related -->
205+
206+
<!-- Section for all links. Make sure to keep an empty line after the `section` element and another before the `/section` close. -->
207+
208+
<section class="links">
209+
210+
[coefficient-of-variation]: https://en.wikipedia.org/wiki/Coefficient_of_variation
211+
212+
[arithmetic-mean]: https://en.wikipedia.org/wiki/Arithmetic_mean
213+
214+
[standard-deviation]: https://en.wikipedia.org/wiki/Standard_deviation
215+
216+
<!-- <related-links> -->
217+
218+
[@stdlib/stats/incr/cv]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/stats/incr/cv
219+
220+
[@stdlib/stats/incr/mmean]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/stats/incr/mmean
221+
222+
[@stdlib/stats/incr/mstdev]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/stats/incr/mstdev
223+
224+
[@stdlib/stats/incr/mvmr]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/stats/incr/mvmr
225+
226+
[@stdlib/stats/incr/mcv]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/stats/incr/mcv
227+
228+
229+
<!-- </related-links> -->
230+
231+
</section>
232+
233+
<!-- /.links -->
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
// MODULES //
22+
23+
var bench = require( '@stdlib/bench' );
24+
var randu = require( '@stdlib/random/base/randu' );
25+
var isnan = require( '@stdlib/assert/is-nan' );
26+
var pkg = require( './../package.json' ).name;
27+
var incrnanmcv = require( './../lib' );
28+
29+
30+
// MAIN //
31+
32+
bench( pkg, function benchmark( b ) {
33+
var f;
34+
var i;
35+
b.tic();
36+
for ( i = 0; i < b.iterations; i++ ) {
37+
f = incrnanmcv( (i % 5) + 1 );
38+
if ( typeof f !== 'function' ) {
39+
b.fail( 'should return a function' );
40+
}
41+
}
42+
b.toc();
43+
if ( typeof f !== 'function' ) {
44+
b.fail( 'should return a function' );
45+
}
46+
b.pass( 'benchmark finished' );
47+
b.end();
48+
});
49+
50+
bench( pkg+'::accumulator', function benchmark( b ) {
51+
var acc;
52+
var v;
53+
var x;
54+
var i;
55+
56+
acc = incrnanmcv( 5 );
57+
58+
b.tic();
59+
for ( i = 0; i < b.iterations; i++ ) {
60+
// Randomly introduce NaN values:
61+
x = ( randu() < 0.1 ) ? NaN : randu();
62+
v = acc( x );
63+
if ( isnan( v ) ) {
64+
b.fail( 'should not return NaN' );
65+
}
66+
}
67+
b.toc();
68+
if ( isnan( v ) ) {
69+
b.fail( 'should not return NaN' );
70+
}
71+
b.pass( 'benchmark finished' );
72+
b.end();
73+
});
74+
75+
bench( pkg+'::accumulator,known_mean', function benchmark( b ) {
76+
var acc;
77+
var v;
78+
var x;
79+
var i;
80+
81+
acc = incrnanmcv( 5, 0.5 );
82+
83+
b.tic();
84+
for ( i = 0; i < b.iterations; i++ ) {
85+
x = ( randu() < 0.1 ) ? NaN : randu();
86+
v = acc( x );
87+
if ( isnan( v ) ) {
88+
b.fail( 'should not return NaN' );
89+
}
90+
}
91+
b.toc();
92+
if ( isnan( v ) ) {
93+
b.fail( 'should not return NaN' );
94+
}
95+
b.pass( 'benchmark finished' );
96+
b.end();
97+
});
Lines changed: 43 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)