time-to-botec

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

main.js (4474B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2018 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 isCollection = require( '@stdlib/assert/is-collection' );
     24 var contains = require( '@stdlib/assert/contains' );
     25 var sum = require( './sum.js' );
     26 var order = require( './order.js' );
     27 var isMissing = require( './is_missing.js' );
     28 var validate = require( './validate.js' );
     29 
     30 
     31 // MAIN //
     32 
     33 /**
     34 * Computes the sample ranks for the values of an array-like object.
     35 *
     36 * @param {Collection} x - data array
     37 * @param {Object} [options] - options object
     38 * @param {string} [options.method='average'] - method name determining how ties are treated
     39 * @param {string} [options.missing='last'] - determines where missing values go (`first`,`last`, or `remove`)
     40 * @param {Array} [options.encoding=[null,NaN]] - array of values encoding missing values
     41 * @throws {TypeError} first argument has to be an array-like object
     42 * @throws {TypeError} options argument must be an object
     43 * @throws {TypeError} must provide valid options
     44 * @returns {Array} array containing the computed ranks for the elements of x
     45 *
     46 * @example
     47 * var arr = [ 1.1, 2.0, 3.5, 0.0, 2.4 ];
     48 * var out = ranks( arr );
     49 * // returns [ 2, 3, 5, 1, 4 ]
     50 *
     51 * @example
     52 * // Ties are averaged:
     53 * arr = [ 2, 2, 1, 4, 3 ];
     54 * out = ranks( arr );
     55 * // returns [ 2.5, 2.5, 1, 5, 4 ]
     56 *
     57 * @example
     58 * // Missing values are placed last:
     59 * arr = [ null, 2, 2, 1, 4, 3, NaN, NaN ];
     60 * out = ranks( arr );
     61 * // returns [ 6, 2.5, 2.5, 1, 5, 4, 7 ,8 ]
     62 */
     63 function ranks( x, options ) {
     64 	var missingIndices;
     65 	var noDuplicates;
     66 	var countMissing;
     67 	var totalNoTies;
     68 	var finalRanks;
     69 	var encoding;
     70 	var iPlusOne;
     71 	var ordered;
     72 	var missing;
     73 	var tieRank;
     74 	var method;
     75 	var ranks;
     76 	var opts;
     77 	var xnew;
     78 	var err;
     79 	var n;
     80 	var i;
     81 	var j;
     82 
     83 	if ( !isCollection( x ) ) {
     84 		throw new TypeError( 'invalid argument. First argument `x` must be an array-like object. Value: `' + x + '`.' );
     85 	}
     86 	opts = {};
     87 	if ( arguments.length > 1 ) {
     88 		err = validate( opts, options );
     89 		if ( err ) {
     90 			throw err;
     91 		}
     92 	}
     93 	method = opts.method || 'average';
     94 	encoding = opts.encoding || [ null, NaN ];
     95 	missing = opts.missing || 'last';
     96 
     97 	n = x.length;
     98 	xnew = [];
     99 	for ( i = 0; i < n; i++ ) {
    100 		if ( !contains( encoding, x[ i ] ) ) {
    101 			xnew.push( x[ i ] );
    102 		}
    103 	}
    104 	missingIndices = isMissing( x, encoding );
    105 	n = xnew.length;
    106 	totalNoTies = 0;
    107 	ranks = new Array( n );
    108 	ordered = order( xnew );
    109 
    110 	if ( method === 'ordinal' ) {
    111 		for ( i = 0; i < n; i++ ) {
    112 			ranks[ ordered[ i ] ] = i + 1;
    113 		}
    114 	} else {
    115 		noDuplicates = 0;
    116 		for ( i = 0; i < n; i++ ) {
    117 			iPlusOne = i + 1;
    118 			if (
    119 				( i === n - 1 ) ||
    120 				( xnew[ ordered[i] ] !== xnew[ ordered[ iPlusOne ] ] )
    121 			) {
    122 				switch ( method ) {
    123 				case 'average':
    124 				default:
    125 					tieRank = iPlusOne - ( 0.5 * noDuplicates );
    126 					break;
    127 				case 'min':
    128 					tieRank = iPlusOne - noDuplicates;
    129 					break;
    130 				case 'max':
    131 					tieRank = iPlusOne;
    132 					break;
    133 				case 'dense':
    134 					tieRank = iPlusOne - noDuplicates - totalNoTies;
    135 					totalNoTies += noDuplicates;
    136 					break;
    137 				}
    138 				for ( j = i - noDuplicates; j < iPlusOne; j++ ) {
    139 					ranks[ ordered[ j ] ] = tieRank;
    140 				}
    141 				noDuplicates = 0;
    142 			} else {
    143 				noDuplicates += 1;
    144 			}
    145 		}
    146 	}
    147 
    148 	if ( missing === 'first' ) {
    149 		countMissing = sum( missingIndices );
    150 		j = 1;
    151 		finalRanks = new Array( missingIndices.length );
    152 		for ( i = 0; i < missingIndices.length; i++ ) {
    153 			if ( missingIndices[ i ] ) {
    154 				finalRanks[ i ] = j;
    155 				j += 1;
    156 			} else {
    157 				finalRanks[ i ] = ranks.shift() + countMissing;
    158 			}
    159 		}
    160 		return finalRanks;
    161 	}
    162 	if ( missing === 'last' ) {
    163 		finalRanks = new Array( missingIndices.length );
    164 		for ( i = 0; i < missingIndices.length; i++ ) {
    165 			if ( missingIndices[ i ] ) {
    166 				finalRanks[ i ] = i + ranks.length + 1;
    167 			} else {
    168 				finalRanks[ i ] = ranks.shift();
    169 			}
    170 		}
    171 		return finalRanks;
    172 	}
    173 	// Case: missing = 'remove'
    174 	return ranks;
    175 }
    176 
    177 
    178 // EXPORTS //
    179 
    180 module.exports = ranks;