time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

main.js (4174B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2020 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 setReadOnly = require( '@stdlib/utils/define-read-only-property' );
     24 var isNonNegativeIntegerArray = require( '@stdlib/assert/is-nonnegative-integer-array' );
     25 var isMatrixLike = require( '@stdlib/assert/is-matrix-like' );
     26 var isArrayArray = require( '@stdlib/assert/is-array-array' );
     27 var array = require( '@stdlib/ndarray/array' );
     28 var incrmin = require( './../../incr/min' );
     29 var gsum = require( '@stdlib/blas/ext/base/gsum' );
     30 var min = require( '@stdlib/math/base/special/min' );
     31 var copy = require( '@stdlib/utils/copy' );
     32 var chisqCDF = require( './../../base/dists/chisquare/cdf' );
     33 var prettyPrint = require( './print.js' );
     34 var defaults = require( './defaults.json' );
     35 var sumByDimension = require( './sum.js' );
     36 var outer = require( './outer.js' );
     37 var absdiff = require( './absdiff.js' );
     38 var validate = require( './validate.js' );
     39 
     40 
     41 // MAIN //
     42 
     43 /**
     44 * Performs a chi-square independence test.
     45 *
     46 * @param {(ndarray|ArrayArray)} x - two-way table of cell counts
     47 * @param {Options} [options] - function options
     48 * @param {number} [options.alpha=0.05] - significance level
     49 * @param {boolean} [options.correct=true] - boolean indicating whether to use Yates' continuity correction when provided a 2x2 contingency table
     50 * @throws {TypeError} first argument must be an array of arrays or ndarray-like object with dimension two
     51 * @returns {Object} test results
     52 *
     53 * @example
     54 *
     55 * @example
     56 * var x = [ [ 20, 30 ], [ 30, 20 ] ];
     57 * var out = chi2test( x );
     58 * // returns { 'rejected': false, 'alpha': 0.05, 'pValue': ~0.072, ... }
     59 */
     60 function chi2test( x, options ) {
     61 	var absDiff;
     62 	var colSums;
     63 	var rowSums;
     64 	var minAbs;
     65 	var yates;
     66 	var means;
     67 	var param;
     68 	var nrow;
     69 	var ncol;
     70 	var opts;
     71 	var pval;
     72 	var stat;
     73 	var err;
     74 	var out;
     75 	var N;
     76 	var i;
     77 
     78 	if ( isArrayArray( x ) ) {
     79 		x = array( x );
     80 	}
     81 	if ( !isMatrixLike( x ) ) {
     82 		throw new TypeError( 'invalid argument. First argument `x` must be an array of arrays or ndarray-like object with dimension two. Value: `' + x + '`.' );
     83 	}
     84 	if ( !isNonNegativeIntegerArray( x.data ) ) {
     85 		throw new TypeError( 'invalid argument. First argument `x` must contain nonnegative integers. Value: `' + x + '`.' );
     86 	}
     87 	opts = copy( defaults );
     88 	if ( arguments.length > 1 ) {
     89 		err = validate( opts, options );
     90 		if ( err ) {
     91 			throw err;
     92 		}
     93 	}
     94 	N = gsum( x.length, x.data, 1 );
     95 	nrow = x.shape[ 0 ];
     96 	ncol = x.shape[ 1 ];
     97 
     98 	colSums = sumByDimension( x, 1 );
     99 	rowSums = sumByDimension( x, 2 );
    100 	means = outer( rowSums, colSums );
    101 	for ( i = 0; i < means.length; i++ ) {
    102 		means.data[ i ] /= N;
    103 	}
    104 	absDiff = absdiff( x, means );
    105 
    106 	if ( opts.correct && nrow === 2 && ncol === 2 ) {
    107 		// Apply Yates' continuity correction:
    108 		minAbs = incrmin();
    109 		for ( i = 0; i < absDiff.length; i++ ) {
    110 			minAbs( absDiff[ i ] );
    111 		}
    112 		yates = min( 0.5, minAbs() );
    113 		for ( i = 0; i < absDiff.length; i++ ) {
    114 			absDiff[ i ] -= yates;
    115 		}
    116 	}
    117 	for ( i = 0; i < absDiff.length; i++ ) {
    118 		absDiff[ i ] *= absDiff[ i ];
    119 		absDiff[ i ] /= means.data[ i ];
    120 	}
    121 	stat = gsum( absDiff.length, absDiff, 1 );
    122 	param = ( nrow - 1 ) * ( ncol - 1 );
    123 	pval = 1 - chisqCDF( stat, param );
    124 
    125 	out = {};
    126 	setReadOnly( out, 'rejected', pval <= opts.alpha );
    127 	setReadOnly( out, 'alpha', opts.alpha );
    128 	setReadOnly( out, 'pValue', pval );
    129 	setReadOnly( out, 'df', param );
    130 	setReadOnly( out, 'expected', means );
    131 	setReadOnly( out, 'statistic', stat );
    132 	setReadOnly( out, 'method', 'Chi-square independence test' );
    133 	setReadOnly( out, 'print', prettyPrint( out ) );
    134 	return out;
    135 }
    136 
    137 
    138 // EXPORTS //
    139 
    140 module.exports = chi2test;