Skip to content

Commit a0f761b

Browse files
authored
feat: add support for accessor arrays and refactor stats/base/nanstdevtk
PR-URL: #7589 Closes: #5670 Reviewed-by: Athan Reines <[email protected]>
1 parent 719cc56 commit a0f761b

File tree

12 files changed

+667
-130
lines changed

12 files changed

+667
-130
lines changed

lib/node_modules/@stdlib/stats/base/nanstdevtk/README.md

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ The use of the term `n-1` is commonly referred to as Bessel's correction. Note,
9898
var nanstdevtk = require( '@stdlib/stats/base/nanstdevtk' );
9999
```
100100

101-
#### nanstdevtk( N, correction, x, stride )
101+
#### nanstdevtk( N, correction, x, strideX )
102102

103-
Computes the [standard deviation][standard-deviation] of a strided array `x` ignoring `NaN` values and using a one-pass textbook algorithm.
103+
Computes the [standard deviation][standard-deviation] of a strided array ignoring `NaN` values and using a one-pass textbook algorithm.
104104

105105
```javascript
106106
var x = [ 1.0, -2.0, NaN, 2.0 ];
@@ -114,38 +114,32 @@ The function has the following parameters:
114114
- **N**: number of indexed elements.
115115
- **correction**: degrees of freedom adjustment. Setting this parameter to a value other than `0` has the effect of adjusting the divisor during the calculation of the [standard deviation][standard-deviation] according to `N-c` where `c` corresponds to the provided degrees of freedom adjustment. When computing the [standard deviation][standard-deviation] of a population, setting this parameter to `0` is the standard choice (i.e., the provided array contains data constituting an entire population). When computing the corrected sample [standard deviation][standard-deviation], setting this parameter to `1` is the standard choice (i.e., the provided array contains data sampled from a larger population; this is commonly referred to as Bessel's correction).
116116
- **x**: input [`Array`][mdn-array] or [`typed array`][mdn-typed-array].
117-
- **stride**: index increment for `x`.
117+
- **strideX**: stride length for `x`.
118118

119119
The `N` and `stride` parameters determine which elements in `x` are accessed at runtime. For example, to compute the [standard deviation][standard-deviation] of every other element in `x`,
120120

121121
```javascript
122-
var floor = require( '@stdlib/math/base/special/floor' );
123-
124122
var x = [ 1.0, 2.0, 2.0, -7.0, -2.0, 3.0, 4.0, 2.0, NaN ];
125-
var N = floor( x.length / 2 );
126123

127-
var v = nanstdevtk( N, 1, x, 2 );
124+
var v = nanstdevtk( 5, 1, x, 2 );
128125
// returns 2.5
129126
```
130127

131128
Note that indexing is relative to the first index. To introduce an offset, use [`typed array`][mdn-typed-array] views.
132129

133-
<!-- eslint-disable stdlib/capitalized-comments -->
130+
<!-- eslint-disable stdlib/capitalized-comments, max-len -->
134131

135132
```javascript
136133
var Float64Array = require( '@stdlib/array/float64' );
137-
var floor = require( '@stdlib/math/base/special/floor' );
138134

139-
var x0 = new Float64Array( [ 2.0, 1.0, 2.0, -2.0, -2.0, 2.0, 3.0, 4.0, NaN ] );
135+
var x0 = new Float64Array( [ 2.0, 1.0, 2.0, -2.0, -2.0, 2.0, 3.0, 4.0, NaN, NaN ] );
140136
var x1 = new Float64Array( x0.buffer, x0.BYTES_PER_ELEMENT*1 ); // start at 2nd element
141137

142-
var N = floor( x0.length / 2 );
143-
144-
var v = nanstdevtk( N, 1, x1, 2 );
138+
var v = nanstdevtk( 5, 1, x1, 2 );
145139
// returns 2.5
146140
```
147141

148-
#### nanstdevtk.ndarray( N, correction, x, stride, offset )
142+
#### nanstdevtk.ndarray( N, correction, x, strideX, offsetX )
149143

150144
Computes the [standard deviation][standard-deviation] of a strided array ignoring `NaN` values and using a one-pass textbook algorithm and alternative indexing semantics.
151145

@@ -158,17 +152,14 @@ var v = nanstdevtk.ndarray( x.length, 1, x, 1, 0 );
158152

159153
The function has the following additional parameters:
160154

161-
- **offset**: starting index for `x`.
155+
- **offsetX**: starting index for `x`.
162156

163-
While [`typed array`][mdn-typed-array] views mandate a view offset based on the underlying `buffer`, the `offset` parameter supports indexing semantics based on a starting index. For example, to calculate the [standard deviation][standard-deviation] for every other value in `x` starting from the second value
157+
While [`typed array`][mdn-typed-array] views mandate a view offset based on the underlying buffer, the offset parameter supports indexing semantics based on a starting index. For example, to calculate the [standard deviation][standard-deviation] for every other element in `x` starting from the second element
164158

165159
```javascript
166-
var floor = require( '@stdlib/math/base/special/floor' );
160+
var x = [ 2.0, 1.0, 2.0, -2.0, -2.0, 2.0, 3.0, 4.0, NaN, NaN ];
167161

168-
var x = [ 2.0, 1.0, 2.0, -2.0, -2.0, 2.0, 3.0, 4.0 ];
169-
var N = floor( x.length / 2 );
170-
171-
var v = nanstdevtk.ndarray( N, 1, x, 2, 1 );
162+
var v = nanstdevtk.ndarray( 5, 1, x, 2, 1 );
172163
// returns 2.5
173164
```
174165

@@ -183,6 +174,7 @@ var v = nanstdevtk.ndarray( N, 1, x, 2, 1 );
183174
- If `N <= 0`, both functions return `NaN`.
184175
- If `n - c` is less than or equal to `0` (where `c` corresponds to the provided degrees of freedom adjustment and `n` corresponds to the number of non-`NaN` indexed elements), both functions return `NaN`.
185176
- Some caution should be exercised when using the one-pass textbook algorithm. Literature overwhelmingly discourages the algorithm's use for two reasons: 1) the lack of safeguards against underflow and overflow and 2) the risk of catastrophic cancellation when subtracting the two sums if the sums are large and the variance small. These concerns have merit; however, the one-pass textbook algorithm should not be dismissed outright. For data distributions with a moderately large standard deviation to mean ratio (i.e., **coefficient of variation**), the one-pass textbook algorithm may be acceptable, especially when performance is paramount and some precision loss is acceptable (including a risk of computing a negative variance due to floating-point rounding errors!). In short, no single "best" algorithm for computing the standard deviation exists. The "best" algorithm depends on the underlying data distribution, your performance requirements, and your minimum precision requirements. When evaluating which algorithm to use, consider the relative pros and cons, and choose the algorithm which best serves your needs.
177+
- Both functions support array-like objects having getter and setter accessors for array element access (e.g., [`@stdlib/array/base/accessor`][@stdlib/array/base/accessor]).
186178
- Depending on the environment, the typed versions ([`dnanstdevtk`][@stdlib/stats/strided/dnanstdevtk], [`snanstdevtk`][@stdlib/stats/base/snanstdevtk], etc.) are likely to be significantly more performant.
187179

188180
</section>
@@ -196,18 +188,19 @@ var v = nanstdevtk.ndarray( N, 1, x, 2, 1 );
196188
<!-- eslint no-undef: "error" -->
197189

198190
```javascript
199-
var randu = require( '@stdlib/random/base/randu' );
200-
var round = require( '@stdlib/math/base/special/round' );
201-
var Float64Array = require( '@stdlib/array/float64' );
191+
var uniform = require( '@stdlib/random/base/uniform' );
192+
var filledarrayBy = require( '@stdlib/array/filled-by' );
193+
var bernoulli = require( '@stdlib/random/base/bernoulli' );
202194
var nanstdevtk = require( '@stdlib/stats/base/nanstdevtk' );
203195

204-
var x;
205-
var i;
206-
207-
x = new Float64Array( 10 );
208-
for ( i = 0; i < x.length; i++ ) {
209-
x[ i ] = round( (randu()*100.0) - 50.0 );
196+
function rand() {
197+
if ( bernoulli( 0.8 ) < 1 ) {
198+
return NaN;
199+
}
200+
return uniform( -50.0, 50.0 );
210201
}
202+
203+
var x = filledarrayBy( 10, 'float64', rand );
211204
console.log( x );
212205

213206
var v = nanstdevtk( x.length, 1, x, 1 );
@@ -258,6 +251,8 @@ console.log( v );
258251

259252
[mdn-typed-array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
260253

254+
[@stdlib/array/base/accessor]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/array/base/accessor
255+
261256
[@ling:1974a]: https://doi.org/10.2307/2286154
262257

263258
<!-- <related-links> -->

lib/node_modules/@stdlib/stats/base/nanstdevtk/benchmark/benchmark.js

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,30 @@
2121
// MODULES //
2222

2323
var bench = require( '@stdlib/bench' );
24-
var randu = require( '@stdlib/random/base/randu' );
24+
var uniform = require( '@stdlib/random/base/uniform' );
25+
var bernoulli = require( '@stdlib/random/base/bernoulli' );
26+
var filledarrayBy = require( '@stdlib/array/filled-by' );
2527
var isnan = require( '@stdlib/math/base/assert/is-nan' );
2628
var pow = require( '@stdlib/math/base/special/pow' );
2729
var pkg = require( './../package.json' ).name;
28-
var nanstdevtk = require( './../lib/nanstdevtk.js' );
30+
var nanstdevtk = require( './../lib/main.js' );
2931

3032

3133
// FUNCTIONS //
3234

35+
/**
36+
* Returns a random number.
37+
*
38+
* @private
39+
* @returns {number} random number
40+
*/
41+
function rand() {
42+
if ( bernoulli( 0.8 ) < 1 ) {
43+
return NaN;
44+
}
45+
return uniform( -10.0, 10.0 );
46+
}
47+
3348
/**
3449
* Creates a benchmark function.
3550
*
@@ -38,17 +53,7 @@ var nanstdevtk = require( './../lib/nanstdevtk.js' );
3853
* @returns {Function} benchmark function
3954
*/
4055
function createBenchmark( len ) {
41-
var x;
42-
var i;
43-
44-
x = [];
45-
for ( i = 0; i < len; i++ ) {
46-
if ( randu() < 0.2 ) {
47-
x.push( NaN );
48-
} else {
49-
x.push( ( randu()*20.0 ) - 10.0 );
50-
}
51-
}
56+
var x = filledarrayBy( len, 'generic', rand );
5257
return benchmark;
5358

5459
function benchmark( b ) {

lib/node_modules/@stdlib/stats/base/nanstdevtk/benchmark/benchmark.ndarray.js

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
// MODULES //
2222

2323
var bench = require( '@stdlib/bench' );
24-
var randu = require( '@stdlib/random/base/randu' );
24+
var uniform = require( '@stdlib/random/base/uniform' );
25+
var bernoulli = require( '@stdlib/random/base/bernoulli' );
26+
var filledarrayBy = require( '@stdlib/array/filled-by' );
2527
var isnan = require( '@stdlib/math/base/assert/is-nan' );
2628
var pow = require( '@stdlib/math/base/special/pow' );
2729
var pkg = require( './../package.json' ).name;
@@ -30,6 +32,19 @@ var nanstdevtk = require( './../lib/ndarray.js' );
3032

3133
// FUNCTIONS //
3234

35+
/**
36+
* Returns a random number.
37+
*
38+
* @private
39+
* @returns {number} random number
40+
*/
41+
function rand() {
42+
if ( bernoulli( 0.8 ) < 1 ) {
43+
return NaN;
44+
}
45+
return uniform( -10.0, 10.0 );
46+
}
47+
3348
/**
3449
* Creates a benchmark function.
3550
*
@@ -38,17 +53,7 @@ var nanstdevtk = require( './../lib/ndarray.js' );
3853
* @returns {Function} benchmark function
3954
*/
4055
function createBenchmark( len ) {
41-
var x;
42-
var i;
43-
44-
x = [];
45-
for ( i = 0; i < len; i++ ) {
46-
if ( randu() < 0.2 ) {
47-
x.push( NaN );
48-
} else {
49-
x.push( ( randu()*20.0 ) - 10.0 );
50-
}
51-
}
56+
var x = filledarrayBy( len, 'generic', rand );
5257
return benchmark;
5358

5459
function benchmark( b ) {

lib/node_modules/@stdlib/stats/base/nanstdevtk/docs/repl.txt

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11

2-
{{alias}}( N, correction, x, stride )
2+
{{alias}}( N, correction, x, strideX )
33
Computes the standard deviation of a strided array ignoring `NaN` values and
44
using a one-pass textbook algorithm.
55

6-
The `N` and `stride` parameters determine which elements in `x` are accessed
7-
at runtime.
6+
The `N` and stride parameters determine which elements in the strided array
7+
are accessed at runtime.
88

99
Indexing is relative to the first index. To introduce an offset, use a typed
1010
array view.
@@ -33,8 +33,8 @@
3333
x: Array<number>|TypedArray
3434
Input array.
3535

36-
stride: integer
37-
Index increment.
36+
strideX: integer
37+
Stride length.
3838

3939
Returns
4040
-------
@@ -48,20 +48,19 @@
4848
> {{alias}}( x.length, 1, x, 1 )
4949
~2.0817
5050

51-
// Using `N` and `stride` parameters:
52-
> x = [ -2.0, 1.0, 1.0, -5.0, 2.0, -1.0 ];
53-
> var N = {{alias:@stdlib/math/base/special/floor}}( x.length / 2 );
54-
> {{alias}}( N, 1, x, 2 )
51+
// Using `N` and stride parameters:
52+
> x = [ -2.0, 1.0, 1.0, -5.0, 2.0, -1.0, NaN ];
53+
> {{alias}}( 4, 1, x, 2 )
5554
~2.0817
5655

5756
// Using view offsets:
58-
> var x0 = new {{alias:@stdlib/array/float64}}( [ 1.0, -2.0, 3.0, 2.0, 5.0, -1.0 ] );
57+
> var x0 = new {{alias:@stdlib/array/float64}}( [ 1.0, -2.0, 3.0, 2.0, 5.0, -1.0, NaN, NaN ] );
5958
> var x1 = new {{alias:@stdlib/array/float64}}( x0.buffer, x0.BYTES_PER_ELEMENT*1 );
60-
> N = {{alias:@stdlib/math/base/special/floor}}( x0.length / 2 );
61-
> {{alias}}( N, 1, x1, 2 )
59+
> {{alias}}( 4, 1, x1, 2 )
6260
~2.0817
6361

64-
{{alias}}.ndarray( N, correction, x, stride, offset )
62+
63+
{{alias}}.ndarray( N, correction, x, strideX, offsetX )
6564
Computes the standard deviation of a strided array ignoring `NaN` values and
6665
using a one-pass textbook algorithm and alternative indexing semantics.
6766

@@ -89,10 +88,10 @@
8988
x: Array<number>|TypedArray
9089
Input array.
9190

92-
stride: integer
93-
Index increment.
91+
strideX: integer
92+
Stride length.
9493

95-
offset: integer
94+
offsetX: integer
9695
Starting index.
9796

9897
Returns
@@ -108,9 +107,8 @@
108107
~2.0817
109108

110109
// Using offset parameter:
111-
> var x = [ 1.0, -2.0, 3.0, 2.0, 5.0, -1.0 ];
112-
> var N = {{alias:@stdlib/math/base/special/floor}}( x.length / 2 );
113-
> {{alias}}.ndarray( N, 1, x, 2, 1 )
110+
> x = [ 1.0, -2.0, 3.0, 2.0, 5.0, -1.0, NaN, NaN ];
111+
> {{alias}}.ndarray( 4, 1, x, 2, 1 )
114112
~2.0817
115113

116114
See Also

lib/node_modules/@stdlib/stats/base/nanstdevtk/docs/types/index.d.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@
2020

2121
/// <reference types="@stdlib/types"/>
2222

23-
import { NumericArray } from '@stdlib/types/array';
23+
import { NumericArray, Collection, AccessorArrayLike } from '@stdlib/types/array';
24+
25+
/**
26+
* Input array.
27+
*/
28+
type InputArray = NumericArray | Collection<number> | AccessorArrayLike<number>;
2429

2530
/**
2631
* Interface describing `nanstdevtk`.
@@ -32,7 +37,7 @@ interface Routine {
3237
* @param N - number of indexed elements
3338
* @param correction - degrees of freedom adjustment
3439
* @param x - input array
35-
* @param stride - stride length
40+
* @param strideX - stride length
3641
* @returns standard deviation
3742
*
3843
* @example
@@ -41,16 +46,16 @@ interface Routine {
4146
* var v = nanstdevtk( x.length, 1, x, 1 );
4247
* // returns ~2.0817
4348
*/
44-
( N: number, correction: number, x: NumericArray, stride: number ): number;
49+
( N: number, correction: number, x: InputArray, strideX: number ): number;
4550

4651
/**
4752
* Computes the standard deviation of a strided array ignoring `NaN` values and using a one-pass textbook algorithm and alternative indexing semantics.
4853
*
4954
* @param N - number of indexed elements
5055
* @param correction - degrees of freedom adjustment
5156
* @param x - input array
52-
* @param stride - stride length
53-
* @param offset - starting index
57+
* @param strideX - stride length
58+
* @param offsetX - starting index
5459
* @returns standard deviation
5560
*
5661
* @example
@@ -59,7 +64,7 @@ interface Routine {
5964
* var v = nanstdevtk.ndarray( x.length, 1, x, 1, 0 );
6065
* // returns ~2.0817
6166
*/
62-
ndarray( N: number, correction: number, x: NumericArray, stride: number, offset: number ): number;
67+
ndarray( N: number, correction: number, x: InputArray, strideX: number, offsetX: number ): number;
6368
}
6469

6570
/**
@@ -68,7 +73,7 @@ interface Routine {
6873
* @param N - number of indexed elements
6974
* @param correction - degrees of freedom adjustment
7075
* @param x - input array
71-
* @param stride - stride length
76+
* @param strideX - stride length
7277
* @returns standard deviation
7378
*
7479
* @example

lib/node_modules/@stdlib/stats/base/nanstdevtk/docs/types/test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* limitations under the License.
1717
*/
1818

19+
import AccessorArray = require( '@stdlib/array/base/accessor' );
1920
import nanstdevtk = require( './index' );
2021

2122

@@ -26,6 +27,7 @@ import nanstdevtk = require( './index' );
2627
const x = new Float64Array( 10 );
2728

2829
nanstdevtk( x.length, 1, x, 1 ); // $ExpectType number
30+
nanstdevtk( x.length, 1, new AccessorArray( x ), 1 ); // $ExpectType number
2931
}
3032

3133
// The compiler throws an error if the function is provided a first argument which is not a number...
@@ -101,6 +103,7 @@ import nanstdevtk = require( './index' );
101103
const x = new Float64Array( 10 );
102104

103105
nanstdevtk.ndarray( x.length, 1, x, 1, 0 ); // $ExpectType number
106+
nanstdevtk.ndarray( x.length, 1, new AccessorArray( x ), 1, 0 ); // $ExpectType number
104107
}
105108

106109
// The compiler throws an error if the `ndarray` method is provided a first argument which is not a number...

0 commit comments

Comments
 (0)