diff --git a/lib/node_modules/@stdlib/stats/spearman-test/README.md b/lib/node_modules/@stdlib/stats/spearman-test/README.md new file mode 100644 index 000000000000..91c02f30a2d8 --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/README.md @@ -0,0 +1,211 @@ + + +# Spearman Correlation Test + +> Compute a Spearman rank correlation test between paired samples. + +
+ +The [Spearman rank correlation coefficient][spearman-correlation] is a non-parametric measure of rank correlation that assesses how well the relationship between two variables can be described using a monotonic function. Unlike the [Pearson correlation][pearson-correlation], which assesses linear relationships, the Spearman correlation assesses monotonic relationships (whether linear or not). + +For a sample of size `n`, the Spearman rank correlation coefficient is defined as the [Pearson correlation coefficient][pearson-correlation] between the rank variables. + +
+ + + +
+ +## Usage + +```javascript +var spearmanTest = require( '@stdlib/stats/spearman-test' ); +``` + +#### spearmanTest( x, y\[, opts] ) + +By default, the function performs a t-test for the null hypothesis that the paired data in [arrays][mdn-array] or [typed arrays][mdn-typed-array] `x` and `y` have a [Spearman correlation coefficient][spearman-correlation] of zero. + +```javascript +var x = [ 0.7, -1.6, -0.2, -1.2, -0.1, 3.4, 3.7, 0.8, 0.0, 2.0 ]; +var y = [ 1.9, 0.8, 1.1, 0.1, -0.1, 4.4, 5.5, 1.6, 4.6, 3.4 ]; + +var out = spearmanTest( x, y ); +/* e.g., returns + { + 'alpha': 0.05, + 'rejected': true, + 'pValue': ~0.014, + 'statistic': ~2.98, + 'ci': [ ~0.162, ~0.95 ], + 'nullValue': 0, + 'scorr': ~0.745, + // ... + } +*/ +``` + +The returned object comes with a `.print()` method which when invoked will print a formatted output of the results of the hypothesis test. `print` accepts a `digits` option that controls the number of decimal digits displayed for the outputs and a `decision` option, which when set to `false` will hide the test decision. + + + +```javascript +console.log( out.print() ); +/* e.g., => + t-test for Spearman's rank correlation coefficient + + Alternative hypothesis: True correlation coefficient is not equal to 0 + + pValue: 0.014 + statistic: 2.9848 + 95% confidence interval: [0.1623,0.9504] + + Test Decision: Reject null in favor of alternative at 5% significance level +*/ +``` + +The function accepts the following `options`: + +- **alpha**: `number` in the interval `[0,1]` giving the significance level of the hypothesis test. Default: `0.05`. +- **alternative**: Either `two-sided`, `less` or `greater`. Indicates whether the alternative hypothesis is that `x` has a larger (or smaller) Spearman rank correlation with `y` than `rho` (`two-sided`), that `x` has a larger rank correlation than `rho` (`greater`) or a smaller rank correlation than `rho` (`less`). Default: `two-sided`. +- **rho**: `number` denoting the correlation under the null hypothesis. Default: `0`. + +By default, a two-sided test is performed. To perform either of the one-sided tests, set the `alternative` option. + +```javascript +var x = [ 0.7, -1.6, -0.2, -1.2, -0.1, 3.4, 3.7, 0.8, 0.0, 2.0 ]; +var y = [ 1.9, 0.8, 1.1, 0.1, -0.1, 4.4, 5.5, 1.6, 4.6, 3.4 ]; + +var out = spearmanTest( x, y, { + 'alternative': 'less' +}); +/* e.g., returns + { + 'alpha': 0.05, + 'rejected': false, + 'pValue': ~0.993, + // ... + } +*/ + +out = spearmanTest( x, y, { + 'alternative': 'greater' +}); +/* e.g., returns + { + 'alpha': 0.05, + 'rejected': true, + 'pValue': ~0.007, + // ... + } +*/ +``` + +By default, the null hypothesis is that the correlation coefficient is `0`. To test against a different value, set the `rho` option. + +```javascript +var x = [ 0.7, -1.6, -0.2, -1.2, -0.1, 3.4, 3.7, 0.8, 0.0, 2.0 ]; +var y = [ 1.9, 0.8, 1.1, 0.1, -0.1, 4.4, 5.5, 1.6, 4.6, 3.4 ]; + +var out = spearmanTest( x, y, { + 'rho': 0.8 +}); +``` + +To change the significance level, set the `alpha` option. + +```javascript +var x = [ 0.7, -1.6, -0.2, -1.2, -0.1, 3.4, 3.7, 0.8, 0.0, 2.0 ]; +var y = [ 1.9, 0.8, 1.1, 0.1, -0.1, 4.4, 5.5, 1.6, 4.6, 3.4 ]; + +var out = spearmanTest( x, y, { + 'alpha': 0.01 +}); +``` + +
+ + + +
+ +## Examples + + + +```javascript +var rnorm = require( '@stdlib/random/base/normal' ); +var sqrt = require( '@stdlib/math/base/special/sqrt' ); +var spearmanTest = require( '@stdlib/stats/spearman-test' ); + +var table; +var out; +var rho; +var x; +var y; +var i; + +rho = 0.5; +x = []; +y = []; +for ( i = 0; i < 300; i++ ) { + x.push( rnorm( 0.0, 1.0 ) ); + y.push( ( rho * x[ i ] ) + rnorm( 0.0, sqrt( 1.0 - (rho*rho) ) ) ); +} + +out = spearmanTest( x, y ); +table = out.print(); +console.log( table ); + +out = spearmanTest( x, y, { + 'rho': 0.5 +}); +table = out.print(); +console.log( table ); +``` + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/stats/spearman-test/benchmark/benchmark.js b/lib/node_modules/@stdlib/stats/spearman-test/benchmark/benchmark.js new file mode 100644 index 000000000000..b2df84a0172c --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/benchmark/benchmark.js @@ -0,0 +1,138 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var bench = require( '@stdlib/bench' ); +var isObject = require( '@stdlib/assert/is-object' ); +var isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var rnorm = require( '@stdlib/random/base/normal' ); +var sqrt = require( '@stdlib/math/base/special/sqrt' ); +var pkg = require( './../package.json' ).name; +var spearmanTest = require( './../lib' ); + + +// MAIN // + +bench( pkg, function benchmark( b ) { + var result; + var rho; + var idx; + var y; + var x; + var i; + + rho = 0.5; + x = []; + y = []; + for ( i = 0; i < 300; i++ ) { + x.push( rnorm( 0.0, 1.0 ) ); + y.push( ( rho * x[ i ] ) + rnorm( 0.0, sqrt( 1.0 - (rho*rho) ) ) ); + } + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + idx = i % x.length; + y[ idx ] = ( rho * x[ i ] ) + rnorm( 0.0, sqrt( 1.0 - (rho*rho) ) ); + result = spearmanTest( x, y ); + if ( typeof result !== 'object' ) { + b.fail( 'should return an object' ); + } + } + b.toc(); + if ( !isObject( result ) ) { + b.fail( 'should return an object' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+'::one-sided', function benchmark( b ) { + var result; + var opts; + var rho; + var idx; + var y; + var x; + var i; + + rho = 0.5; + x = []; + y = []; + for ( i = 0; i < 300; i++ ) { + x.push( rnorm( 0.0, 1.0 ) ); + y.push( ( rho * x[ i ] ) + rnorm( 0.0, sqrt( 1.0 - (rho*rho) ) ) ); + } + opts = { + 'alternative': 'less' + }; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + idx = i % x.length; + y[ idx ] = ( rho * x[ i ] ) + rnorm( 0.0, sqrt( 1.0 - (rho*rho) ) ); + result = spearmanTest( x, y, opts ); + if ( typeof result !== 'object' ) { + b.fail( 'should return an object' ); + } + } + b.toc(); + if ( !isObject( result ) ) { + b.fail( 'should return an object' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':print', function benchmark( b ) { + var digits; + var result; + var output; + var rho; + var y; + var x; + var i; + + rho = 0.5; + x = []; + y = []; + for ( i = 0; i < 300; i++ ) { + x.push( rnorm( 0.0, 1.0 ) ); + y.push( ( rho * x[ i ] ) + rnorm( 0.0, sqrt( 1.0 - (rho*rho) ) ) ); + } + result = spearmanTest( x, y ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + digits = ( i % 8 ) + 1; + output = result.print({ + 'digits': digits + }); + if ( typeof output !== 'string' ) { + b.fail( 'should return a string' ); + } + } + b.toc(); + if ( !isString( output ) ) { + b.fail( 'should return a string' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/lib/node_modules/@stdlib/stats/spearman-test/docs/repl.txt b/lib/node_modules/@stdlib/stats/spearman-test/docs/repl.txt new file mode 100644 index 000000000000..a2a587871548 --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/docs/repl.txt @@ -0,0 +1,113 @@ + +{{alias}}( x, y[, options] ) + Computes a Spearman rank correlation test between paired samples. + + By default, the function performs a t-test for the null hypothesis that the + data in arrays or typed arrays `x` and `y` have a Spearman rank correlation + coefficient of zero. A test against a different population correlation can + be carried out by supplying the `rho` option. In this case, a test using + Fisher's z transform is conducted. + + The returned object comes with a `.print()` method which when invoked will + print a formatted output of the results of the hypothesis test. + + Parameters + ---------- + x: Array + First data array. + + y: Array + Second data array. + + options: Object (optional) + Options. + + options.alpha: number (optional) + Number in the interval `[0,1]` giving the significance level of the + hypothesis test. Default: `0.05`. + + options.alternative: string (optional) + Either `two-sided`, `less` or `greater`. Indicates whether the + alternative hypothesis is that `x` has a larger Spearman rank + correlation with `y` than `rho` (`greater`), `x` has a smaller rank + correlation than `rho` (`less`) or the rank correlations are the same + (`two-sided`). Default: `'two-sided'`. + + options.rho: number (optional) + Number denoting the correlation under the null hypothesis. + Default: `0`. + + Returns + ------- + out: Object + Test result object. + + out.alpha: number + Used significance level. + + out.rejected: boolean + Test decision. + + out.pValue: number + p-value of the test. + + out.statistic: number + Value of test statistic. + + out.ci: Array + 1-alpha confidence interval for the Spearman rank correlation + coefficient. + + out.nullValue: number + Assumed correlation under H0 (equal to the supplied `rho` option). + + out.scorr: number + Spearman rank correlation coefficient. + + out.alternative: string + Alternative hypothesis (`two-sided`, `less` or `greater`). + + out.method: string + Name of test. + + out.print: Function + Function to print formatted output. + + Examples + -------- + > var rho = 0.5; + > var x = new Array( 300 ); + > var y = new Array( 300 ); + > for ( var i = 0; i < 300; i++ ) { + ... x[ i ] = {{alias:@stdlib/random/base/normal}}( 0.0, 1.0 ); + ... y[ i ] = ( rho * x[ i ] ) + {{alias:@stdlib/random/base/normal}}( 0.0, + ... {{alias:@stdlib/math/base/special/sqrt}}( 1.0 - (rho*rho) ) ); + ... }; + > var out = {{alias}}( x, y ); + { + alpha: 0.05, + rejected: true, + pValue: 0, + statistic: 10.115805615994121, + ci: [ 0.4161679018930295, 0.5853122968949995 ], + alternative: 'two-sided', + method: 't-test for Spearman\'s rank correlation coefficient', + nullValue: 0, + scorr: 0.505582072355616, + } + + // Print output: + > var table = out.print(); + t-test for Spearman's rank correlation coefficient + + Alternative hypothesis: True correlation coefficient is not equal to 0 + + pValue: 0 + statistic: 10.1158 + 95% confidence interval: [0.4162,0.5853] + + Test Decision: Reject null in favor of alternative at 5% significance level + + See Also + -------- + diff --git a/lib/node_modules/@stdlib/stats/spearman-test/docs/types/index.d.ts b/lib/node_modules/@stdlib/stats/spearman-test/docs/types/index.d.ts new file mode 100644 index 000000000000..ee39de7eb8e7 --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/docs/types/index.d.ts @@ -0,0 +1,130 @@ +/* +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +// TypeScript Version: 4.1 + +/// + +import { NumericArray } from '@stdlib/types/array'; + +/** +* Interface defining function options. +*/ +interface Options { + /** + * Significance level (default: 0.05). + */ + alpha?: number; + + /** + * Alternative hypothesis (`two-sided`, `less`, or `greater`; default: 'two-sided'). + */ + alternative?: 'two-sided' | 'less' | 'greater'; + + /** + * Correlation under H0 (default: 0.0). + */ + rho?: number; +} + +/** +* Test result. +*/ +interface Results { + /** + * Used significance level. + */ + alpha: number; + + /** + * Test decision. + */ + rejected: boolean; + + /** + * p-value of the test. + */ + pValue: number; + + /** + * Value of test statistic. + */ + statistic: number; + + /** + * 1-alpha confidence interval for the Spearman rank correlation coefficient. + */ + ci: Array; + + /** + * Spearman rank correlation coefficient. + */ + scorr: number; + + /** + * Assumed correlation under H0 (equal to the supplied `rho` option). + */ + nullValue: number; + + /** + * Alternative hypothesis (`two-sided`, `less` or `greater`). + */ + alternative: string; + + /** + * Name of test. + */ + method: string; + + /** + * Function to print formatted output. + */ + print: Function; +} + + +/** +* Computes a Spearman rank correlation test between paired samples. +* +* ## Notes +* +* - By default, the function performs a t-test for the null hypothesis that the data in arrays or typed arrays `x` and `y` have a Spearman rank correlation coefficient of zero. A test against a different population correlation can be carried out by supplying the `rho` option. In this case, a test using Fisher's z transform is conducted. +* - The returned object comes with a `.print()` method which when invoked will print a formatted output of the results of the hypothesis test. +* +* @param x - first data array +* @param y - second data array +* @param options - function options +* @param options.alpha - significance level (default: 0.05) +* @param options.alternative - alternative hypothesis (`two-sided`, `less` or `greater`; default: 'two-sided') +* @param options.rho - correlation under H0 (default: 0.0) +* @throws x and y must be arrays of the same length +* @throws x and y must contain at least four elements +* @throws must provide valid options +* @returns test result object +* +* @example +* var x = [ 2, 4, 3, 1, 2, 3 ]; +* var y = [ 3, 2, 4, 1, 2, 4 ]; +* var out = spearmanTest( x, y ); +*/ +declare function spearmanTest( x: NumericArray, y: NumericArray, options?: Options ): Results; + + +// EXPORTS // + +export = spearmanTest; diff --git a/lib/node_modules/@stdlib/stats/spearman-test/docs/types/test.ts b/lib/node_modules/@stdlib/stats/spearman-test/docs/types/test.ts new file mode 100644 index 000000000000..2b700c5958b9 --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/docs/types/test.ts @@ -0,0 +1,120 @@ +/* +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import spearmanTest = require( './index' ); + + +// TESTS // + +// The function returns a test result object... +{ + const x = [ 2, 4, 3, 1, 2, 3 ]; + const y = [ 3, 2, 4, 1, 2, 4 ]; + spearmanTest( x, y ); // $ExpectType Results + spearmanTest( x, y, { 'alpha': 0.1 } ); // $ExpectType Results + spearmanTest( x, y, { 'alternative': 'less' } ); // $ExpectType Results + spearmanTest( x, y, { 'rho': 0.3 } ); // $ExpectType Results +} + +// The compiler throws an error if the function is provided a first argument that is not a numeric array... +{ + const y = [ 3, 2, 4, 1, 2, 4 ]; + spearmanTest( 'abc', y ); // $ExpectError + spearmanTest( true, y ); // $ExpectError + spearmanTest( false, y ); // $ExpectError + spearmanTest( null, y ); // $ExpectError + spearmanTest( undefined, y ); // $ExpectError + spearmanTest( 5, y ); // $ExpectError + spearmanTest( {}, y ); // $ExpectError + spearmanTest( ( x: number ): number => x, y ); // $ExpectError +} + +// The compiler throws an error if the function is provided a second argument that is not a numeric array... +{ + const x = [ 2, 4, 3, 1, 2, 3 ]; + spearmanTest( x, 'abc' ); // $ExpectError + spearmanTest( x, true ); // $ExpectError + spearmanTest( x, false ); // $ExpectError + spearmanTest( x, null ); // $ExpectError + spearmanTest( x, undefined ); // $ExpectError + spearmanTest( x, 5 ); // $ExpectError + spearmanTest( x, {} ); // $ExpectError + spearmanTest( x, ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the function is provided a third argument which is not an options object... +{ + const x = [ 2, 4, 3, 1, 2, 3 ]; + const y = [ 3, 2, 4, 1, 2, 4 ]; + spearmanTest( x, y, true ); // $ExpectError + spearmanTest( x, y, false ); // $ExpectError + spearmanTest( x, y, null ); // $ExpectError + spearmanTest( x, y, 5 ); // $ExpectError + spearmanTest( x, y, 'abc' ); // $ExpectError + spearmanTest( x, y, ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the function is provided a `alpha` option which is not a number... +{ + const x = [ 2, 4, 3, 1, 2, 3 ]; + const y = [ 3, 2, 4, 1, 2, 4 ]; + spearmanTest( x, y, { 'alpha': 'abc' } ); // $ExpectError + spearmanTest( x, y, { 'alpha': '123' } ); // $ExpectError + spearmanTest( x, y, { 'alpha': true } ); // $ExpectError + spearmanTest( x, y, { 'alpha': false } ); // $ExpectError + spearmanTest( x, y, { 'alpha': null } ); // $ExpectError + spearmanTest( x, y, { 'alpha': [] } ); // $ExpectError + spearmanTest( x, y, { 'alpha': {} } ); // $ExpectError + spearmanTest( x, y, { 'alpha': ( x: number ): number => x } ); // $ExpectError +} + +// The compiler throws an error if the function is provided a `rho` option which is not a number... +{ + const x = [ 2, 4, 3, 1, 2, 3 ]; + const y = [ 3, 2, 4, 1, 2, 4 ]; + spearmanTest( x, y, { 'rho': 'abc' } ); // $ExpectError + spearmanTest( x, y, { 'rho': '123' } ); // $ExpectError + spearmanTest( x, y, { 'rho': true } ); // $ExpectError + spearmanTest( x, y, { 'rho': false } ); // $ExpectError + spearmanTest( x, y, { 'rho': null } ); // $ExpectError + spearmanTest( x, y, { 'rho': {} } ); // $ExpectError + spearmanTest( x, y, { 'rho': [] } ); // $ExpectError + spearmanTest( x, y, { 'rho': ( x: number ): number => x } ); // $ExpectError +} + +// The compiler throws an error if the function is provided an `alternative` option which is not a known test direction... +{ + const x = [ 2, 4, 3, 1, 2, 3 ]; + const y = [ 3, 2, 4, 1, 2, 4 ]; + spearmanTest( x, y, { 'alternative': 'abc' } ); // $ExpectError + spearmanTest( x, y, { 'alternative': 123 } ); // $ExpectError + spearmanTest( x, y, { 'alternative': true } ); // $ExpectError + spearmanTest( x, y, { 'alternative': false } ); // $ExpectError + spearmanTest( x, y, { 'alternative': null } ); // $ExpectError + spearmanTest( x, y, { 'alternative': [] } ); // $ExpectError + spearmanTest( x, y, { 'alternative': {} } ); // $ExpectError + spearmanTest( x, y, { 'alternative': ( x: number ): number => x } ); // $ExpectError +} + +// The compiler throws an error if the function is provided an invalid number of arguments... +{ + const x = [ 2, 4, 3, 1, 2, 3 ]; + const y = [ 3, 2, 4, 1, 2, 4 ]; + spearmanTest( x ); // $ExpectError + spearmanTest( x, y, {}, {} ); // $ExpectError +} diff --git a/lib/node_modules/@stdlib/stats/spearman-test/examples/index.js b/lib/node_modules/@stdlib/stats/spearman-test/examples/index.js new file mode 100644 index 000000000000..c629c1e19db5 --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/examples/index.js @@ -0,0 +1,48 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +var rnorm = require( '@stdlib/random/base/normal' ); +var sqrt = require( '@stdlib/math/base/special/sqrt' ); +var spearmanTest = require( './../lib' ); + +var table; +var out; +var rho; +var x; +var y; +var i; + +rho = 0.5; +x = []; +y = []; +for ( i = 0; i < 300; i++ ) { + x.push( rnorm( 0.0, 1.0 ) ); + y.push( ( rho * x[ i ] ) + rnorm( 0.0, sqrt( 1.0 - (rho*rho) ) ) ); +} + +out = spearmanTest( x, y ); +table = out.print(); +console.log( table ); + +out = spearmanTest( x, y, { + 'rho': 0.5 +}); +table = out.print(); +console.log( table ); diff --git a/lib/node_modules/@stdlib/stats/spearman-test/lib/index.js b/lib/node_modules/@stdlib/stats/spearman-test/lib/index.js new file mode 100644 index 000000000000..eee6ff767f9d --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/lib/index.js @@ -0,0 +1,43 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** +* Compute a Spearman rank correlation test between paired samples. +* +* @module @stdlib/stats/spearman-test +* +* @example +* var spearmanTest = require( '@stdlib/stats/spearman-test' ); +* +* var x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; +* var y = [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]; +* +* var out = spearmanTest( x, y ); +* var table = out.print(); +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; diff --git a/lib/node_modules/@stdlib/stats/spearman-test/lib/main.js b/lib/node_modules/@stdlib/stats/spearman-test/lib/main.js new file mode 100644 index 000000000000..f6e8b2d27569 --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/lib/main.js @@ -0,0 +1,206 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var isNumberArray = require( '@stdlib/assert/is-number-array' ).primitives; +var isTypedArrayLike = require( '@stdlib/assert/is-typed-array-like' ); +var setReadOnly = require( '@stdlib/utils/define-read-only-property' ); +var quantileFactory = require( '@stdlib/stats/base/dists/normal/quantile' ).factory; +var cdfFactory = require( '@stdlib/stats/base/dists/normal/cdf' ).factory; +var format = require( '@stdlib/string/format' ); +var atanh = require( '@stdlib/math/base/special/atanh' ); +var tanh = require( '@stdlib/math/base/special/tanh' ); +var tCDF = require( '@stdlib/stats/base/dists/t/cdf' ); +var sqrt = require( '@stdlib/math/base/special/sqrt' ); +var min = require( '@stdlib/math/base/special/min' ); +var print = require( './print.js' ); // eslint-disable-line stdlib/no-redeclare +var scorr = require( './scorr.js' ); +var validate = require( './validate.js' ); + + +// VARIABLES // + +var normQuantile = quantileFactory( 0.0, 1.0 ); +var normCDF = cdfFactory( 0.0, 1.0 ); + + +// MAIN // + +/** +* Computes a Spearman rank correlation test between paired samples. +* +* ## Notes +* +* - The Spearman rank correlation coefficient is a non-parametric measure of rank correlation that assesses how well the relationship between two variables can be described using a monotonic function. +* - When `rho` is zero, the test statistic follows a t-distribution with `n-2` degrees of freedom (for large samples). When `rho` is non-zero, Fisher's z transform is used. +* +* @param {NumericArray} x - first data array +* @param {NumericArray} y - second data array +* @param {Options} [options] - function options +* @param {number} [options.alpha=0.05] - significance level +* @param {string} [options.alternative='two-sided'] - alternative hypothesis (`two-sided`, `less` or `greater`) +* @param {number} [options.rho=0.0] - correlation under H0 +* @throws {TypeError} first argument has to be a typed array or array of numbers +* @throws {TypeError} second argument has to be a typed array or array of numbers +* @throws {RangeError} first and second arguments must have the same length +* @throws {Error} first and second arguments must contain at least four elements +* @throws {TypeError} options must be an object +* @throws {TypeError} must provide valid options +* @returns {Object} test result object +* +* @example +* var x = [ 2, 4, 3, 1, 2, 3 ]; +* var y = [ 3, 2, 4, 1, 2, 4 ]; +* var out = spearmanTest( x, y ); +* +* @example +* var x = [ 2, 4, 3, 1, 2, 3 ]; +* var y = [ 3, 2, 4, 1, 2, 4 ]; +* var out = spearmanTest( x, y, { +* 'alternative': 'greater' +* }); +*/ +function spearmanTest( x, y, options ) { + var method; + var result; + var alpha; + var cint; + var opts; + var pval; + var stat; + var alt; + var err; + var out; + var rho; + var val; + var df; + var rs; + var se; + var n; + var z; + + if ( !isTypedArrayLike( x ) && !isNumberArray( x ) ) { + throw new TypeError( format( 'invalid argument. First argument must be a numeric array. Value: `%s`.', x ) ); + } + if ( !isTypedArrayLike( y ) && !isNumberArray( y ) ) { + throw new TypeError( format( 'invalid argument. Second argument must be a numeric array. Value: `%s`.', y ) ); + } + n = x.length; + if ( n !== y.length ) { + throw new RangeError( 'invalid arguments. First and second arguments must be arrays having the same length.' ); + } + opts = {}; + if ( options ) { + err = validate( opts, options ); + if ( err ) { + throw err; + } + } + if ( opts.alpha === void 0 ) { + alpha = 0.05; + } else { + alpha = opts.alpha; + } + if ( n < 4 ) { + throw new Error( 'invalid arguments. Not enough observations. First and second arguments must contain at least four observations.' ); + } + if ( opts.rho === void 0 ) { + rho = 0.0; + } else { + rho = opts.rho; + } + if ( opts.alternative === void 0 ) { + alt = 'two-sided'; + } else { + alt = opts.alternative; + } + + result = scorr( x, y ); + rs = result.rs; + z = atanh( rs ); + se = 1.0 / sqrt( n - 3 ); + if ( rho === 0.0 ) { + // Use t-test for H0: rho = 0.0 vs H1: rho != 0.0... + method = 't-test for Spearman\'s rank correlation coefficient'; + df = n - 2; + stat = sqrt( df ) * rs / sqrt( 1.0 - (rs*rs) ); + switch ( alt ) { + case 'greater': + pval = 1.0 - tCDF( stat, df ); + break; + case 'less': + pval = tCDF( stat, df ); + break; + case 'two-sided': + default: + pval = 2.0 * min( tCDF( stat, df ), 1.0 - tCDF( stat, df ) ); + break; + } + } else { + // Use large-sample normality to calculate p-value based on Fisher's z transform... + method = 'Fisher\'s z transform test for Spearman\'s rank correlation coefficient'; + stat = ( z - atanh( rho ) ) * sqrt( n - 3 ); + switch ( alt ) { + case 'greater': + pval = normCDF( -stat ); + break; + case 'less': + pval = 1.0 - normCDF( -stat ); + break; + case 'two-sided': + default: + pval = 2.0 * min( normCDF( -stat ), 1.0 - normCDF( -stat ) ); + break; + } + } + + switch ( alt ) { + case 'greater': + cint = [ tanh( z - ( se*normQuantile( 1.0 - alpha ) ) ), 1.0 ]; + break; + case 'less': + cint = [ -1.0, tanh( z + ( se*normQuantile( 1.0 - alpha ) ) ) ]; + break; + case 'two-sided': + default: + val = se * normQuantile( 1.0 - ( alpha/2.0 ) ); + cint = [ tanh( z - val ), tanh( z + val ) ]; + break; + } + + out = {}; + setReadOnly( out, 'rejected', pval <= alpha ); + setReadOnly( out, 'alpha', alpha ); + setReadOnly( out, 'pValue', pval ); + setReadOnly( out, 'statistic', stat ); + setReadOnly( out, 'ci', cint ); + setReadOnly( out, 'alternative', alt ); + setReadOnly( out, 'method', method ); + setReadOnly( out, 'nullValue', rho ); + setReadOnly( out, 'scorr', rs ); + setReadOnly( out, 'print', print ); + return out; +} + + +// EXPORTS // + +module.exports = spearmanTest; diff --git a/lib/node_modules/@stdlib/stats/spearman-test/lib/print.js b/lib/node_modules/@stdlib/stats/spearman-test/lib/print.js new file mode 100644 index 000000000000..98b290890406 --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/lib/print.js @@ -0,0 +1,110 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var isPositiveInteger = require( '@stdlib/assert/is-positive-integer' ); +var isObject = require( '@stdlib/assert/is-plain-object' ); +var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive; +var hasOwnProp = require( '@stdlib/assert/has-own-property' ); +var roundn = require( '@stdlib/math/base/special/roundn' ); +var format = require( '@stdlib/string/format' ); + + +// MAIN // + +/** +* Pretty-print output of test. +* +* @param {Object} [opts] - options object +* @param {PositiveInteger} [opts.digits=4] - number of digits after the decimal point +* @param {boolean} [opts.decision=true] - boolean indicating whether to print the test decision +* @throws {TypeError} options argument must be an object +* @throws {TypeError} must provide valid options +* @returns {string} formatted output +*/ +function print( opts ) { // eslint-disable-line stdlib/no-redeclare + /* eslint-disable no-invalid-this */ + var decision; + var dgts; + var str; + + dgts = 4; + decision = true; + if ( arguments.length > 0 ) { + if ( !isObject( opts ) ) { + throw new TypeError( format( 'invalid argument. First argument must be an object. Value: `%s`.', opts ) ); + } + if ( hasOwnProp( opts, 'digits' ) ) { + if ( !isPositiveInteger( opts.digits ) ) { + throw new TypeError( format( 'invalid option. `%s` option must be a positive integer. Option: `%s`.', 'digits', opts.digits ) ); + } + dgts = opts.digits; + } + if ( hasOwnProp( opts, 'decision' ) ) { + if ( !isBoolean( opts.decision ) ) { + throw new TypeError( format( 'invalid option. `%s` option must be a boolean. Option: `%s`.', 'decision', opts.decision ) ); + } + decision = opts.decision; + } + } + str = ''; + str += this.method; + str += '\n\n'; + str += 'Alternative hypothesis: '; + str += 'True correlation coefficient is '; + switch ( this.alternative ) { + case 'less': + str += 'less than '; + break; + case 'greater': + str += 'greater than '; + break; + case 'two-sided': + default: + str += 'not equal to '; + break; + } + str += this.nullValue; + str += '\n\n'; + str += ' pValue: ' + roundn( this.pValue, -dgts ) + '\n'; + str += ' statistic: ' + roundn( this.statistic, -dgts ) + '\n'; + str += ' ' + ((1.0-this.alpha)*100) + '% confidence interval: [' + + roundn( this.ci[0], -dgts ) + + ',' + + roundn( this.ci[1], -dgts ) + + ']'; + str += '\n\n'; + if ( decision ) { + str += 'Test Decision: '; + if ( this.rejected ) { + str += 'Reject null in favor of alternative at ' + (this.alpha*100) + '% significance level'; + } else { + str += 'Fail to reject null in favor of alternative at ' + (this.alpha*100) + '% significance level'; + } + str += '\n'; + } + return str; +} + + +// EXPORTS // + +module.exports = print; diff --git a/lib/node_modules/@stdlib/stats/spearman-test/lib/ranks.js b/lib/node_modules/@stdlib/stats/spearman-test/lib/ranks.js new file mode 100644 index 000000000000..b8969ba0f98a --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/lib/ranks.js @@ -0,0 +1,98 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// FUNCTIONS // + +/** +* Comparator function to sort pairs in ascending order by value. +* +* @private +* @param {Array} a - first pair +* @param {Array} b - second pair +* @returns {number} difference between first elements +*/ +function ascending( a, b ) { + return a[ 0 ] - b[ 0 ]; +} + + +// MAIN // + +/** +* Computes the ranks of values in an array, handling ties by averaging. +* +* @private +* @param {NumericArray} x - input data array +* @returns {Array} array of ranks +* +* @example +* var x = [ 3.0, 1.0, 2.0, 2.0 ]; +* var r = rankData( x ); +* // returns [ 4.0, 1.0, 2.5, 2.5 ] +*/ +function rankData( x ) { + var sorted; + var ranks; + var sum; + var cnt; + var n; + var i; + var j; + + n = x.length; + + // Create an array of [value, originalIndex] pairs: + sorted = []; + for ( i = 0; i < n; i++ ) { + sorted.push( [ x[ i ], i ] ); + } + + // Sort by value: + sorted.sort( ascending ); + + // Assign ranks, averaging tied values: + ranks = []; + i = 0; + while ( i < n ) { + j = i; + + // Find the group of tied values: + while ( j < n - 1 && sorted[ j ][ 0 ] === sorted[ j + 1 ][ 0 ] ) { + j += 1; + } + // Compute the average rank for the tied group: + cnt = j - i + 1; + sum = 0; + for ( j = i; j < i + cnt; j++ ) { + sum += j + 1; // Ranks are 1-based + } + // Assign the average rank to each tied element: + for ( j = i; j < i + cnt; j++ ) { + ranks[ sorted[ j ][ 1 ] ] = sum / cnt; + } + i += cnt; + } + return ranks; +} + + +// EXPORTS // + +module.exports = rankData; diff --git a/lib/node_modules/@stdlib/stats/spearman-test/lib/scorr.js b/lib/node_modules/@stdlib/stats/spearman-test/lib/scorr.js new file mode 100644 index 000000000000..064b9bd1ebbf --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/lib/scorr.js @@ -0,0 +1,94 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var max = require( '@stdlib/math/base/special/max' ); +var min = require( '@stdlib/math/base/special/min' ); +var sqrt = require( '@stdlib/math/base/special/sqrt' ); +var rankData = require( './ranks.js' ); + + +// MAIN // + +/** +* Computes the Spearman rank correlation coefficient between `x` and `y`. +* +* @private +* @param {NumericArray} x - first data array +* @param {NumericArray} y - second data array +* @returns {Object} object containing the correlation coefficient and the ranks +* +* @example +* var x = [ 1.0, 2.0, 3.0, 4.0 ]; +* var y = [ 1.0, 3.0, 2.0, 4.0 ]; +* var out = scorr( x, y ); +* // returns { 'rs': ~0.8, 'xRanks': [...], 'yRanks': [...] } +*/ +function scorr( x, y ) { + var xRanks; + var yRanks; + var xmean; + var ymean; + var denom; + var num; + var out; + var xv; + var yv; + var n; + var i; + + n = x.length; + + // Compute ranks: + xRanks = rankData( x ); + yRanks = rankData( y ); + + // Compute Pearson correlation on the ranks: + xmean = 0.0; + ymean = 0.0; + for ( i = 0; i < n; i++ ) { + xmean += xRanks[ i ]; + ymean += yRanks[ i ]; + } + xmean /= n; + ymean /= n; + + num = 0.0; + xv = 0.0; + yv = 0.0; + for ( i = 0; i < n; i++ ) { + num += ( xRanks[ i ] - xmean ) * ( yRanks[ i ] - ymean ); + xv += ( xRanks[ i ] - xmean ) * ( xRanks[ i ] - xmean ); + yv += ( yRanks[ i ] - ymean ) * ( yRanks[ i ] - ymean ); + } + denom = sqrt( xv ) * sqrt( yv ); + + out = {}; + out.rs = ( denom === 0.0 ) ? 0.0 : max( min( num / denom, 1.0 ), -1.0 ); + out.xRanks = xRanks; + out.yRanks = yRanks; + return out; +} + + +// EXPORTS // + +module.exports = scorr; diff --git a/lib/node_modules/@stdlib/stats/spearman-test/lib/validate.js b/lib/node_modules/@stdlib/stats/spearman-test/lib/validate.js new file mode 100644 index 000000000000..55de6174aa8e --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/lib/validate.js @@ -0,0 +1,98 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var isNumber = require( '@stdlib/assert/is-number' ).isPrimitive; +var isObject = require( '@stdlib/assert/is-plain-object' ); +var isnan = require( '@stdlib/assert/is-nan' ); +var indexOf = require( '@stdlib/utils/index-of' ); +var hasOwnProp = require( '@stdlib/assert/has-own-property' ); +var format = require( '@stdlib/string/format' ); + + +// VARIABLES // + +var alternative = [ 'two-sided', 'less', 'greater' ]; + + +// MAIN // + +/** +* Validates function options. +* +* @private +* @param {Object} opts - destination for validated options +* @param {Options} options - function options +* @param {number} [options.alpha] - significance level +* @param {string} [options.alternative] - alternative hypothesis (`two-sided`, `less` or `greater`) +* @param {number} [options.rho] - correlation coefficient under H0 +* @returns {(null|Error)} null or an error +* +* @example +* var opts = {}; +* var options = { +* 'alpha': 0.01, +* 'rho': 0.5 +* }; +* var err = validate( opts, options ); +* if ( err ) { +* throw err; +* } +*/ +function validate( opts, options ) { + if ( !isObject( options ) ) { + return new TypeError( format( 'invalid argument. Options argument must be an object. Value: `%s`.', options ) ); + } + if ( hasOwnProp( options, 'alpha' ) ) { + opts.alpha = options.alpha; + if ( + !isNumber( opts.alpha ) || + isnan( opts.alpha ) || + opts.alpha < 0.0 || + opts.alpha > 1.0 + ) { + return new TypeError( format( 'invalid option. `%s` option must be a number on the interval: [0, 1]. Option: `%s`.', 'alpha', opts.alpha ) ); + } + } + if ( hasOwnProp( options, 'alternative' ) ) { + opts.alternative = options.alternative; + if ( indexOf( alternative, opts.alternative ) === -1 ) { + return new TypeError( format( 'invalid option. `%s` option must be one of the following: "%s". Option: `%s`.', 'alternative', alternative.join( '", "' ), opts.alternative ) ); + } + } + if ( hasOwnProp( options, 'rho' ) ) { + opts.rho = options.rho; + if ( + !isNumber( opts.rho ) || + isnan( opts.rho ) || + opts.rho < -1.0 || + opts.rho > 1.0 + ) { + return new TypeError( format( 'invalid option. `%s` option must be a number on the interval: [-1, 1]. Option: `%s`.', 'rho', opts.rho ) ); + } + } + return null; +} + + +// EXPORTS // + +module.exports = validate; diff --git a/lib/node_modules/@stdlib/stats/spearman-test/package.json b/lib/node_modules/@stdlib/stats/spearman-test/package.json new file mode 100644 index 000000000000..fea2b158807a --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/package.json @@ -0,0 +1,70 @@ +{ + "name": "@stdlib/stats/spearman-test", + "version": "0.0.0", + "description": "Compute a Spearman rank correlation test between paired samples.", + "license": "Apache-2.0", + "author": { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + }, + "contributors": [ + { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + } + ], + "main": "./lib", + "directories": { + "benchmark": "./benchmark", + "doc": "./docs", + "example": "./examples", + "lib": "./lib", + "test": "./test" + }, + "types": "./docs/types", + "scripts": {}, + "homepage": "https://github.com/stdlib-js/stdlib", + "repository": { + "type": "git", + "url": "git://github.com/stdlib-js/stdlib.git" + }, + "bugs": { + "url": "https://github.com/stdlib-js/stdlib/issues" + }, + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=0.10.0", + "npm": ">2.7.0" + }, + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "keywords": [ + "stdlib", + "stdmath", + "statistics", + "stats", + "mathematics", + "math", + "summary", + "test", + "hypothesis", + "association", + "rank", + "correlation", + "paired", + "nonparametric", + "two-sample", + "spearman", + "monotonic" + ] +} diff --git a/lib/node_modules/@stdlib/stats/spearman-test/test/fixtures/r/DESCRIPTION b/lib/node_modules/@stdlib/stats/spearman-test/test/fixtures/r/DESCRIPTION new file mode 100644 index 000000000000..27a7afccbf53 --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/test/fixtures/r/DESCRIPTION @@ -0,0 +1,10 @@ +Fixtures generated using R (version 4.x). + +The R script `runner.R` generates test fixture data using `cor.test()` with +`method = "spearman"`. + +To run the script: + +```bash +$ Rscript runner.R +``` diff --git a/lib/node_modules/@stdlib/stats/spearman-test/test/fixtures/r/runner.R b/lib/node_modules/@stdlib/stats/spearman-test/test/fixtures/r/runner.R new file mode 100644 index 000000000000..d64e62339a64 --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/test/fixtures/r/runner.R @@ -0,0 +1,136 @@ +#!/usr/bin/env Rscript +# +# @license Apache-2.0 +# +# Copyright (c) 2026 The Stdlib Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Set the precision to 16 digits: +options( digits = 16L ); + +#' Generate test fixtures. +#' +#' @examples +#' main(); +main <- function() { + #' Get the script filepath. + #' + #' @return The absolute path of this script + #' + #' @examples + #' filepath <- get_script_path(); + get_script_path <- function() { + args <- commandArgs( trailingOnly = FALSE ); + needle <- "--file="; + match <- grep( needle, args ); + if ( length( match ) > 0 ) { + # Rscript: + filepath <- sub( needle, "", args[match] ); + } else { + ls_vars <- ls( sys.frames()[[1]] ) + if ( "fileName" %in% ls_vars ) { + # Source'd via RStudio: + filepath <- sys.frames()[[1]]$fileName; # nolint + } else { + # Source'd via R console: + filepath <- sys.frames()[[1]]$ofile; + } + } + return( normalizePath( filepath ) ); + } + + #' Convert a data structure to JSON. + #' + #' @param x A data structure to convert + #' @return JSON blob + #' + #' @examples + #' x <- seq( -6.5, 25, 0.5 ); + #' json <- to_json( x ); + to_json <- function( x ) { + return( jsonlite::toJSON( x, digits = 16, auto_unbox = TRUE ) ); + } + + #' Generate an output absolute filepath based on the script directory. + #' + #' @param name An output filename + #' @return An absolute filepath + #' + #' @examples + #' filepath <- get_filepath( "data.json" ); + get_filepath <- function( name ) { + return( paste( source_dir, "/", name, sep = "" ) ); + } + + # Get the directory of this script: + source_dir <- dirname( get_script_path() ); + + # Generate test fixture data (two-sided): + set.seed( 42 ); + rho <- 0.5; + x <- rnorm( 200 ); + y <- rnorm( 200, 0.0, sqrt( 1.0 - rho*rho ) ) + rho*x; + out <- cor.test( x, y, method = "spearman" ); + + # Convert fixture data to JSON: + twosided <- list( + x = x, + y = y, + statistic = out$statistic, + pValue = out$p.value + ); + twosided <- to_json( twosided ); + + # Write the data to file... + filepath <- get_filepath( "twosided.json" ); + write( twosided, filepath ); + + # Generate test fixture data (less): + set.seed( 123 ); + x <- rnorm( 200 ); + y <- rnorm( 200 ) - 0.5*x; + out <- cor.test( x, y, method = "spearman", alternative = "less" ); + + less <- list( + x = x, + y = y, + statistic = out$statistic, + pValue = out$p.value + ); + less <- to_json( less ); + + # Write the data to file... + filepath <- get_filepath( "less.json" ); + write( less, filepath ); + + # Generate test fixture data (greater): + set.seed( 456 ); + x <- rnorm( 200 ); + y <- rnorm( 200 ) + 0.3*x; + out <- cor.test( x, y, method = "spearman", alternative = "greater" ); + + greater <- list( + x = x, + y = y, + statistic = out$statistic, + pValue = out$p.value + ); + greater <- to_json( greater ); + + # Write the data to file... + filepath <- get_filepath( "greater.json" ); + write( greater, filepath ); +} + +main(); diff --git a/lib/node_modules/@stdlib/stats/spearman-test/test/test.js b/lib/node_modules/@stdlib/stats/spearman-test/test/test.js new file mode 100644 index 000000000000..aeaa2897ceff --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/test/test.js @@ -0,0 +1,449 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var contains = require( '@stdlib/assert/contains' ); +var abs = require( '@stdlib/math/base/special/abs' ); +var EPS = require( '@stdlib/constants/float64/eps' ); +var spearmanTest = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof spearmanTest, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function throws an error if the `x` argument is not a numeric array', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + [ 1, 2, 'not a numeric array, hehe' ], + true, + void 0, + null, + NaN, + function noop() {}, + {} + ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + spearmanTest( value, [1, 2, 3] ); + }; + } +}); + +tape( 'the function throws an error if the `y` argument is not a numeric array', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + [ 1, 2, 'not a numeric array, hehe' ], + true, + void 0, + null, + NaN, + function noop() {}, + {} + ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + spearmanTest( [1, 2, 3], value ); + }; + } +}); + +tape( 'the function throws an error if arrays have different lengths', function test( t ) { + t.throws( function throws() { + spearmanTest( [1, 2, 3, 4], [1, 2, 3] ); + }, RangeError, 'throws a range error' ); + t.end(); +}); + +tape( 'the function throws an error if provided fewer than four observations', function test( t ) { + t.throws( function throws() { + spearmanTest( [1, 2, 3], [1, 2, 3] ); + }, Error, 'throws an error' ); + t.end(); +}); + +tape( 'the function throws an error if provided an invalid option', function test( t ) { + t.throws( function throws() { + spearmanTest( [1, 2, 3, 4], [1, 2, 3, 4], { + 'alpha': 1.5 + }); + }, TypeError, 'throws a type error' ); + t.end(); +}); + +tape( 'the function throws an error if provided an invalid alternative option', function test( t ) { + t.throws( function throws() { + spearmanTest( [1, 2, 3, 4], [1, 2, 3, 4], { + 'alternative': 'abc' + }); + }, TypeError, 'throws a type error' ); + t.end(); +}); + +tape( 'the function computes a Spearman rank correlation test (two-sided)', function test( t ) { + var out; + var x; + var y; + + // Perfect positive rank correlation: + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]; + + out = spearmanTest( x, y ); + + t.strictEqual( typeof out.rejected, 'boolean', 'has rejected property' ); + t.strictEqual( typeof out.pValue, 'number', 'has pValue property' ); + t.strictEqual( typeof out.statistic, 'number', 'has statistic property' ); + t.strictEqual( typeof out.scorr, 'number', 'has scorr property' ); + t.strictEqual( out.alternative, 'two-sided', 'correct alternative' ); + t.ok( abs( out.scorr - 1.0 ) < 100*EPS, 'perfect positive correlation' ); + t.strictEqual( out.rejected, true, 'rejects null hypothesis' ); + t.end(); +}); + +tape( 'the function computes a Spearman rank correlation test (perfect negative)', function test( t ) { + var out; + var x; + var y; + + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]; + + out = spearmanTest( x, y ); + + t.ok( abs( out.scorr - (-1.0) ) < 100*EPS, 'perfect negative correlation' ); + t.strictEqual( out.rejected, true, 'rejects null hypothesis' ); + t.end(); +}); + +tape( 'the function computes a Spearman rank correlation test (no correlation)', function test( t ) { + var out; + var x; + var y; + + // Data designed to have near-zero correlation: + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ]; + y = [ 5, 14, 7, 18, 3, 16, 9, 12, 1, 20, 11, 8, 15, 2, 19, 6, 13, 10, 17, 4 ]; + + out = spearmanTest( x, y ); + + t.strictEqual( typeof out.pValue, 'number', 'has pValue property' ); + t.strictEqual( out.rejected, false, 'fails to reject null hypothesis' ); + t.end(); +}); + +tape( 'the function correctly handles tied values', function test( t ) { + var out; + var x; + var y; + + // Data with ties: + x = [ 1, 2, 2, 3, 4, 4, 5 ]; + y = [ 1, 3, 2, 4, 5, 6, 7 ]; + + out = spearmanTest( x, y ); + + t.strictEqual( typeof out.scorr, 'number', 'returns a correlation coefficient' ); + t.ok( out.scorr > 0 && out.scorr <= 1.0, 'positive correlation' ); + t.end(); +}); + +tape( 'the function supports the `greater` alternative', function test( t ) { + var out; + var x; + var y; + + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]; + + out = spearmanTest( x, y, { + 'alternative': 'greater' + }); + + t.strictEqual( out.alternative, 'greater', 'correct alternative' ); + t.strictEqual( out.rejected, true, 'rejects in favor of greater' ); + t.end(); +}); + +tape( 'the function supports the `less` alternative', function test( t ) { + var out; + var x; + var y; + + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]; + + out = spearmanTest( x, y, { + 'alternative': 'less' + }); + + t.strictEqual( out.alternative, 'less', 'correct alternative' ); + t.strictEqual( out.rejected, true, 'rejects in favor of less' ); + t.end(); +}); + +tape( 'the function supports custom alpha level', function test( t ) { + var out; + var x; + var y; + + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]; + + out = spearmanTest( x, y, { + 'alpha': 0.01 + }); + + t.strictEqual( out.alpha, 0.01, 'correct alpha level' ); + t.end(); +}); + +tape( 'the function returns confidence intervals', function test( t ) { + var out; + var x; + var y; + + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]; + + out = spearmanTest( x, y ); + + t.strictEqual( out.ci.length, 2, 'confidence interval has two elements' ); + t.ok( out.ci[0] <= out.scorr, 'lower bound is at most the estimate' ); + t.ok( out.ci[1] >= out.scorr, 'upper bound is at least the estimate' ); + t.end(); +}); + +tape( 'the function returns an object with a `.print()` method', function test( t ) { + var table; + var out; + var x; + var y; + + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]; + + out = spearmanTest( x, y ); + + t.strictEqual( typeof out.print, 'function', 'has print method' ); + + table = out.print(); + t.strictEqual( typeof table, 'string', 'returns a pretty-printed table' ); + t.ok( contains( table, 'Spearman' ), 'output mentions Spearman' ); + t.end(); +}); + +tape( 'the function supports testing against a non-zero rho', function test( t ) { + var out; + var x; + var y; + + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]; + + out = spearmanTest( x, y, { + 'rho': 0.5 + }); + + t.strictEqual( out.nullValue, 0.5, 'correct null value' ); + t.ok( contains( out.method, 'Fisher' ), 'uses Fisher z transform' ); + t.end(); +}); + +tape( 'the function works with typed arrays', function test( t ) { + var out; + var x; + var y; + + x = new Float64Array( [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] ); + y = new Float64Array( [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ] ); + + out = spearmanTest( x, y ); + + t.strictEqual( typeof out.pValue, 'number', 'returns p-value' ); + t.strictEqual( out.rejected, true, 'rejects null hypothesis' ); + t.end(); +}); + +tape( 'the function handles the minimum number of observations (4)', function test( t ) { + var out; + var x; + var y; + + x = [ 1, 2, 3, 4 ]; + y = [ 2, 4, 3, 5 ]; + + out = spearmanTest( x, y ); + + t.strictEqual( typeof out.pValue, 'number', 'returns valid p-value' ); + t.strictEqual( typeof out.statistic, 'number', 'returns valid test statistic' ); + t.end(); +}); + +tape( 'the function returns an object with read-only properties', function test( t ) { + var out; + var x; + var y; + + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]; + + out = spearmanTest( x, y ); + + t.throws( function throws() { + out.pValue = 0.5; + }, TypeError, 'pValue property is read-only' ); + t.throws( function throws() { + out.rejected = false; + }, TypeError, 'rejected property is read-only' ); + t.throws( function throws() { + out.statistic = 0.5; + }, TypeError, 'statistic property is read-only' ); + t.end(); +}); + +tape( 'the function uses t-test method when rho is zero', function test( t ) { + var out; + var x; + var y; + + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]; + + out = spearmanTest( x, y, { + 'rho': 0.0 + }); + + t.ok( contains( out.method, 't-test' ), 'uses t-test method for rho=0' ); + t.strictEqual( out.nullValue, 0.0, 'null value is zero' ); + t.end(); +}); + +tape( 'the function correctly computes confidence intervals for different alternatives', function test( t ) { + var outGreater; + var outLess; + var outTwoSided; + var x; + var y; + + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]; + + outGreater = spearmanTest( x, y, { + 'alternative': 'greater' + }); + + outLess = spearmanTest( x, y, { + 'alternative': 'less' + }); + + outTwoSided = spearmanTest( x, y, { + 'alternative': 'two-sided' + }); + + t.ok( outGreater.ci[0] >= -1.0, 'greater alternative has lower bound >= -1' ); + t.ok( outLess.ci[1] <= 1.0, 'less alternative has upper bound <= 1' ); + t.ok( outTwoSided.ci[0] < outTwoSided.ci[1], 'two-sided ci has proper ordering' ); + t.end(); +}); + +tape( 'the function handles various rho values with all alternatives', function test( t ) { + var outGreaterNonZero; + var outLessNonZero; + var outTwoSidedNonZero; + var x; + var y; + + x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + y = [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]; + + outGreaterNonZero = spearmanTest( x, y, { + 'alternative': 'greater', + 'rho': 0.3 + }); + + outLessNonZero = spearmanTest( x, y, { + 'alternative': 'less', + 'rho': 0.7 + }); + + outTwoSidedNonZero = spearmanTest( x, y, { + 'alternative': 'two-sided', + 'rho': 0.5 + }); + + t.strictEqual( typeof outGreaterNonZero.pValue, 'number', 'greater with non-zero rho' ); + t.strictEqual( typeof outLessNonZero.pValue, 'number', 'less with non-zero rho' ); + t.strictEqual( typeof outTwoSidedNonZero.pValue, 'number', 'two-sided with non-zero rho' ); + t.ok( contains( outGreaterNonZero.method, 'Fisher' ), 'uses Fisher transform' ); + t.end(); +}); + +tape( 'the function correctly validates input arrays of varying lengths', function test( t ) { + var x; + var y; + + // Test with exactly matching lengths + x = [ 1, 2, 3, 4 ]; + y = [ 2, 3, 4, 5 ]; + t.doesNotThrow( function throws() { + spearmanTest( x, y ); + }, 'same length arrays pass validation' ); + + // Test with larger arrays + x = new Array( 100 ); + y = new Array( 100 ); + var i; + for ( i = 0; i < 100; i++ ) { + x[ i ] = i; + y[ i ] = i + 1; + } + t.doesNotThrow( function throws() { + spearmanTest( x, y ); + }, 'large arrays pass validation' ); + + t.end(); +}); diff --git a/lib/node_modules/@stdlib/stats/spearman-test/test/test.validate.js b/lib/node_modules/@stdlib/stats/spearman-test/test/test.validate.js new file mode 100644 index 000000000000..46b500d9089e --- /dev/null +++ b/lib/node_modules/@stdlib/stats/spearman-test/test/test.validate.js @@ -0,0 +1,142 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var validate = require( './../lib/validate.js' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof validate, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function returns an error if not provided an object', function test( t ) { + var values; + var err; + var i; + + values = [ + '5', + 5, + NaN, + true, + null, + void 0, + [], + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + err = validate( {}, values[ i ] ); + t.ok( err instanceof TypeError, 'returns a type error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'the function returns an error if provided an invalid `alpha` option', function test( t ) { + var values; + var err; + var i; + + values = [ + '5', + -0.1, + 1.5, + NaN, + true, + null, + void 0, + [], + {}, + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'alpha': values[ i ] + }); + t.ok( err instanceof TypeError, 'returns a type error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'the function returns an error if provided an invalid `alternative` option', function test( t ) { + var values; + var err; + var i; + + values = [ + 'abc', + 'not-valid', + '' + ]; + + for ( i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'alternative': values[ i ] + }); + t.ok( err instanceof TypeError, 'returns a type error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'the function returns an error if provided an invalid `rho` option', function test( t ) { + var values; + var err; + var i; + + values = [ + '5', + -1.5, + 1.5, + NaN, + true, + null, + void 0, + [], + {}, + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'rho': values[ i ] + }); + t.ok( err instanceof TypeError, 'returns a type error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'the function returns `null` if all options are valid', function test( t ) { + var err; + + err = validate( {}, { + 'alpha': 0.01, + 'alternative': 'greater', + 'rho': 0.5 + }); + t.strictEqual( err, null, 'returns null' ); + t.end(); +});