From b07e59406addac038c147e408ef7b0f50bdf1836 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Mon, 22 Jun 2015 12:50:52 +0200 Subject: [PATCH 1/5] [UPDATE] dotfiles, LICENSE, eqn, Makefile --- .editorconfig | 11 ++++++++ .gitattributes | 1 + .jshintignore | 14 ++++++++++ .jshintrc | 71 +++++++++++++++++++++++++++++++++++++++++++++++ .npmignore | 5 +++- .travis.yml | 11 ++++++-- LICENSE | 4 +-- Makefile | 37 ++++++++++++++++++------ docs/img/eqn1.svg | 56 +++++++++++++++++++++++++++++++++++++ docs/img/eqn2.svg | 54 +++++++++++++++++++++++++++++++++++ 10 files changed, 251 insertions(+), 13 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .jshintignore create mode 100644 .jshintrc create mode 100644 docs/img/eqn1.svg create mode 100644 docs/img/eqn2.svg diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8e74bf3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = tab +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 0000000..3163c22 --- /dev/null +++ b/.jshintignore @@ -0,0 +1,14 @@ + +# Directories # +############### +build/ +reports/ +dist/ + +# Node.js # +########### +/node_modules/ + +# Git # +####### +.git* diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..d09f1fa --- /dev/null +++ b/.jshintrc @@ -0,0 +1,71 @@ +{ + "bitwise": false, + "camelcase": false, + "curly": true, + "eqeqeq": true, + "es3": false, + "forin": true, + "freeze": true, + "immed": true, + "indent": 4, + "latedef": "nofunc", + "newcap": true, + "noarg": true, + "noempty": false, + "nonbsp": true, + "nonew": true, + "plusplus": false, + "quotmark": "single", + "undef": true, + "unused": true, + "strict": true, + "maxparams": 10, + "maxdepth": 5, + "maxstatements": 100, + "maxcomplexity": false, + "maxlen": 1000, + "asi": false, + "boss": false, + "debug": false, + "eqnull": false, + "esnext": false, + "evil": false, + "expr": false, + "funcscope": false, + "globalstrict": false, + "iterator": false, + "lastsemic": false, + "laxbreak": false, + "laxcomma": false, + "loopfunc": false, + "maxerr": 1000, + "moz": false, + "multistr": false, + "notypeof": false, + "proto": false, + "scripturl": false, + "shadow": false, + "sub": true, + "supernew": false, + "validthis": false, + "noyield": false, + "browser": true, + "browserify": true, + "couch": false, + "devel": true, + "dojo": false, + "jasmine": false, + "jquery": false, + "mocha": true, + "mootools": false, + "node": true, + "nonstandard": false, + "prototypejs": false, + "qunit": false, + "rhino": false, + "shelljs": false, + "worker": false, + "wsh": false, + "yui": false, + "globals": {} +} \ No newline at end of file diff --git a/.npmignore b/.npmignore index 42781b3..9db298d 100644 --- a/.npmignore +++ b/.npmignore @@ -13,6 +13,7 @@ examples/ reports/ support/ test/ +benchmark/ # Node.js # ########### @@ -46,4 +47,6 @@ Desktop.ini # Utilities # ############# .jshintrc -.travis.yml \ No newline at end of file +.jshintignore +.travis.yml +.editorconfig diff --git a/.travis.yml b/.travis.yml index 7c16620..29cff27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,12 @@ language: node_js node_js: - - "0.10" + - '0.12' + - '0.11' + - '0.10' + - '0.8' + - 'iojs' +before_install: + - npm update -g npm after_script: - - npm run coveralls \ No newline at end of file + - npm run coveralls + diff --git a/LICENSE b/LICENSE index 2fe3939..93cf2ea 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Athan Reines. +Copyright (c) 2014-2015 The Compute.io Authors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/Makefile b/Makefile index e3cad30..c6f009c 100644 --- a/Makefile +++ b/Makefile @@ -5,25 +5,29 @@ # Set the node.js environment to test: NODE_ENV ?= test +# Kernel name: +KERNEL ?= $(shell uname -s) + +ifeq ($(KERNEL), Darwin) + OPEN ?= open +else + OPEN ?= xdg-open +endif # NOTES # -NOTES ?= 'TODO|FIXME' +NOTES ?= 'TODO|FIXME|WARNING|HACK|NOTE' # MOCHA # -# Specify the test framework bin locations: MOCHA ?= ./node_modules/.bin/mocha _MOCHA ?= ./node_modules/.bin/_mocha - -# Specify the mocha reporter: MOCHA_REPORTER ?= spec # ISTANBUL # -# Istanbul configuration: ISTANBUL ?= ./node_modules/.bin/istanbul ISTANBUL_OUT ?= ./reports/coverage ISTANBUL_REPORT ?= lcov @@ -31,6 +35,12 @@ ISTANBUL_LCOV_INFO_PATH ?= $(ISTANBUL_OUT)/lcov.info ISTANBUL_HTML_REPORT_PATH ?= $(ISTANBUL_OUT)/lcov-report/index.html +# JSHINT # + +JSHINT ?= ./node_modules/.bin/jshint +JSHINT_REPORTER ?= ./node_modules/jshint-stylish + + # FILES # @@ -81,7 +91,8 @@ test-istanbul-mocha: node_modules NODE_ENV=$(NODE_ENV) \ NODE_PATH=$(NODE_PATH_TEST) \ $(ISTANBUL) cover \ - --dir $(ISTANBUL_OUT) --report $(ISTANBUL_REPORT) \ + --dir $(ISTANBUL_OUT) \ + --report $(ISTANBUL_REPORT) \ $(_MOCHA) -- \ --reporter $(MOCHA_REPORTER) \ $(TESTS) @@ -95,9 +106,20 @@ test-istanbul-mocha: node_modules view-cov: view-istanbul-report view-istanbul-report: - open $(ISTANBUL_HTML_REPORT_PATH) + $(OPEN) $(ISTANBUL_HTML_REPORT_PATH) +# LINT # + +.PHONY: lint lint-jshint + +lint: lint-jshint + +lint-jshint: node_modules + $(JSHINT) \ + --reporter $(JSHINT_REPORTER) \ + ./ + # NODE # @@ -116,7 +138,6 @@ clean-node: # CLEAN # - .PHONY: clean clean: diff --git a/docs/img/eqn1.svg b/docs/img/eqn1.svg new file mode 100644 index 0000000..3822f81 --- /dev/null +++ b/docs/img/eqn1.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/img/eqn2.svg b/docs/img/eqn2.svg new file mode 100644 index 0000000..d2c62a1 --- /dev/null +++ b/docs/img/eqn2.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 36bbde3dc297a2e34dc13cf5ce22cd0c58e7f361 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Mon, 22 Jun 2015 20:26:36 +0200 Subject: [PATCH 2/5] [UPDATE] implementation --- lib/accessor.js | 56 +++++++++++++++++++ lib/array.js | 55 ++++++++++++++++++ lib/contains.js | 24 ++++++++ lib/index.js | 144 ++++++++++++++++++++++++++++-------------------- lib/matrix.js | 78 ++++++++++++++++++++++++++ lib/validate.js | 60 ++++++++++++++++++++ package.json | 41 ++++++++++---- 7 files changed, 385 insertions(+), 73 deletions(-) create mode 100644 lib/accessor.js create mode 100644 lib/array.js create mode 100644 lib/contains.js create mode 100644 lib/matrix.js create mode 100644 lib/validate.js diff --git a/lib/accessor.js b/lib/accessor.js new file mode 100644 index 0000000..31bab08 --- /dev/null +++ b/lib/accessor.js @@ -0,0 +1,56 @@ +'use strict'; + +// MODULES // + +var isNumber = require( 'validate.io-number-primitive' ); + +// FUNCTIONS // + +var contains = require( './contains.js' ); + +// NANSTDEV // + +/** +* FUNCTION: nanstdev( arr, encoding, clbk[, bias] ) +* Computes the standard deviation of an array using an accessor ignoring non-numeric / missing values.. +* +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} arr - input array +* @param {Array} encoding - array whose elements encode missing values +* @param {Function} accessor - accessor function for accessing array values +* @param {Boolean} [bias=false] - boolean indicating whether to calculate a biased or unbiased estimate of the standard deviation +* @returns {Number|Null} standard deviation or null +*/ +function nanstdev( arr, encoding, clbk, bias ) { + var len = arr.length, + N = 0, + delta = 0, + mean = 0, + M2 = 0, + x, i; + + if ( !len ) { + return null; + } + for ( i = 0; i < len; i++ ) { + x = clbk( arr[ i ], i ); + if ( !isNumber( x ) || contains( encoding, x ) ) { + continue; + } + N += 1; + delta = x - mean; + mean += delta / N; + M2 += delta * ( x - mean ); + } + if ( N < 2 ) { + return 0; + } + if ( bias ) { + return Math.sqrt( M2 / N ); + } + return Math.sqrt( M2 / ( N - 1 ) ); +} // end FUNCTION nanstdev() + + +// EXPORTS // + +module.exports = nanstdev; diff --git a/lib/array.js b/lib/array.js new file mode 100644 index 0000000..5ef1012 --- /dev/null +++ b/lib/array.js @@ -0,0 +1,55 @@ +'use strict'; + +// MODULES // + +var isNumber = require( 'validate.io-number-primitive' ); + +// FUNCTIONS // + +var contains = require( './contains.js' ); + +// NANSTDEV // + +/** +* FUNCTION: nanstdev( arr, encoding[, bias] ) +* Computes the standard deviation of an array ignoring non-numeric / missing values.. +* +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} arr - input array +* @param {Array} encoding - array whose elements encode missing values +* @param {Boolean} [bias=false] - boolean indicating whether to calculate a biased or unbiased estimate of the standard deviation +* @returns {Number|Null} standard deviation or null +*/ +function nanstdev( arr, encoding, bias ) { + var len = arr.length, + N = 0, + delta = 0, + mean = 0, + M2 = 0, + x, i; + + if ( !len ) { + return null; + } + for ( i = 0; i < len; i++ ) { + x = arr[ i ]; + if ( !isNumber( x ) || contains( encoding, x ) ) { + continue; + } + N += 1; + delta = x - mean; + mean += delta / N; + M2 += delta * ( x - mean ); + } + if ( N < 2 ) { + return 0; + } + if ( bias ) { + return Math.sqrt( M2 / N ); + } + return Math.sqrt( M2 / ( N - 1 ) ); +} // end FUNCTION nanstdev() + + +// EXPORTS // + +module.exports = nanstdev; diff --git a/lib/contains.js b/lib/contains.js new file mode 100644 index 0000000..5a200d3 --- /dev/null +++ b/lib/contains.js @@ -0,0 +1,24 @@ +'use strict'; + +/** +* FUNCTION: contains( arr, val ) +* Checks whether input array contains a certain value +* +* @private +* @param {Array} arr - input array +* @param {*} val - input value +* @returns {Number} 1 if input array contains val, 0 otherwise +*/ +function contains( arr, val ) { + var i, ret = 0; + for ( i = 0; i < arr.length; i++ ) { + if ( arr[ i ] === val) { + ret = 1; + } + } + return ret; +} // end FUNCTION contains() + +// EXPORTS // + +module.exports = contains; diff --git a/lib/index.js b/lib/index.js index 51c3085..3ff32b0 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,73 +1,95 @@ +'use strict'; + +// MODULES // + +var isArrayLike = require( 'validate.io-array-like' ), + isMatrixLike = require( 'validate.io-matrix-like' ), + ctors = require( 'compute-array-constructors' ), + matrix = require( 'dstructs-matrix' ).raw, + validate = require( './validate.js' ); + + +// FUNCTIONS // + +var nanstdev1 = require( './array.js' ), + nanstdev2 = require( './accessor.js' ), + nanstdev3 = require( './matrix.js' ); + + +// NANSTDEV // + /** +* FUNCTION: nanstdev( x[, opts] ) +* Computes the sample standard deviation ignoring non-numeric / missing values. * -* COMPUTE: nanstdev -* -* -* DESCRIPTION: -* - Computes the sample standard deviation over an array of values ignoring any values which are not numeric. -* -* -* NOTES: -* [1] -* -* -* TODO: -* [1] -* -* -* LICENSE: -* MIT -* -* Copyright (c) 2014. Athan Reines. -* -* -* AUTHOR: -* Athan Reines. kgryte@gmail.com. 2014. -* +* @param {Number[]|Array|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Matrix} x - input value +* @param {Object} [opts] - function options +* @param {Boolean} [opts.bias=false] - boolean indicating whether to calculate a biased or unbiased estimate of the standard deviation +* @param {Function} [opts.accessor] - accessor function for accessing array values +* @param {Number} [opts.dim=2] - dimension along which to compute the standard deviation. +* @param {String} [opts.dtype="float64"] - output data type +* @returns {Number|Matrix|Null} sample standard deviation(s) or null */ +function nanstdev( x, options ) { + /* jshint newcap:false */ + var opts = {}, + shape, + ctor, + err, + len, + dim, + dt, + d, + m, + encoding; -(function() { - 'use strict'; + if ( arguments.length > 1 ) { + err = validate( opts, options ); + if ( err ) { + throw err; + } + } - // NANSTDEV // + encoding = opts.encoding || []; - /** - * FUNCTION: nanstdev( arr ) - * Computes the sample standard deviation over an array of values ignoring non-numeric values. - * - * @param {Array} arr - array of values - * @returns {Number} sample standard deviation - */ - function nanstdev( arr ) { - if ( !Array.isArray( arr ) ) { - throw new TypeError( 'nanstdev()::invalid input argument. Must provide an array.' ); - } - var len = arr.length, - N = 0, - mean = 0, - M2 = 0, - delta = 0, - x; + if ( isMatrixLike( x ) ) { + dt = opts.dtype || 'float64'; + dim = opts.dim; - for ( var i = 0; i < len; i++ ) { - x = arr[ i ]; - if ( typeof x !== 'number' || x !== x ) { - continue; - } - N += 1; - delta = x - mean; - mean += delta / N; - M2 += delta * ( x - mean ); + // Determine if provided a vector... + if ( x.shape[ 0 ] === 1 || x.shape[ 1 ] === 1 ) { + // Treat as an array-like object: + return nanstdev1( x.data, encoding, opts.bias ); } - if ( N < 2 ) { - return 0; + if ( dim > 2 ) { + throw new RangeError( 'nanstdev()::invalid option. Dimension option exceeds number of matrix dimensions. Option: `' + dim + '`.' ); } - return Math.sqrt( M2 / ( N-1 ) ); - } // end FUNCTION nanstdev() - + if ( dim === void 0 || dim === 2 ) { + len = x.shape[ 0 ]; + shape = [ len, 1 ]; + } else { + len = x.shape[ 1 ]; + shape = [ 1, len ]; + } + ctor = ctors( dt ); + if ( ctor === null ) { + throw new Error( 'nanstdev()::invalid option. Data type option does not have a corresponding array constructor. Option: `' + dt + '`.' ); + } + // Create an output matrix and calculate the harmonic means: + d = new ctor( len ); + m = matrix( d, shape, dt ); + return nanstdev3( m, x, encoding, opts.bias, dim ); + } + if ( isArrayLike( x ) ) { + if ( opts.accessor ) { + return nanstdev2( x, encoding, opts.accessor, opts.bias ); + } + return nanstdev1( x, encoding, opts.bias ); + } + throw new TypeError( 'nanstdev()::invalid input argument. First argument must be either an array or a matrix. Value: `' + x + '`.' ); +} // end FUNCTION nanstdev() - // EXPORTS // - module.exports = nanstdev; +// EXPORTS // -})(); \ No newline at end of file +module.exports = nanstdev; diff --git a/lib/matrix.js b/lib/matrix.js new file mode 100644 index 0000000..214483e --- /dev/null +++ b/lib/matrix.js @@ -0,0 +1,78 @@ +'use strict'; + +// MODULES // + +var isNumber = require( 'validate.io-number-primitive' ); + +// FUNCTIONS // + +var contains = require( './contains.js' ); + +// NANSTDEV // + +/** +* FUNCTION: nanstdev( out, mat,[bias , dim] ) +* Computes the standard deviation along a matrix dimension ignoring non-numeric / missing values. +* +* @param {Matrix} out - output matrix +* @param {Matrix} mat - input matrix +* @param {Array} encoding - array whose elements encode missing values +* @param {Boolean} [bias=false] - boolean indicating whether to calculate a biased or unbiased estimate of the standard deviation +* @param {Number} [dim=2] - matrix dimension along which to compute the standard deviation. If `dim=1`, compute along matrix rows. If `dim=2`, compute along matrix columns. +* @returns {Matrix|Null} standard deviation(s) or null +*/ +function nanstdev( out, mat, encoding, bias, dim ) { + var mu, delta, x, M2, + M, N, Nobs, + s0, s1, + o, + i, j, k; + + if ( dim === 1 ) { + // Compute along the rows... + M = mat.shape[ 1 ]; + N = mat.shape[ 0 ]; + s0 = mat.strides[ 1 ]; + s1 = mat.strides[ 0 ]; + } else { + // Compute along the columns... + M = mat.shape[ 0 ]; + N = mat.shape[ 1 ]; + s0 = mat.strides[ 0 ]; + s1 = mat.strides[ 1 ]; + } + if ( M === 0 || N === 0 ) { + return null; + } + o = mat.offset; + + for ( i = 0; i < M; i++ ) { + k = o + i * s0; + Nobs = 0; + mu = 0; + delta = 0; + M2 = 0; + for ( j = 0; j < N; j++ ) { + x = mat.data[ k + j*s1 ]; + if ( !isNumber( x ) || contains( encoding, x ) ) { + continue; + } + Nobs += 1; + delta = x - mu; + mu += delta / (j+1); + M2 += delta * ( x - mu ); + } + if ( bias ) { + out.data[ i ] = Math.sqrt( M2 / ( Nobs ) ); + } else { + out.data[ i ] = Math.sqrt( M2 / ( Nobs - 1 ) ); + } + } + + return out; +} // end FUNCTION nanstdev() + + +// EXPORTS // + +module.exports = nanstdev; diff --git a/lib/validate.js b/lib/validate.js new file mode 100644 index 0000000..8cc5249 --- /dev/null +++ b/lib/validate.js @@ -0,0 +1,60 @@ +'use strict'; + +// MODULES // + +var isObject = require( 'validate.io-object' ), + isFunction = require( 'validate.io-function' ), + isString = require( 'validate.io-string-primitive' ), + isBoolean = require( 'validate.io-boolean-primitive'), + isPositiveInteger = require( 'validate.io-positive-integer' ); + + +// VALIDATE // + +/** +* FUNCTION: validate( opts, options ) +* Validates function options. +* +* @param {Object} opts - destination for validated options +* @param {Object} options - function options +* @param {Function} [options.accessor] - accessor function for accessing array values +* @param {Number} [options.dim] - dimension +* @param {String} [options.dtype] - output data type +* @param {Boolean} [options.bias=false] - boolean indicating whether to calculate a biased or unbiased estimate of the standard deviation +* @returns {Null|Error} null or an error +*/ +function validate( opts, options ) { + if ( !isObject( options ) ) { + return new TypeError( 'nanstdev()::invalid input argument. Options argument must be an object. Value: `' + options + '`.' ); + } + if ( options.hasOwnProperty( 'accessor' ) ) { + opts.accessor = options.accessor; + if ( !isFunction( opts.accessor ) ) { + return new TypeError( 'nanstdev()::invalid option. Accessor must be a function. Option: `' + opts.accessor + '`.' ); + } + } + if ( options.hasOwnProperty( 'dim' ) ) { + opts.dim = options.dim; + if ( !isPositiveInteger( opts.dim ) ) { + return new TypeError( 'nanstdev()::invalid option. Dimension option must be a positive integer. Option: `' + opts.dim + '`.' ); + } + } + if ( options.hasOwnProperty( 'dtype' ) ) { + opts.dtype = options.dtype; + if ( !isString( opts.dtype ) ) { + return new TypeError( 'nanstdev()::invalid option. Data type option must be a string primitive. Option: `' + opts.dtype + '`.' ); + } + } + if ( options.hasOwnProperty( 'bias' ) ) { + opts.bias = options.bias; + if ( !isBoolean( opts.bias ) ) { + return new TypeError( 'nanstdev()::invalid option. Bias option must be a Boolean primitive. Option: `' + opts.bias + '`.' ); + } + } + return null; +} // end FUNCTION validate() + + +// EXPORTS // + +module.exports = validate; diff --git a/package.json b/package.json index 0042e9f..72ddd86 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "compute-nanstdev", "version": "1.0.0", - "description": "Computes the sample standard deviation over an array of values ignoring any values which are not numeric.", + "description": "Computes the sample standard deviation ignoring any values which are not numeric or encoded as missing values.", "author": { "name": "Athan Reines", "email": "kgryte@gmail.com" @@ -10,6 +10,10 @@ { "name": "Athan Reines", "email": "kgryte@gmail.com" + }, + { + "name": "Philipp Burckhardt", + "email": "pburckhardt@outlook.com" } ], "scripts": { @@ -26,27 +30,40 @@ "compute.io", "compute", "computation", + "mathematics", + "math", "statistics", "stats", "standard deviation", "sample standard deviation", "stdev", - "nan" + "nan", + "missing values" ], "bugs": { "url": "https://github.com/compute-io/nanstdev/issues" }, - "dependencies": {}, + "dependencies": { + "compute-array-constructors": "^1.0.0", + "dstructs-matrix": "^2.0.0", + "validate.io-array-like": "^1.0.0", + "validate.io-boolean-primitive": "^1.0.0", + "validate.io-function": "^1.0.2", + "validate.io-matrix-like": "^1.0.2", + "validate.io-number": "^1.0.3", + "validate.io-number-primitive": "^1.0.0", + "validate.io-object": "^1.0.4", + "validate.io-positive-integer": "^1.0.0", + "validate.io-string-primitive": "^1.0.0" + }, "devDependencies": { - "chai": "1.x.x", - "mocha": "1.x.x", + "chai": "3.x.x", + "mocha": "2.x.x", "coveralls": "^2.11.1", - "istanbul": "^0.3.0" + "istanbul": "^0.3.0", + "jshint": "2.x.x", + "jshint-stylish": "^2.0.0", + "validate.io-nan": "^1.0.3" }, - "licenses": [ - { - "type": "MIT", - "url": "http://www.opensource.org/licenses/MIT" - } - ] + "license": "MIT" } From 13e257eb14a6065d92409ad34563e0b95663d288 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Mon, 22 Jun 2015 20:56:07 +0200 Subject: [PATCH 3/5] [UPDATE] tests --- lib/array.js | 2 +- lib/validate.js | 17 +++- package.json | 1 + test/test.accessor.js | 104 +++++++++++++++++++++ test/test.array.js | 58 ++++++++++++ test/test.js | 213 ++++++++++++++++++++++++++++++++++++++---- test/test.matrix.js | 118 +++++++++++++++++++++++ test/test.validate.js | 172 ++++++++++++++++++++++++++++++++++ 8 files changed, 661 insertions(+), 24 deletions(-) create mode 100644 test/test.accessor.js create mode 100644 test/test.array.js create mode 100644 test/test.matrix.js create mode 100644 test/test.validate.js diff --git a/lib/array.js b/lib/array.js index 5ef1012..e921442 100644 --- a/lib/array.js +++ b/lib/array.js @@ -12,7 +12,7 @@ var contains = require( './contains.js' ); /** * FUNCTION: nanstdev( arr, encoding[, bias] ) -* Computes the standard deviation of an array ignoring non-numeric / missing values.. +* Computes the standard deviation of an array ignoring non-numeric / missing values. * * @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} arr - input array * @param {Array} encoding - array whose elements encode missing values diff --git a/lib/validate.js b/lib/validate.js index 8cc5249..b12d240 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -2,7 +2,8 @@ // MODULES // -var isObject = require( 'validate.io-object' ), +var isArray = require( 'validate.io-array' ), + isObject = require( 'validate.io-object' ), isFunction = require( 'validate.io-function' ), isString = require( 'validate.io-string-primitive' ), isBoolean = require( 'validate.io-boolean-primitive'), @@ -33,6 +34,12 @@ function validate( opts, options ) { return new TypeError( 'nanstdev()::invalid option. Accessor must be a function. Option: `' + opts.accessor + '`.' ); } } + if ( options.hasOwnProperty( 'bias' ) ) { + opts.bias = options.bias; + if ( !isBoolean( opts.bias ) ) { + return new TypeError( 'nanstdev()::invalid option. Bias option must be a Boolean primitive. Option: `' + opts.bias + '`.' ); + } + } if ( options.hasOwnProperty( 'dim' ) ) { opts.dim = options.dim; if ( !isPositiveInteger( opts.dim ) ) { @@ -45,10 +52,10 @@ function validate( opts, options ) { return new TypeError( 'nanstdev()::invalid option. Data type option must be a string primitive. Option: `' + opts.dtype + '`.' ); } } - if ( options.hasOwnProperty( 'bias' ) ) { - opts.bias = options.bias; - if ( !isBoolean( opts.bias ) ) { - return new TypeError( 'nanstdev()::invalid option. Bias option must be a Boolean primitive. Option: `' + opts.bias + '`.' ); + if ( options.hasOwnProperty( 'encoding' ) ) { + opts.encoding = options.encoding; + if ( !isArray( opts.encoding ) ) { + return new TypeError( 'nanvariance()::invalid option. Encoding option must be an array. Option: `' + opts.encoding + '`.' ); } } return null; diff --git a/package.json b/package.json index 72ddd86..5d30d6d 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "dependencies": { "compute-array-constructors": "^1.0.0", "dstructs-matrix": "^2.0.0", + "validate.io-array": "^1.0.6", "validate.io-array-like": "^1.0.0", "validate.io-boolean-primitive": "^1.0.0", "validate.io-function": "^1.0.2", diff --git a/test/test.accessor.js b/test/test.accessor.js new file mode 100644 index 0000000..de0e734 --- /dev/null +++ b/test/test.accessor.js @@ -0,0 +1,104 @@ +/* global describe, it, require */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Module to be tested: + nanstdev = require( './../lib/accessor.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'accessor standard deviation', function tests() { + + it( 'should export a function', function test() { + expect( nanstdev ).to.be.a( 'function' ); + }); + + it( 'should compute the standard deviation using an accessor', function test() { + var data, expected; + + data = [ + {'x':2}, + {'x':4}, + {'x':5}, + {'x':3}, + {'x':8}, + {'x':2}, + {'x':true}, + {'x':NaN}, + {'x':{}}, + {'x':function(){}}, + // encode missing values as 999 + {'x':999}, + {'x':999} + ]; + + expected = 2.280350850198276; + + assert.strictEqual( nanstdev( data, [ 999 ], getValue ), expected ); + + function getValue( d ) { + return d.x; + } + }); + + it( 'should compute the (biased) standard deviation using an accessor', function test() { + var data, expected; + + data = [ + {'x':2}, + {'x':4}, + {'x':5}, + {'x':3}, + {'x':8}, + {'x':2}, + {'x':true}, + {'x':NaN}, + {'x':{}}, + {'x':function(){}}, + // encode missing values as 999 + {'x':999}, + {'x':999} + ]; + + expected = 2.0816659994661326; + + assert.strictEqual( nanstdev( data, [ 999 ], getValue, true ), expected ); + + function getValue( d ) { + return d.x; + } + }); + + it( 'should return 0 for a single element array', function test() { + var data, expected; + + data = [ {'x':2} ]; + expected = 0; + + assert.strictEqual( nanstdev( data, [], getValue ), expected ); + + function getValue( d ) { + return d.x; + } + }); + + it( 'should return null if provided an empty array', function test() { + assert.isNull( nanstdev( [], [], getValue ) ); + + function getValue( d ) { + return d.x; + } + }); + +}); diff --git a/test/test.array.js b/test/test.array.js new file mode 100644 index 0000000..13439b1 --- /dev/null +++ b/test/test.array.js @@ -0,0 +1,58 @@ +/* global describe, it, require */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Module to be tested: + nanstdev = require( './../lib/array.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'array standard deviation', function tests() { + + it( 'should export a function', function test() { + expect( nanstdev ).to.be.a( 'function' ); + }); + + it( 'should compute the standard deviation', function test() { + var data, expected; + + data = [ 2, 4, 5, 3, 8, 2, null, NaN, true, function(){}, {}, 'string', 999, 999 ]; + expected = 2.280350850198276; + + assert.strictEqual( nanstdev( data, [ 999 ] ), expected ); + }); + + it( 'should compute the (biased) standard deviation', function test() { + var data, expected; + + data = [ 2, 4, 5, 3, 8, 2, null, NaN, true, function(){}, {}, 'string', 999, 999 ]; + expected = 2.0816659994661326; + + assert.strictEqual( nanstdev( data, [ 999 ], true ), expected ); + }); + + it( 'should return 0 for a single element array', function test() { + var data, expected; + + data = [ 2 ]; + expected = 0; + + assert.strictEqual( nanstdev( data, [] ), expected ); + }); + + it( 'should return null if provided an empty array', function test() { + assert.isNull( nanstdev( [], [] ) ); + }); + +}); diff --git a/test/test.js b/test/test.js index b7a5476..24d4bf9 100644 --- a/test/test.js +++ b/test/test.js @@ -1,9 +1,14 @@ +/* global describe, it, require */ +'use strict'; // MODULES // var // Expectation library: chai = require( 'chai' ), + // Matrix data structure: + matrix = require( 'dstructs-matrix' ), + // Module to be tested: nanstdev = require( './../lib' ); @@ -17,23 +22,22 @@ var expect = chai.expect, // TESTS // describe( 'compute-nanstdev', function tests() { - 'use strict'; it( 'should export a function', function test() { expect( nanstdev ).to.be.a( 'function' ); }); - it( 'should throw an error if provided a non-array', function test() { + it( 'should throw an error if the first argument is neither array-like or matrix-like', function test() { var values = [ - '5', - 5, - true, - undefined, - null, - NaN, - function(){}, - {} - ]; + // '5', // valid as is array-like (length) + 5, + true, + undefined, + null, + NaN, + function(){}, + {} + ]; for ( var i = 0; i < values.length; i++ ) { expect( badValue( values[i] ) ).to.throw( TypeError ); @@ -45,22 +49,195 @@ describe( 'compute-nanstdev', function tests() { } }); - it( 'should compute the sample standard deviation', function test() { + it( 'should throw an error if provided an unrecognized/unsupported data type option', function test() { + var values = [ + 'beep', + 'boop' + ]; + + for ( var i = 0; i < values.length; i++ ) { + expect( badValue( values[i] ) ).to.throw( Error ); + } + function badValue( value ) { + return function() { + nanstdev( matrix( [2,2] ), { + 'dtype': value + }); + }; + } + }); + + it( 'should throw an error if provided a dim option which is not a positive integer', function test() { + var data, values; + + values = [ + '5', + -5, + 2.2, + true, + undefined, + null, + NaN, + [], + {} + ]; + + data = matrix( new Int32Array([1,2,3,4]), [2,2] ); + + for ( var i = 0; i < values.length; i++ ) { + expect( badValue( values[ i ] ) ).to.throw( Error ); + } + function badValue( value ) { + return function() { + nanstdev( data, { + 'dim': value + }); + }; + } + }); + + it( 'should throw an error if provided a dim option which exceeds the number of matrix dimensions (2)', function test() { + var data, values; + + values = [ + 3, + 4, + 5 + ]; + + data = matrix( new Int32Array([1,2,3,4]), [2,2] ); + + for ( var i = 0; i < values.length; i++ ) { + expect( badValue( values[ i ] ) ).to.throw( RangeError ); + } + function badValue( value ) { + return function() { + nanstdev( data, { + 'dim': value + }); + }; + } + }); + + it( 'should compute the sample standard deviation ignoring non-numeric values', function test() { var data, expected; - data = [ 2, 4, NaN, 5, 3, true, null, undefined, 8, [], {}, function(){}, 2 ]; - expected = Math.sqrt( 5.2 ); + data = [ 2, 4, 5, 3, 8, 2, null, {}, function(){}, true ]; + expected = Math.sqrt(5.2); assert.strictEqual( nanstdev( data ), expected ); }); - it( 'should return 0 for an array containing a single numeric element', function test() { + it( 'should compute the sample standard deviation ignoring missing values', function test() { var data, expected; - data = [ 2, NaN, null, false ]; - expected = 0; + data = [ 2, 999, 4, 5, 3, 8, 2, 999, 981 ]; + expected = Math.sqrt( 5.2 ); + + assert.strictEqual( nanstdev( data, {'encoding': [981, 999] } ), expected ); + }); + + it( 'should compute the sample standard deviation of a typed array ignoring non-numeric / missing values', function test() { + var data, expected; + + data = new Int32Array( [ 2, 4, 5, 3, 8, 2, 981, 999 ] ); + expected = Math.sqrt( 5.2 ); + + assert.strictEqual( nanstdev( data, {'encoding': [981, 999] } ), expected ); + }); + + it( 'should compute the (biased) sample standard deviation ignoring non-numeric values', function test() { + var data, expected, actual; + + data = [ 2, 4, 5, 3, 8, 2, null, {}, function(){}, true ]; + expected = Math.sqrt( 5.2 * (6-1) / 6 ); + + actual = nanstdev( data, { + 'bias': true + }); + + assert.strictEqual( actual, expected ); + }); + + it( 'should compute the sample standard deviation using an accessor function ignoring non-numeric values', function test() { + var data, expected, actual; + + data = [ + {'x':2}, + {'x':4}, + {'x':5}, + {'x':3}, + {'x':8}, + {'x':2}, + {'x':true}, + {'x':NaN}, + {'x':{}}, + {'x':function(){}}, + {'x':'string'} + ]; + actual = nanstdev( data, { + 'accessor': getValue + }); + expected = Math.sqrt( 5.2 ); + + assert.strictEqual( actual, expected ); + + function getValue( d ) { + return d.x; + } + }); + + it( 'should return `null` when provided an empty array', function test() { + assert.isNull( nanstdev( [] ) ); + }); + + it( 'should calculate the column standard deviations of a matrix', function test() { + var data, expected, s; + + data = matrix( new Int32Array( [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ), [3,3] ); + expected = matrix( new Float64Array( [ 1, 1, 1 ] ), [3,1] ); + + s = nanstdev( data, { + 'dim': 2 + }); + + assert.deepEqual( s.data, expected.data ); + }); + + it( 'should calculate the row standard deviations of a matrix', function test() { + var data, expected, s; + + data = matrix( new Int32Array( [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ), [3,3] ); + expected = matrix( new Float64Array( [ 3, 3, 3 ] ), [1, 3] ); + + s = nanstdev( data, { + 'dim': 1 + }); + + assert.deepEqual( s.data, expected.data ); + }); + + it( 'should calculate the standard deviations of a matrix and output a matrix having a specified data type', function test() { + var data, expected, s; + + data = matrix( new Int32Array( [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ), [3,3] ); + expected = matrix( new Int32Array( [ 1, 1, 1 ] ), [3,1] ); + + s = nanstdev( data, { + 'dtype': 'int32' + }); + + assert.strictEqual( s.dtype, 'int32' ); + assert.deepEqual( s.data, expected.data ); + }); + + it( 'should compute the standard deviation for a vector (matrix with one column or row)', function test() { + var data, expected; + + expected = Math.sqrt( 5.2 ); + data = matrix( new Int32Array( [ 2, 4, 5, 3, 8, 2 ] ), [6,1] ); assert.strictEqual( nanstdev( data ), expected ); }); -}); \ No newline at end of file +}); diff --git a/test/test.matrix.js b/test/test.matrix.js new file mode 100644 index 0000000..0a79358 --- /dev/null +++ b/test/test.matrix.js @@ -0,0 +1,118 @@ +/* global describe, it, require, beforeEach */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Matrix data structure: + matrix = require( 'dstructs-matrix' ), + + // Module to be tested: + nanstdev = require( './../lib/matrix.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'matrix nanstdev', function tests() { + + var data, + mat, + i; + + data = new Int32Array( 30 ); + for ( i = 0; i < data.length; i++ ) { + if ( i < 25 ) { + data[ i ] = i + 1; + } else { + data[ i ] = 999; + } + } + + beforeEach( function before() { + mat = matrix( data, [6,5], 'int32' ); + }); + + it( 'should export a function', function test() { + expect( nanstdev ).to.be.a( 'function' ); + }); + + it( 'should compute the standard deviation along matrix columns ignoring missing values', function test() { + var out, p, expected; + + out = matrix( [6,1], 'float64' ); + + p = nanstdev( out, mat, [ 999 ] ); + expected = '1.5811388300841898;1.5811388300841898;1.5811388300841898;1.5811388300841898;1.5811388300841898;0'; + + assert.strictEqual( p.toString(), expected ); + + p = nanstdev( out, mat, [ 999 ], false, 2 ); + expected = '1.5811388300841898;1.5811388300841898;1.5811388300841898;1.5811388300841898;1.5811388300841898;0'; + + assert.strictEqual( p.toString(), expected ); + + // Flip matrix up-down: + mat.strides[ 0 ] *= -1; + mat.offset = mat.length + mat.strides[ 0 ]; + + p = nanstdev( out, mat, [ 999 ] ); + expected = '0;1.5811388300841898;1.5811388300841898;1.5811388300841898;1.5811388300841898;1.5811388300841898'; + + assert.strictEqual( p.toString(), expected, 'flipud' ); + }); + + it( 'should compute the standard deviation along matrix rows ignoring missing values', function test() { + var out, p, expected; + + out = matrix( [1,5], 'float64' ); + + p = nanstdev( out, mat, [ 999 ], false, 1 ); + expected = '7.905694150420948,7.905694150420948,7.905694150420948,7.905694150420948,7.905694150420948'; + + assert.strictEqual( p.toString(), expected ); + + // Flip matrix left-right: + mat.strides[ 1 ] *= -1; + mat.offset = mat.strides[ 0 ] - 1; + + p = nanstdev( out, mat, [ 999 ], false, 1 ); + expected = '7.905694150420948,7.905694150420948,7.905694150420948,7.905694150420948,7.905694150420948'; + + assert.strictEqual( p.toString(), expected, 'fliplr' ); + }); + + it( 'should compute the population (biased sample) standard deviation ignoring missing values', function test() { + var out, p, expected; + + out = matrix( [1,6], 'float64' ); + + p = nanstdev( out, mat, [ 999 ], true, 1 ); + expected = '7.0710678118654755,7.0710678118654755,7.0710678118654755,7.0710678118654755,7.0710678118654755,0'; + + assert.strictEqual( p.toString(), expected ); + }); + + it( 'should return null if provided a matrix having one or more zero dimensions', function test() { + var out, mat; + + out = matrix( [0,0] ); + + mat = matrix( [0,10] ); + assert.isNull( nanstdev( out, mat, [] ) ); + + mat = matrix( [10,0] ); + assert.isNull( nanstdev( out, mat, [] ) ); + + mat = matrix( [0,0] ); + assert.isNull( nanstdev( out, mat, [] ) ); + }); + +}); diff --git a/test/test.validate.js b/test/test.validate.js new file mode 100644 index 0000000..83b239b --- /dev/null +++ b/test/test.validate.js @@ -0,0 +1,172 @@ +/* global describe, it, require */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Module to be tested: + validate = require( './../lib/validate.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + +// TESTS // + +describe( 'validate', function tests() { + + it( 'should export a function', function test() { + expect( validate ).to.be.a( 'function' ); + }); + + it( 'should return an error if provided an options argument which is not an object', function test() { + var values = [ + '5', + 5, + true, + undefined, + null, + NaN, + function(){}, + [] + ]; + + for ( var i = 0; i < values.length; i++ ) { + assert.isTrue( validate( {}, values[ i ] ) instanceof TypeError ); + } + }); + + it( 'should return an error if provided an accessor which is not a function', function test() { + var values, err; + + values = [ + '5', + 5, + true, + undefined, + null, + NaN, + [], + {} + ]; + + for ( var i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'accessor': values[ i ] + }); + assert.isTrue( err instanceof TypeError ); + } + }); + + it( 'should return an error if provided a dim option which is not a positive integer', function test() { + var values, err; + + values = [ + '5', + Math.PI, + -1, + 0, + true, + undefined, + null, + NaN, + [], + {}, + function(){} + ]; + + for ( var i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'dim': values[ i ] + }); + assert.isTrue( err instanceof TypeError ); + } + }); + + it( 'should return an error if provided a dtype option which is not a string primitive', function test() { + var values, err; + + values = [ + 5, + true, + undefined, + null, + NaN, + [], + {}, + function(){} + ]; + + for ( var i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'dtype': values[ i ] + }); + assert.isTrue( err instanceof TypeError ); + } + }); + + it( 'should throw an error if provided a bias option which is not a boolean primitive', function test() { + var values, err; + + values = [ + '5', + 5, + [], + new Boolean( false ), + undefined, + null, + NaN, + function(){}, + {} + ]; + + for ( var i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'bias': values[ i ] + }); + assert.isTrue( err instanceof TypeError ); + } + }); + + + it( 'should return an error if provided an encoding option which is not an array', function test() { + var values, err; + + values = [ + '5', + 5, + true, + undefined, + null, + NaN, + function(){}, + {} + ]; + + for ( var i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'encoding': values[ i ] + }); + assert.isTrue( err instanceof TypeError ); + } + }); + + + it( 'should return null if all options are valid', function test() { + var err; + + err = validate( {}, { + 'accessor': function getValue(){}, + 'dim': 2, + 'dtype': 'int32', + 'encoding': [ 999 ] + }); + + assert.isNull( err ); + }); + +}); From b6c317ccb52876d2cca0b06b4c2aed5fa967e6af Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Mon, 22 Jun 2015 20:59:25 +0200 Subject: [PATCH 4/5] [UPDATE] README.md --- README.md | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 185 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4098c24..e822617 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,24 @@ nanstdev === [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Dependencies][dependencies-image]][dependencies-url] -> Computes the sample standard deviation over an array of values ignoring any values which are not numeric. +> Computes the sample standard deviation ignoring any values which are not numeric or encoded as missing values. + +The biased [standard deviation](http://en.wikipedia.org/wiki/Standard_deviation) is defined as + +
+ Equation for the biased sample standard deviation. +
+
+ +and the unbiased [standard deviation](http://en.wikipedia.org/wiki/Standard_deviation) is defined as + +
+ Equation for the sample standard deviation. +
+
+ +where `x_0, x_1,...,x_{N-1}` are individual data values and `N` is the total number of values in the data set. It is the square root of the [sample variance](http://en.wikipedia.org/wiki/Variance). + ## Installation @@ -16,22 +33,179 @@ For use in the browser, use [browserify](https://github.com/substack/node-browse ## Usage -To use the module, ``` javascript var nanstdev = require( 'compute-nanstdev' ); ``` -#### nanstdev( arr ) +#### nanstdev( x[, opts] ) -Computes the sample standard deviation ignoring non-numeric values. +Computes the sample standard deviation ignoring non-numeric values and values encoded as missing. `x` may be either an [`array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array), [`typed array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays), or [`matrix`](https://github.com/dstructs/matrix). + +For numeric `arrays`, ``` javascript -var data = [ 10, 2, 100, NaN, 34, NaN, 0 ]; +var data = [ 1, 4, NaN, 7, NaN ]; + +var s = nanstdev( data ); +// returns 3 -var sigma = nanstdev( data ); +data = new Float64Array( data ); +s = nanstdev( data ); +// returns 3 ``` +If the array or matrix contains missing values encoded by numbers, use the `encoding` option to ensure they do not affect the calculation: + +* __encoding__: `array` holding all values which will be regarded as missing values. Default: `[]`. + +``` javascript +var data, mu; + +data = [ 1, 981, 4, 999, 7 ]; +s = nanstdev( data, { + 'encoding': [ 981, 999 ] +}); +// returns 3 + +data = new Int32Array( data ); +s = nanstdev( data, { + 'encoding': [ 981, 999 ] +}); +// returns 3 +``` + +For object `arrays`, provide an accessor `function` for accessing numeric `array` values + +``` javascript +var data = [ + {'x':1}, + {'x':4}, + {'x':NaN}, + {'x':7}, + {'x':NaN} +]; + +function getValue( d ) { + return d.x; +} + +var s = nanstdev( data, { + 'accessor': getValue +}); +// returns 2 +``` + +By default, the function calculates the *unbiased* sample [standard deviation](http://en.wikipedia.org/wiki/Standard_deviation). To calculate the population standard deviation (or a *biased* sample standard deviation), set the `bias` option to `true`. + +``` javascript +var data = [ 1, 4, NaN, 7, NaN ]; + +var value = nanstdev( data, { + 'bias': true +}); +// returns 1.3333 +``` + +If provided a [`matrix`](https://github.com/dstructs/matrix), the function accepts the following two additional `options`: + +* __dim__: dimension along which to compute the [standard deviation](http://en.wikipedia.org/wiki/Standard_deviation). Default: `2` (along the columns). +* __dtype__: output [`matrix`](https://github.com/dstructs/matrix) data type. Default: `float64`. + +By default, the function computes the [standard deviation](http://en.wikipedia.org/wiki/Standard_deviation) along the columns (`dim=2`). + +``` javascript +var matrix = require( 'dstructs-matrix' ), + data, + mat, + s, + i; + +data = new Int8Array( 25 ); +for ( i = 0; i < data.length; i++ ) { + data[ i ] = i; +} +mat = matrix( data, [5,5], 'int8' ); +/* + [ 0 1 2 3 4 + 5 6 7 8 9 + 10 11 12 13 14 + 15 16 17 18 19 + 20 21 22 23 24 ] +*/ + +s = nanstdev( mat ); +/* + [ 1.581139 + 1.581139 + 1.581139 + 1.581139 + 1.581139 ] +*/ +``` + +To compute the [standard deviation](http://en.wikipedia.org/wiki/Standard_deviation) along the rows, set the `dim` option to `1`. + +``` javascript +s = nanstdev( mat, { + 'dim': 1 +}); +/* + [ 7.905694, 7.905694, 7.905694, 7.905694, 7.905694 ] +*/ +``` + +By default, the output [`matrix`](https://github.com/dstructs/matrix) data type is `float64`. To specify a different output data type, set the `dtype` option. + +``` javascript +s = nanstdev( mat, { + 'dim': 1, + 'dtype': 'uint8' +}); +/* + [ 7, 7, 7, 7, 7 ] +*/ + +var dtype = s.dtype; +// returns 'uint8' +``` + +If provided a [`matrix`](https://github.com/dstructs/matrix) having either dimension equal to `1`, the function treats the [`matrix`](https://github.com/dstructs/matrix) as a [`typed array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays) and returns a `numeric` value. + +``` javascript +data = [ 1, 4, 7 ]; + +// Row vector: +mat = matrix( new Int8Array( data ), [1,3], 'int8' ); +s = nanstdev( mat ); +// returns 2 + +// Column vector: +mat = matrix( new Int8Array( data ), [3,1], 'int8' ); +s = nanstdev( mat ); +// returns 2 +``` + +If provided an empty [`array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array), [`typed array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays), or [`matrix`](https://github.com/dstructs/matrix), the function returns `null`. + +``` javascript +s = nanstdev( [] ); +// returns null + +s = nanstdev( new Int8Array( [] ) ); +// returns null + +s = nanstdev( matrix( [0,0] ) ); +// returns null + +s = nanstdev( matrix( [0,10] ) ); +// returns null + +s = nanstdev( matrix( [10,0] ) ); +// returns null +``` + + ## Examples @@ -76,7 +250,7 @@ console.log( nanstdev( d1 ) === nanstdev( d2 ) ); ### Unit -Unit tests use the [Mocha](http://visionmedia.github.io/mocha) test framework with [Chai](http://chaijs.com) assertions. To run the tests, execute the following command in the top-level application directory: +Unit tests use the [Mocha](http://mochajs.org) test framework with [Chai](http://chaijs.com) assertions. To run the tests, execute the following command in the top-level application directory: ``` bash $ make test @@ -100,16 +274,15 @@ $ make view-cov ``` +--- ## License -[MIT license](http://opensource.org/licenses/MIT). +[MIT license](http://opensource.org/licenses/MIT). ---- ## Copyright -Copyright © 2014. Athan Reines. - +Copyright © 2014-2015. The [Compute.io](https://github.com/compute-io) Authors. [npm-image]: http://img.shields.io/npm/v/compute-nanstdev.svg [npm-url]: https://npmjs.org/package/compute-nanstdev @@ -127,4 +300,4 @@ Copyright © 2014. Athan Reines. [dev-dependencies-url]: https://david-dm.org/dev/compute-io/nanstdev [github-issues-image]: http://img.shields.io/github/issues/compute-io/nanstdev.svg -[github-issues-url]: https://github.com/compute-io/nanstdev/issues \ No newline at end of file +[github-issues-url]: https://github.com/compute-io/nanstdev/issues From f2e26e6035023e0899a2fd5a68994fa4c29343a2 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Tue, 23 Jun 2015 09:49:30 +0200 Subject: [PATCH 5/5] [UPDATE] README, examples --- README.md | 58 +++++++++++++++++++++++++++++++++---- examples/index.js | 74 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 122 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e822617..323e8ba 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,14 @@ nanstdev The biased [standard deviation](http://en.wikipedia.org/wiki/Standard_deviation) is defined as
- Equation for the biased sample standard deviation. + Equation for the biased sample standard deviation.
and the unbiased [standard deviation](http://en.wikipedia.org/wiki/Standard_deviation) is defined as
- Equation for the sample standard deviation. + Equation for the sample standard deviation.
@@ -210,19 +210,65 @@ s = nanstdev( matrix( [10,0] ) ); ## Examples ``` javascript -var nanstdev = require( 'compute-nanstdev' ); +var matrix = require( 'dstructs-matrix' ), + nanstdev = require( 'compute-nanstdev' ); -var data = new Array( 1000 ); +var data, + mat, + mu, + i; -for ( var i = 0; i < data.length; i++ ) { +// Plain arrays... +data = new Array( 1000 ); +for ( i = 0; i < data.length; i++ ) { if ( i%5 === 0 ) { data[ i ] = NaN; } else { data[ i ] = Math.random() * 100; } } +mu = nanstdev( data ); + +// Object arrays (accessors)... +function getValue( d ) { + return d.x; +} +for ( i = 0; i < data.length; i++ ) { + data[ i ] = { + 'x': data[ i ] + }; +} +mu = nanstdev( data, { + 'accessor': getValue +}); -console.log( nanstdev( data ) ); +// Typed arrays... +// only Float64Array and Float32Array support NaN +data = new Float64Array( 1000 ); +for ( i = 0; i < data.length; i++ ) { + if ( i%5 === 0 ) { + data[ i ] = NaN; + } else { + data[ i ] = Math.random() * 100; + } +} +mu = nanstdev( data ); + +// Matrices (along rows)... +mat = matrix( data, [100,10], 'float64' ); +mu = nanstdev( mat, { + 'dim': 1 +}); + +// Matrices (along columns)... +mu = nanstdev( mat, { + 'dim': 2 +}); + +// Matrices (custom output data type)... +mu = nanstdev( mat, { + 'dtype': 'uint8' +}); ``` To run the example code from the top-level application directory, diff --git a/examples/index.js b/examples/index.js index 699f97b..9e492a2 100644 --- a/examples/index.js +++ b/examples/index.js @@ -1,13 +1,79 @@ -var nanstdev = require( './../lib' ); +'use strict'; -var data = new Array( 1000 ); +var matrix = require( 'dstructs-matrix' ), + nanstdev = require( './../lib' ); -for ( var i = 0; i < data.length; i++ ) { +var data, + mat, + mu, + i; + + +// ---- +// Plain arrays... +data = new Array( 1000 ); +for ( i = 0; i < data.length; i++ ) { if ( i%5 === 0 ) { data[ i ] = NaN; } else { data[ i ] = Math.random() * 100; } } +mu = nanstdev( data ); +console.log( 'Arrays: %d\n', mu ); + + +// ---- +// Object arrays (accessors)... +function getValue( d ) { + return d.x; +} +for ( i = 0; i < data.length; i++ ) { + data[ i ] = { + 'x': data[ i ] + }; +} +mu = nanstdev( data, { + 'accessor': getValue +}); +console.log( 'Accessors: %d\n', mu ); + + +// ---- +// Typed arrays... +// only Float64Array and Float32Array support NaN +data = new Float64Array( 1000 ); +for ( i = 0; i < data.length; i++ ) { + if ( i%5 === 0 ) { + data[ i ] = NaN; + } else { + data[ i ] = Math.random() * 100; + } +} +mu = nanstdev( data ); +console.log( 'Typed arrays: %d\n', mu ); + + +// ---- +// Matrices (along rows)... +mat = matrix( data, [100,10], 'float64' ); +mu = nanstdev( mat, { + 'dim': 1 +}); +console.log( 'Matrix (rows): %s\n', mu.toString() ); + + +// ---- +// Matrices (along columns)... +mu = nanstdev( mat, { + 'dim': 2 +}); +console.log( 'Matrix (columns): %s\n', mu.toString() ); + -console.log( nanstdev( data ) ); \ No newline at end of file +// ---- +// Matrices (custom output data type)... +mu = nanstdev( mat, { + 'dtype': 'uint8' +}); +console.log( 'Matrix (%s): %s\n', mu.dtype, mu.toString() );