time-to-botec

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

kde2d.js (5295B)


      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 ndarray = require( '@stdlib/ndarray/array' );
     24 var linspace = require( '@stdlib/array/linspace' );
     25 var setReadOnly = require( '@stdlib/utils/define-read-only-property' );
     26 var isNumericArray = require( '@stdlib/assert/is-numeric-array' );
     27 var isMatrixLike = require( '@stdlib/assert/is-matrix-like' );
     28 var pickBandwidth = require( './pick_bandwidth.js' );
     29 var validate = require( './validate.js' );
     30 var ndarrayLike = require( './ndarray_like.js' );
     31 var min = require( './min.js' );
     32 var max = require( './max.js' );
     33 var gaussian = require( './gaussian.js' );
     34 
     35 
     36 // MAIN //
     37 
     38 /**
     39 * Computes two-dimensional kernel density estimates.
     40 *
     41 * @param {NumericArray} x - array of x values
     42 * @param {NumericArray} y - array of y values
     43 * @param {Options} [options] - function options
     44 * @param {NumericArray} [options.h] - array of length two containing the bandwidth values for x and y
     45 * @param {number} [options.n=25] - number of partitions on the x- and y-axes
     46 * @param {number} [options.xMin] - lower limit of x
     47 * @param {number} [options.xMax] - upper limit of x
     48 * @param {number} [options.yMin] - lower limit of y
     49 * @param {number} [options.yMax] - upper limit of y
     50 * @param {(string|Function)} [options.kernel='gaussian'] - a string or function to specifying the used kernel function
     51 * @throws {TypeError} first argument must be an array or matrix-like
     52 * @throws {TypeError} second argument must be an array
     53 * @throws {Error} first and second arguments must be of the same length
     54 * @throws {RangeError} `xMin` must be smaller than `xMax`
     55 * @throws {RangeError} `yMin` must be smaller than `yMax`
     56 * @throws {TypeError} options argument must be an object
     57 * @throws {TypeError} must provide valid options
     58 * @returns {Object} object containing the density estimates (`z`) along grid points (`x` and `y` values)
     59 *
     60 * @example
     61 * var x = [ 0.6333, 0.8643, 1.0952, 1.3262, 1.5571, 1.7881, 2.019, 2.25, 2.481, 2.7119 ];
     62 * var y = [ -0.0468, 0.8012, 1.6492, 2.4973, 3.3454, 4.1934, 5.0415, 5.8896, 6.7376, 7.5857 ];
     63 * var out = kde2d( x, y );
     64 */
     65 function kde2d() {
     66 	var kernelFunction;
     67 	var maxArgs;
     68 	var zScoreX;
     69 	var zScoreY;
     70 	var gridX;
     71 	var gridY;
     72 	var xMin;
     73 	var xMax;
     74 	var yMin;
     75 	var yMax;
     76 	var xVal; // For gridspace loop
     77 	var yVal; // For gridspace loop
     78 	var subX;
     79 	var subY;
     80 	var opts;
     81 	var arr;
     82 	var err;
     83 	var ans;
     84 	var out;
     85 	var gx;
     86 	var gy;
     87 	var hX;
     88 	var hY;
     89 	var ix;
     90 	var iy;
     91 	var x;
     92 	var y;
     93 	var i;
     94 	var n;
     95 	var z;
     96 
     97 	opts = {};
     98 
     99 	if ( isMatrixLike( arguments[0] ) ) {
    100 		// Case of ndarray, opts
    101 		arr = arguments[ 0 ];
    102 		maxArgs = 1;
    103 	} else {
    104 		x = arguments[ 0 ];
    105 		y = arguments[ 1 ];
    106 		if ( !isNumericArray( x ) ) {
    107 			throw new TypeError( 'invalid argument. First argument `x` must be a numeric array. Value: `' + x + '`.' );
    108 		}
    109 		if ( !isNumericArray( y ) ) {
    110 			throw new TypeError( 'invalid argument. Second argument `y` must be a numeric array. Value: `' + y + '`.' );
    111 		}
    112 		if ( x.length !== y.length ) {
    113 			throw new Error( 'invalid arguments. Arguments `x` and `y` must be arrays of the same length' );
    114 		}
    115 		arr = ndarrayLike( x, y );
    116 		maxArgs = 2;
    117 	}
    118 
    119 	if ( arguments.length > maxArgs ) {
    120 		err = validate( opts, arguments[ maxArgs ] );
    121 		if ( err ) {
    122 			throw err;
    123 		}
    124 	}
    125 
    126 	if ( opts.h ) {
    127 		hX = opts.h[0];
    128 		hY = opts.h[1];
    129 	} else {
    130 		hX = pickBandwidth(arr, 0);
    131 		hY = pickBandwidth(arr, 1);
    132 	}
    133 
    134 	n = opts.n || 25;
    135 	xMin = opts.xMin || min( arr, 0, arr.shape[0] );
    136 	xMax = opts.xMax || max( arr, 0, arr.shape[0] );
    137 	yMin = opts.yMin || min( arr, 1, arr.shape[0] );
    138 	yMax = opts.yMax || max( arr, 1, arr.shape[0] );
    139 
    140 	if ( xMin >= xMax ) {
    141 		throw new RangeError( '`x` min must be strictly less than max' );
    142 	}
    143 	if ( yMin >= yMax ) {
    144 		throw new RangeError( '`y` min must be strictly less than max' );
    145 	}
    146 
    147 	kernelFunction = opts.kernel || gaussian;
    148 
    149 	// Create the `ndarray` to hold the density values:
    150 	z = ndarray({
    151 		'shape': [n, n]
    152 	} );
    153 
    154 	// Make the grid:
    155 	gridX = linspace(xMin, xMax, n);
    156 	gridY = linspace(yMin, yMax, n);
    157 
    158 	// Loop through x and y indices:
    159 	for ( ix = 0; ix < gridX.length; ix++ ) {
    160 		gx = gridX[ ix ];
    161 		for ( iy = 0; iy < gridY.length; iy++ ) {
    162 			gy = gridY[ iy ];
    163 			ans = 0.0;
    164 			for ( i = 0; i < arr.shape[ 0 ]; i++ ) {
    165 				xVal = arr.get( i, 0 );
    166 				yVal = arr.get( i, 1 );
    167 
    168 				zScoreX = ( (xVal - gx) / hX );
    169 				zScoreY = ( (yVal - gy) / hY );
    170 
    171 				subX = ( 1.0 / hX ) * kernelFunction( zScoreX );
    172 				subY = ( 1.0 / hY ) * kernelFunction( zScoreY );
    173 				ans += ( subX * subY );
    174 			}
    175 			z.set( ix, iy, ans / arr.shape[0] );
    176 		}
    177 	}
    178 	out = {};
    179 	setReadOnly( out, 'x', gridX );
    180 	setReadOnly( out, 'y', gridY );
    181 	setReadOnly( out, 'z', z );
    182 	return out;
    183 }
    184 
    185 
    186 // EXPORTS //
    187 
    188 module.exports = kde2d;