From 292e7d535893f6fbacd6cf9dcecc9fa7dea49e54 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Mon, 8 Jun 2015 19:38:28 +0200 Subject: [PATCH 1/8] [UPDATE] dotfiles, Makefile --- .gitattributes | 1 + .jshintignore | 14 ++++++++++++++ .npmignore | 2 ++ .travis.yml | 11 +++++++++-- Makefile | 34 +++++++++++++++++++++++++++------- 5 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 .gitattributes create mode 100644 .jshintignore 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/.npmignore b/.npmignore index cc43218..9db298d 100644 --- a/.npmignore +++ b/.npmignore @@ -13,6 +13,7 @@ examples/ reports/ support/ test/ +benchmark/ # Node.js # ########### @@ -46,5 +47,6 @@ Desktop.ini # Utilities # ############# .jshintrc +.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/Makefile b/Makefile index f5648d1..3234fbb 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/stylish.js + + # FILES # @@ -96,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 # @@ -117,7 +138,6 @@ clean-node: # CLEAN # - .PHONY: clean clean: From 9530a10b3d47b65a2a6cf6a4972928cc8c56d5ca Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Mon, 8 Jun 2015 22:27:13 +0200 Subject: [PATCH 2/8] [UPDATE] implementation --- LICENSE | 4 +- lib/accessor.js | 71 ++++++++++++++++++++ lib/array.js | 58 ++++++++++++++++ lib/ascending.js | 16 +++++ lib/index.js | 151 ++++++++++++++++++++---------------------- lib/matrix.js | 90 +++++++++++++++++++++++++ lib/validate.js | 60 +++++++++++++++++ package.json | 23 ++++--- test/test.accessor.js | 120 +++++++++++++++++++++++++++++++++ test/test.array.js | 75 +++++++++++++++++++++ test/test.js | 138 ++++++++++++++++++++++++++++---------- test/test.matrix.js | 99 +++++++++++++++++++++++++++ test/test.validate.js | 148 +++++++++++++++++++++++++++++++++++++++++ 13 files changed, 927 insertions(+), 126 deletions(-) create mode 100644 lib/accessor.js create mode 100644 lib/array.js create mode 100644 lib/ascending.js create mode 100644 lib/matrix.js create mode 100644 lib/validate.js 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/LICENSE b/LICENSE index 11e54ba..9f32cbf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Rebekah Smith. +Copyright (c) 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/lib/accessor.js b/lib/accessor.js new file mode 100644 index 0000000..e65ce6f --- /dev/null +++ b/lib/accessor.js @@ -0,0 +1,71 @@ +'use strict'; + +// FUNCTIONS // + +var ascending = require( './ascending.js' ); + +// MIDMEAN // + +/** +* FUNCTION: midmean( arr, clbk[, sorted] ) +* Computes the interquartile mean (midmean) of a numeric array. +* +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} arr - input array +* @param {Function} clbk - accessor function for accessing array values +* @param {Boolean} [sorted=false] - boolean flag indicating if the input array is sorted in ascending order +* @returns {Number|Null} midmean or null +*/ +function midmean( arr, clbk, sorted ) { + var len = arr.length, + low, + high, + delta, + values, + mean = 0, + N = 0, + i; + + if ( !len || arr.length < 3 ) { + return null; + } + + // Quartiles sit between values... + if ( len%4 === 0 ) { + low = len*0.25; + high = len*0.75 - 1; + } + else { + low = Math.ceil( len*0.25 ); + high = Math.floor( len*0.75 ) - 1; + } + + if ( !sorted ) { + values = []; + for ( i = 0; i < len; i++ ) { + values.push( clbk( arr[ i ], i ) ); + } + values.sort( ascending ); + + // Compute an arithmetic mean... + for ( i = low; i <= high; i++ ) { + N += 1; + delta = values[ i ] - mean; + mean += delta / N; + } + return mean; + } else { + // Compute an arithmetic mean... + for ( i = low; i <= high; i++ ) { + N += 1; + delta = clbk( arr[ i ], i ) - mean; + mean += delta / N; + } + return mean; + } + +} // end FUNCTION midmean() + + +// EXPORTS // + +module.exports = midmean; diff --git a/lib/array.js b/lib/array.js new file mode 100644 index 0000000..d184182 --- /dev/null +++ b/lib/array.js @@ -0,0 +1,58 @@ +'use strict'; + +// FUNCTIONS // + +var ascending = require( './ascending.js' ); + +// MIDMEAN // + +/** +* FUNCTION: midmean( arr[, sorted] ) +* Computes the interquartile mean (midmean) of a numeric array. +* +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} arr - input array +* @param {Boolean} [sorted=false] - boolean flag indicating if the input array is sorted in ascending order +* @returns {Number|Null} midmean or null +*/ +function midmean( arr, sorted ) { + var len = arr.length, + low, + high, + delta, + mean = 0, + N = 0, + i; + + if ( !len || arr.length < 3) { + return null; + } + + if ( !sorted ) { + arr = arr.slice(); + Array.prototype.sort( arr, ascending ); + } + + // Quartiles sit between values... + if ( len%4 === 0 ) { + low = len*0.25; + high = len*0.75 - 1; + } + else { + low = Math.ceil( len*0.25 ); + high = Math.floor( len*0.75 ) - 1; + } + + // Compute an arithmetic mean... + for ( i = low; i <= high; i++ ) { + N += 1; + delta = arr[ i ] - mean; + mean += delta / N; + } + return mean; + +} // end FUNCTION midmean() + + +// EXPORTS // + +module.exports = midmean; diff --git a/lib/ascending.js b/lib/ascending.js new file mode 100644 index 0000000..d55fca5 --- /dev/null +++ b/lib/ascending.js @@ -0,0 +1,16 @@ +'use strict'; + +/** +* FUNCTION: ascending( a, b ) +* Comparator function used to sort values in ascending order. +* +* @private +* @param {Number} a +* @param {Number} b +* @returns {Number} difference between `a` and `b` +*/ +function ascending( a, b ) { + return a - b; +} // end FUNCTION ascending() + +module.exports = ascending; diff --git a/lib/index.js b/lib/index.js index 79cfd5b..c7ac277 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,97 +1,90 @@ -/** -* -* COMPUTE: midmean -* -* -* DESCRIPTION: -* - Computes the interquartile mean (midmean) of a numeric array. -* -* -* NOTES: -* [1] -* -* -* TODO: -* [1] -* -* -* LICENSE: -* MIT -* -* Copyright (c) 2014. Rebekah Smith. -* -* -* AUTHOR: -* Rebekah Smith. rebekahjs17@gmail.com. 2014. -* -*/ - '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 // -/** -* FUNCTION: ascending( a, b ) -* Comparator function used to sort values in ascending order. -* -* @private -* @param {Number} a -* @param {Number} b -* @returns {Number} difference between `a` and `b` -*/ -function ascending( a, b ) { - return a - b; -} // end FUNCTION ascending() +var midmean1 = require( './array.js' ), + midmean2 = require( './accessor.js' ), + midmean3 = require( './matrix.js' ); // MIDMEAN // /** -* FUNCTION: midmean( arr[, sorted] ) -* Computes the interquartile mean (midmean) of a numeric array. +* FUNCTION: midmean( x[, opts] ) +* Computes the midmean. * -* @param {Array} arr - numeric array -* @param {Boolean} [sorted] - boolean flag indicating if the input array is sorted in ascending order -* @returns {Number} midmean +* @param {Number[]|Array|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Matrix} x - input value +* @param {Object} [opts] - function options +* @param {Function} [opts.accessor] - accessor function for accessing array values +* @param {Boolean} [opts.sorted] - boolean flag indicating if the input array is already sorted in ascending order +* @param {Number} [opts.dim=2] - dimension along which to compute the midmean. +* @param {String} [opts.dtype="float64"] - output data type +* @returns {Number|Matrix|Null} midmean value(s) or null */ -function midmean( arr, sorted ) { - if ( !Array.isArray( arr ) ) { - throw new TypeError( 'midmean()::invalid input argument. Must provide an array.' ); - } - if ( arguments.length > 1 && typeof sorted !== 'boolean' ) { - throw new TypeError( 'midmean()::invalid input argument. Second argument must be a boolean.' ); - } - if ( arr.length < 3 ) { - throw new TypeError( 'midmean()::invalid input argument. Midmean not applicable.' ); - } - if ( !sorted ) { - arr = arr.slice(); - arr.sort( ascending ); - } - var len = arr.length, - mean = 0, - N = 0, - delta, - low, - high; +function midmean( x, options ) { + /* jshint newcap:false */ + var opts = {}, + shape, + ctor, + err, + len, + dim, + dt, + d, + m, + sorted; - // Quartiles sit between values... - if ( len%4 === 0 ) { - low = len*0.25; - high = len*0.75 - 1; - } - else { - low = Math.ceil( len*0.25 ); - high = Math.floor( len*0.75 ) - 1; + if ( arguments.length > 1 ) { + err = validate( opts, options ); + if ( err ) { + throw err; + } } + sorted = opts.sorted || false; + if ( isMatrixLike( x ) ) { + dt = opts.dtype || 'float64'; + dim = opts.dim; - // Compute an arithmetic mean... - for ( var i = low; i <= high; i++ ) { - N += 1; - delta = arr[ i ] - mean; - mean += delta / N; + // Determine if provided a vector... + if ( x.shape[ 0 ] === 1 || x.shape[ 1 ] === 1 ) { + // Treat as an array-like object: + return midmean1( x.data, sorted ); + } + if ( dim > 2 ) { + throw new RangeError( 'midmean()::invalid option. Dimension option exceeds number of matrix dimensions. Option: `' + dim + '`.' ); + } + 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( 'midmean()::invalid option. Data type option does not have a corresponding array constructor. Option: `' + dt + '`.' ); + } + // Create an output matrix and calculate the midmeans: + d = new ctor( len ); + m = matrix( d, shape, dt ); + return midmean3( m, x, sorted, dim ); + } + if ( isArrayLike( x ) ) { + if ( opts.accessor ) { + return midmean2( x, opts.accessor, sorted ); + } + return midmean1( x, sorted ); } - return mean; + throw new TypeError( 'midmean()::invalid input argument. First argument must be either an array or a matrix. Value: `' + x + '`.' ); } // end FUNCTION midmean() diff --git a/lib/matrix.js b/lib/matrix.js new file mode 100644 index 0000000..3bd874c --- /dev/null +++ b/lib/matrix.js @@ -0,0 +1,90 @@ +'use strict'; + +// FUNCTIONS // + +var ascending = require( './ascending.js' ); + +// MIDMEAN // + +/** +* FUNCTION: midmean( out, mat[, sorted[, dim] ] ) +* Computes the midmean along a matrix dimension. +* +* @param {Matrix} out - output matrix +* @param {Matrix} mat - input matrix +* @param {Boolean} [sorted=false] - boolean flag indicating if the rows / columns are already sorted in ascending order +* @param {Number} [dim=2] - matrix dimension along which to compute the maximum. If `dim=1`, compute along matrix rows. If `dim=2`, compute along matrix columns. +* @returns {Matrix|Null} midmean or null +*/ +function midmean( out, mat, sorted, dim ) { + var values, + delta, + mean, Nobs, + M, N, o, + s0, s1, + i, j, k, + low, high; + + 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; + + // Quartiles sit between values... + if ( N%4 === 0 ) { + low = N*0.25; + high = N*0.75 - 1; + } + else { + low = Math.ceil( N*0.25 ); + high = Math.floor( N*0.75 ) - 1; + } + if ( sorted ) { + for ( i = 0; i < M; i++ ) { + mean = 0; + Nobs = 0; + k = o + i*s0; + // Compute an arithmetic mean... + for ( j = low; j <= high; j++ ) { + N += 1; + delta = mat.data[ k + j*s1 ] - mean; + mean += delta / Nobs; + } + out.data[ i ] = mean; + } + } else { + for ( i = 0; i < M; i++ ) { + mean = 0; + Nobs = 0; + k = o + i*s0; + values = mat.data.slice( k, k + (N-1)*s1 ); + Array.prototype.sort( values, ascending ); + // Compute an arithmetic mean... + for ( j = low; j <= high; j++ ) { + Nobs += 1; + delta = values[ j ] - mean; + mean += delta / Nobs; + } + out.data[ i ] = mean; + } + } + return out; +} // end FUNCTION midmean() + + +// EXPORTS // + +module.exports = midmean; diff --git a/lib/validate.js b/lib/validate.js new file mode 100644 index 0000000..7ceb281 --- /dev/null +++ b/lib/validate.js @@ -0,0 +1,60 @@ +'use strict'; + +// MODULES // + +var isObject = require( 'validate.io-object' ), + isBoolean = require( 'validate.io-boolean-primitive'), + isFunction = require( 'validate.io-function' ), + isString = require( 'validate.io-string-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.sorted] - boolean flag indicating if the input array is already sorted in ascending order +* @returns {Null|Error} null or an error +*/ +function validate( opts, options ) { + if ( !isObject( options ) ) { + return new TypeError( 'midmean()::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( 'midmean()::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( 'midmean()::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( 'midmean()::invalid option. Data type option must be a string primitive. Option: `' + opts.dtype + '`.' ); + } + } + if ( options.hasOwnProperty( 'sorted' ) ) { + opts.sorted = options.sorted; + if ( !isBoolean( opts.sorted ) ) { + return new TypeError( 'midmean()::invalid option. Sorted option must be a boolean primitive. Option: `' + opts.sorted + '`.' ); + } + } + return null; +} // end FUNCTION validate() + + +// EXPORTS // + +module.exports = validate; diff --git a/package.json b/package.json index f755267..5f6dfb6 100644 --- a/package.json +++ b/package.json @@ -46,17 +46,22 @@ "bugs": { "url": "https://github.com/compute-io/midmean/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.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" }, - "licenses": [ - { - "type": "MIT", - "url": "http://www.opensource.org/licenses/MIT" - } - ] + "license": "MIT" } diff --git a/test/test.accessor.js b/test/test.accessor.js new file mode 100644 index 0000000..b3b882b --- /dev/null +++ b/test/test.accessor.js @@ -0,0 +1,120 @@ +/* global describe, it, require */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Module to be tested: + midmean = require( './../lib/accessor.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'accessor midmean', function tests() { + + it( 'should export a function', function test() { + expect( midmean ).to.be.a( 'function' ); + }); + + it( 'should compute the interquartile mean (midmean) for an array length divisible by 4', function test() { + var data, expected; + + data = [ + {'x':3}, + {'x':7}, + {'x':1}, + {'x':34}, + {'x':8}, + {'x':9}, + {'x':3}, + {'x':5}, + {'x':7}, + {'x':45}, + {'x':6}, + {'x':2} + ]; + + expected = 6; + + // Unsorted test: + assert.strictEqual( midmean( data, getValue ), expected ); + + // Sort the data: + data.sort( function sort( a, b ) { + return a.x - b.x; + }); + + // Sorted test: + assert.strictEqual( midmean( data, getValue, true ), expected ); + + function getValue( d ) { + return d.x; + } + }); + + it( 'should compute the interquartile mean (midmean) for an array length not divisible by 4', function test() { + var data, expected; + + data = [ + {'x':3}, + {'x':7}, + {'x':1}, + {'x':34}, + {'x':8}, + {'x':9}, + {'x':3}, + {'x':5}, + {'x':7}, + {'x':45}, + {'x':8} + ]; + + expected = 7; + + // Unsorted test: + assert.strictEqual( midmean( data, getValue ), expected ); + + // Sort the data: + data.sort( function sort( a, b ) { + return a.x - b.x; + }); + + // Sorted test: + assert.strictEqual( midmean( data, getValue, true ), expected ); + + function getValue( d ) { + return d.x; + } + }); + + it( 'should return null if provided an empty array', function test() { + assert.isNull( midmean( [], getValue ) ); + + function getValue( d ) { + return d.x; + } + }); + + it( 'should return null if provided an array of insufficient length', function test() { + var data = [ + {'x': 2}, + {'x': 5} + ]; + + assert.isNull( midmean( data, 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..35a137e --- /dev/null +++ b/test/test.array.js @@ -0,0 +1,75 @@ +/* global describe, it, require */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Module to be tested: + midmean = require( './../lib/array.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'array midmean', function tests() { + + it( 'should export a function', function test() { + expect( midmean ).to.be.a( 'function' ); + }); + + + it( 'should compute the interquartile mean (midmean) for an array length divisible by 4', function test() { + var data, expected; + + data = [ 3, 7, 1, 34, 8, 9, 3, 5, 7, 45, 6, 2 ]; + expected = 6; + + // Unsorted test: + assert.strictEqual( midmean( data ), expected ); + + // Sort the data: + data.sort( function sort( a, b ) { + return a - b; + }); + + // Sorted test: + assert.strictEqual( midmean( data, true ), expected ); + }); + + it( 'should compute the interquartile mean (midmean) for an array length not divisible by 4', function test() { + var data, expected; + + data = [ 3, 7, 1, 34, 8, 9, 3, 5, 7, 45, 8 ]; + expected = 7; + + // Unsorted test: + assert.strictEqual( midmean( data ), expected ); + + // Sort the data: + data.sort( function sort( a, b ) { + return a - b; + }); + + // Sorted test: + assert.strictEqual( midmean( data, true ), expected ); + }); + + it( 'should return null if provided an empty array', function test() { + assert.isNull( midmean( [] ) ); + }); + + it( 'should return null if provided an array of insufficient length', function test() { + var data = [2, 5]; + + assert.isNull( midmean( data ) ); + + }); + +}); diff --git a/test/test.js b/test/test.js index a370906..b611d32 100644 --- a/test/test.js +++ b/test/test.js @@ -1,3 +1,4 @@ +/* global describe, it, require */ 'use strict'; // MODULES // @@ -5,6 +6,9 @@ var // Expectation library: chai = require( 'chai' ), + // Matrix data structure: + matrix = require( 'dstructs-matrix' ), + // Module to be tested: midmean = require( './../lib' ); @@ -23,9 +27,9 @@ describe( 'compute-midmean', function tests() { expect( midmean ).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', // valid as is array-like (length) 5, true, undefined, @@ -38,84 +42,146 @@ describe( 'compute-midmean', function tests() { for ( var i = 0; i < values.length; i++ ) { expect( badValue( values[i] ) ).to.throw( TypeError ); } - function badValue( value ) { return function() { - midmean( value, true ); + midmean( value ); }; } }); - it( 'should throw an error if provided a non-boolean for the second argument', function test() { + it( 'should throw an error if provided a dimension which is greater than 2 when provided a matrix', function test() { var values = [ '5', 5, - [], + true, undefined, null, NaN, - function(){}, - {} + [], + {}, + function(){} ]; for ( var i = 0; i < values.length; i++ ) { - expect( badValue( values[i] ) ).to.throw( TypeError ); + expect( badValue( values[i] ) ).to.throw( Error ); } - function badValue( value ) { return function() { - midmean( [], value ); + midmean( matrix( [2,2] ), { + 'dim': value + }); }; } }); - it( 'should throw an error if provided an array of insufficient length', function test() { - var data = [2, 5]; + it( 'should throw an error if provided an unrecognized/unsupported data type option', function test() { + var values = [ + 'beep', + 'boop' + ]; - function badValue( array ) { + for ( var i = 0; i < values.length; i++ ) { + expect( badValue( values[i] ) ).to.throw( Error ); + } + function badValue( value ) { return function() { - midmean( array ); + midmean( matrix( [2,2] ), { + 'dtype': value + }); }; } + }); + + it( 'should compute the midmean', function test() { + var data, expected; - expect( badValue( data ) ).to.throw( TypeError ); + data = [ 2, 4, 5, 3, 8, 2 ]; + expected = 3.5; + assert.strictEqual( midmean( data ), expected ); }); - it( 'should compute the interquartile mean (midmean) for an array length divisible by 4', function test() { + it( 'should compute the midmean of a typed array', function test() { var data, expected; - data = [ 3, 7, 1, 34, 8, 9, 3, 5, 7, 45, 6, 2 ]; - expected = 6; + data = new Int8Array( [ 2, 4, 5, 3, 8, 2 ] ); + expected = 3.5; - // Unsorted test: assert.strictEqual( midmean( data ), expected ); + }); + + it( 'should compute the midmean using an accessor function', function test() { + var data, expected, actual; - // Sort the data: - data.sort( function sort( a, b ) { - return a - b; + data = [ + {'x':2}, + {'x':4}, + {'x':5}, + {'x':3}, + {'x':8}, + {'x':2} + ]; + + expected = 3.5; + actual = midmean( data, { + 'accessor': getValue }); - // Sorted test: - assert.strictEqual( midmean( data, true ), expected ); + assert.strictEqual( actual, expected ); + + function getValue( d ) { + return d.x; + } }); - it( 'should compute the interquartile mean (midmean) for an array length not divisible by 4', function test() { - var data, expected; + it( 'should compute the midmean along a matrix dimension', function test() { + var expected, + data, + mat, + mu, + i; - data = [ 3, 7, 1, 34, 8, 9, 3, 5, 7, 45, 8 ]; - expected = 7; + data = new Int8Array( 25 ); + for ( i = 0; i < data.length; i++ ) { + data[ i ] = i; + } + mat = matrix( data, [5,5], 'int8' ); - // Unsorted test: - assert.strictEqual( midmean( data ), expected ); + // Default: + mu = midmean( mat ); + expected = '2;7;12;17;22'; - // Sort the data: - data.sort( function sort( a, b ) { - return a - b; + assert.strictEqual( mu.toString(), expected, 'default' ); + + // Along columns: + mu = midmean( mat, { + 'dim': 2 }); + expected = '2;7;12;17;22'; + + assert.strictEqual( mu.toString(), expected, 'dim: 2' ); + + // Along rows: + mu = midmean( mat, { + 'dim': 1 + }); + expected = '10,11,12,13,14'; + + assert.strictEqual( mu.toString(), expected, 'dim: 1' ); + }); + + it( 'should compute the midmean of 1d matrices (vectors)', function test() { + var data, mat; + + data = [ 2, 4, 5, 3, 8, 2 ]; + + // Row vector: + mat = matrix( data, [1,6], 'int8' ); + assert.strictEqual( midmean( mat ), 4 ); - // Sorted test: - assert.strictEqual( midmean( data, true ), expected ); + // Column vector: + mat = matrix( data, [6,1], 'int8' ); + assert.strictEqual( midmean( mat ), 4 ); }); }); diff --git a/test/test.matrix.js b/test/test.matrix.js new file mode 100644 index 0000000..f8f4a80 --- /dev/null +++ b/test/test.matrix.js @@ -0,0 +1,99 @@ +/* global describe, it, require */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Matrix data structure: + matrix = require( 'dstructs-matrix' ), + + // Module to be tested: + midmean = require( './../lib/matrix.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'matrix midmean', function tests() { + + var data, + mat, + i; + + data = new Int32Array( 25 ); + for ( i = 0; i < data.length; i++ ) { + data[ i ] = i + 1; + } + mat = matrix( data, [5,5], 'int8' ); + + + it( 'should export a function', function test() { + expect( midmean ).to.be.a( 'function' ); + }); + + it( 'should compute the midmean along matrix columns', function test() { + var out, p, expected; + + out = matrix( [5,1], 'float64' ); + + p = midmean( out, mat ); + expected = '3;8;13;18;23'; + + assert.strictEqual( p.toString(), expected ); + + p = midmean( out, mat, false, 2 ); + expected = '3;8;13;18;23'; + + assert.strictEqual( p.toString(), expected ); + }); + + it( 'should compute the midmean along matrix columns for already sorted rows', function test() { + var out, p, expected; + + out = matrix( [5,1], 'float64' ); + + p = midmean( out, mat, true ); + expected = '3;8;13;18;23'; + + assert.strictEqual( p.toString(), expected ); + + p = midmean( out, mat, true, 2 ); + expected = '3;8;13;18;23'; + + assert.strictEqual( p.toString(), expected ); + }); + + it( 'should compute the midmean along matrix rows', function test() { + var out, p, expected; + + out = matrix( [1,5], 'float64' ); + + p = midmean( out, mat, false, 1 ); + expected = '11,12,13,14,15'; + + 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( midmean( out, mat ) ); + + mat = matrix( [10,0] ); + assert.isNull( midmean( out, mat ) ); + + mat = matrix( [0,0] ); + assert.isNull( midmean( out, mat ) ); + }); + +}); diff --git a/test/test.validate.js b/test/test.validate.js new file mode 100644 index 0000000..f17a902 --- /dev/null +++ b/test/test.validate.js @@ -0,0 +1,148 @@ +/* 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 return an error if provided a sorted 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( {}, { + 'sorted': 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' + }); + + assert.isNull( err ); + }); + +}); From 86c9a6b380324e2d4e74d74e19fed91df0aa460e Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Tue, 9 Jun 2015 11:26:49 +0200 Subject: [PATCH 3/8] [UPDATE] finished implementation, full code coverage --- README.md | 11 ++++--- examples/index.js | 69 +++++++++++++++++++++++++++++++++++++++---- lib/accessor.js | 2 +- lib/array.js | 5 ++-- lib/matrix.js | 14 +++++++-- test/test.js | 5 ++-- test/test.matrix.js | 58 +++++++++++++++++++++++++++--------- test/test.validate.js | 3 +- 8 files changed, 132 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 832b4b3..899971e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Midmean === [![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 [interquartile mean](http://www.jstor.org/stable/1268431) (midmean) of a numeric array. +> Computes the [interquartile mean](http://www.jstor.org/stable/1268431) (midmean). ## Installation @@ -16,7 +16,6 @@ For use in the browser, use [browserify](https://github.com/substack/node-browse ## Usage -To use the module, ``` javascript var midmean = require( 'compute-midmean' ); @@ -76,7 +75,7 @@ The midmean includes the values located between *but not including* the first an ### 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,15 +99,15 @@ $ make view-cov ``` +--- ## License -[MIT license](http://opensource.org/licenses/MIT). +[MIT license](http://opensource.org/licenses/MIT). ---- ## Copyright -Copyright © 2014. Rebekah Smith. +Copyright © 2014-2015. The [Compute.io](https://github.com/compute-io) Authors. [npm-image]: http://img.shields.io/npm/v/compute-midmean.svg diff --git a/examples/index.js b/examples/index.js index d311bf4..bbdbb2f 100644 --- a/examples/index.js +++ b/examples/index.js @@ -1,11 +1,70 @@ 'use strict'; -var midmean = require( './../lib' ); +var matrix = require( 'dstructs-matrix' ), + midmean = require( './../lib' ); -var data = new Array( 100 ); +var data, + mat, + mu, + i; -for ( var i = 0; i < data.length; i++ ) { - data[i] = Math.round( Math.random()*100 ); + +// ---- +// Plain arrays... +data = new Array( 1000 ); +for ( i = 0; i < data.length; i++ ) { + data[ i ] = Math.random() * 100; +} +mu = midmean( 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 = midmean( data, { + 'accessor': getValue +}); +console.log( 'Accessors: %d\n', mu ); + + +// ---- +// Typed arrays... +data = new Int32Array( 1000 ); +for ( i = 0; i < data.length; i++ ) { + data[ i ] = Math.random() * 100; +} +mu = midmean( data ); +console.log( 'Typed arrays: %d\n', mu ); + + +// ---- +// Matrices (along rows)... +mat = matrix( data, [100,10], 'int32' ); +mu = midmean( mat, { + 'dim': 1 +}); +console.log( 'Matrix (rows): %s\n', mu.toString() ); + + +// ---- +// Matrices (along columns)... +mu = midmean( mat, { + 'dim': 2 +}); +console.log( 'Matrix (columns): %s\n', mu.toString() ); + -console.log( midmean( data ) ); +// ---- +// Matrices (custom output data type)... +mu = midmean( mat, { + 'dtype': 'uint8' +}); +console.log( 'Matrix (%s): %s\n', mu.dtype, mu.toString() ); diff --git a/lib/accessor.js b/lib/accessor.js index e65ce6f..1e9b2e3 100644 --- a/lib/accessor.js +++ b/lib/accessor.js @@ -25,7 +25,7 @@ function midmean( arr, clbk, sorted ) { N = 0, i; - if ( !len || arr.length < 3 ) { + if ( len < 3 ) { return null; } diff --git a/lib/array.js b/lib/array.js index d184182..82a56fa 100644 --- a/lib/array.js +++ b/lib/array.js @@ -23,13 +23,14 @@ function midmean( arr, sorted ) { N = 0, i; - if ( !len || arr.length < 3) { + if ( len < 3) { return null; } if ( !sorted ) { arr = arr.slice(); - Array.prototype.sort( arr, ascending ); + // Borrow prototype method as typed arrays do not have sort + Array.prototype.sort.call( arr, ascending ); } // Quartiles sit between values... diff --git a/lib/matrix.js b/lib/matrix.js index 3bd874c..259dc15 100644 --- a/lib/matrix.js +++ b/lib/matrix.js @@ -41,6 +41,11 @@ function midmean( out, mat, sorted, dim ) { if ( M === 0 || N === 0 ) { return null; } + if ( N < 3 ) { + return null; + } + + // matrix offset o = mat.offset; // Quartiles sit between values... @@ -59,7 +64,7 @@ function midmean( out, mat, sorted, dim ) { k = o + i*s0; // Compute an arithmetic mean... for ( j = low; j <= high; j++ ) { - N += 1; + Nobs += 1; delta = mat.data[ k + j*s1 ] - mean; mean += delta / Nobs; } @@ -70,8 +75,11 @@ function midmean( out, mat, sorted, dim ) { mean = 0; Nobs = 0; k = o + i*s0; - values = mat.data.slice( k, k + (N-1)*s1 ); - Array.prototype.sort( values, ascending ); + values = []; + for ( j = 0; j < N; j++ ) { + values.push( mat.data[ k + j*s1 ] ); + } + values.sort( ascending ); // Compute an arithmetic mean... for ( j = low; j <= high; j++ ) { Nobs += 1; diff --git a/test/test.js b/test/test.js index b611d32..cb5eb9e 100644 --- a/test/test.js +++ b/test/test.js @@ -112,7 +112,6 @@ describe( 'compute-midmean', function tests() { it( 'should compute the midmean using an accessor function', function test() { var data, expected, actual; - data = [ {'x':2}, {'x':4}, @@ -177,11 +176,11 @@ describe( 'compute-midmean', function tests() { // Row vector: mat = matrix( data, [1,6], 'int8' ); - assert.strictEqual( midmean( mat ), 4 ); + assert.strictEqual( midmean( mat ), 3.5 ); // Column vector: mat = matrix( data, [6,1], 'int8' ); - assert.strictEqual( midmean( mat ), 4 ); + assert.strictEqual( midmean( mat ), 3.5 ); }); }); diff --git a/test/test.matrix.js b/test/test.matrix.js index f8f4a80..09649ef 100644 --- a/test/test.matrix.js +++ b/test/test.matrix.js @@ -25,6 +25,7 @@ describe( 'matrix midmean', function tests() { var data, mat, + mat2, i; data = new Int32Array( 25 ); @@ -32,53 +33,69 @@ describe( 'matrix midmean', function tests() { data[ i ] = i + 1; } mat = matrix( data, [5,5], 'int8' ); - + mat2 = mat.mget( null, [ 0, 1, 2, 3 ] ); it( 'should export a function', function test() { expect( midmean ).to.be.a( 'function' ); }); it( 'should compute the midmean along matrix columns', function test() { - var out, p, expected; + var out, midm, expected; out = matrix( [5,1], 'float64' ); - p = midmean( out, mat ); + midm = midmean( out, mat ); expected = '3;8;13;18;23'; - assert.strictEqual( p.toString(), expected ); + assert.strictEqual( midm.toString(), expected ); - p = midmean( out, mat, false, 2 ); + midm = midmean( out, mat, false, 2 ); expected = '3;8;13;18;23'; - assert.strictEqual( p.toString(), expected ); + assert.strictEqual( midm.toString(), expected ); + }); + + it( 'should compute the midmean along matrix columns with row length divisible by 4', function test() { + var out, midm, expected; + + out = matrix( [5,1], 'float64' ); + + midm = midmean( out, mat2 ); + expected = '2.5;7.5;12.5;17.5;22.5'; + + assert.strictEqual( midm.toString(), expected ); + + midm = midmean( out, mat2, false, 2 ); + expected = '2.5;7.5;12.5;17.5;22.5'; + + assert.strictEqual( midm.toString(), expected ); }); it( 'should compute the midmean along matrix columns for already sorted rows', function test() { - var out, p, expected; + var out, midm, expected; out = matrix( [5,1], 'float64' ); - p = midmean( out, mat, true ); + midm = midmean( out, mat, true ); expected = '3;8;13;18;23'; - assert.strictEqual( p.toString(), expected ); + assert.strictEqual( midm.toString(), expected ); - p = midmean( out, mat, true, 2 ); + midm = midmean( out, mat, true, 2 ); expected = '3;8;13;18;23'; - assert.strictEqual( p.toString(), expected ); + assert.strictEqual( midm.toString(), expected ); }); it( 'should compute the midmean along matrix rows', function test() { - var out, p, expected; + var out, midm, expected; out = matrix( [1,5], 'float64' ); - p = midmean( out, mat, false, 1 ); + midm = midmean( out, mat, false, 1 ); expected = '11,12,13,14,15'; - assert.strictEqual( p.toString(), expected ); + assert.strictEqual( midm.toString(), expected ); }); it( 'should return null if provided a matrix having one or more zero dimensions', function test() { @@ -96,4 +113,17 @@ describe( 'matrix midmean', function tests() { assert.isNull( midmean( out, mat ) ); }); + it( 'should return null if provided a matrix with less than three elements on dimension for which to calculate the midmean' , function test() { + var out, mat; + + out = matrix( [0,0] ); + + mat = matrix( [ 2, 10 ] ); + assert.isNull( midmean( out, mat, false, 1 ) ); + + mat = matrix( [ 10, 2 ] ); + assert.isNull( midmean( out, mat, false, 2 ) ); + + }); + }); diff --git a/test/test.validate.js b/test/test.validate.js index f17a902..b3b20e0 100644 --- a/test/test.validate.js +++ b/test/test.validate.js @@ -139,7 +139,8 @@ describe( 'validate', function tests() { err = validate( {}, { 'accessor': function getValue(){}, 'dim': 2, - 'dtype': 'int32' + 'dtype': 'int32', + 'sorted': true }); assert.isNull( err ); From 913e04449870616aec6cce6af9e837aae36c9b78 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Tue, 9 Jun 2015 14:02:39 +0200 Subject: [PATCH 4/8] [UPDATE] README, package.json --- Makefile | 2 +- README.md | 207 +++++++++++++++++++++++++++++++++++++++++++++++---- package.json | 12 ++- 3 files changed, 203 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 3234fbb..b49940a 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ ISTANBUL_HTML_REPORT_PATH ?= $(ISTANBUL_OUT)/lcov-report/index.html # JSHINT # JSHINT ?= ./node_modules/.bin/jshint -JSHINT_REPORTER ?= ./node_modules/jshint-stylish/stylish.js +JSHINT_REPORTER ?= ./node_modules/jshint-stylish/ diff --git a/README.md b/README.md index 899971e..e40944e 100644 --- a/README.md +++ b/README.md @@ -21,43 +21,145 @@ For use in the browser, use [browserify](https://github.com/substack/node-browse var midmean = require( 'compute-midmean' ); ``` -#### midmean( arr[, sorted] ) +#### midmean( x[, opts] ) -Computes the [midmean](http://www.jstor.org/stable/1268431) of a numeric `array`. +Computes the [midmean](http://www.jstor.org/stable/1268431). `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). ``` javascript var unsorted = [ 5, 6, 7, 2, 1, 8, 4, 3 ]; - var mean = midmean( unsorted ); // returns 4.5 ``` -If the input `array` is already `sorted` in __ascending__ order, set the optional second argument to `true`. +If the input `array` is already `sorted` in __ascending__ order, set the `sorted` option to `true`. ``` javascript var sorted = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; -var mean = midmean( sorted, true ); +var mean = midmean( sorted, { + 'sorted': true +}); // returns 4.5 ``` - -## Examples +For non-numeric `arrays`, provide an accessor `function` for accessing `array` values. ``` javascript -var data = new Array( 100 ); +var data = [ + {'x':2}, + {'x':4}, + {'x':5}, + {'x':3}, + {'x':8}, + {'x':2} +]; + +function getValue( d, i ) { + return d.x; +} + +var mu = mean( data, { + 'accessor': getValue +}); +// returns 3.5 +``` + +If provided a [`matrix`](https://github.com/dstructs/matrix), the function accepts the following `options`: + +* __dim__: dimension along which to compute the [midmean](http://www.jstor.org/stable/1268431). Default: `2` (along the columns). +* __dtype__: output [`matrix`](https://github.com/dstructs/matrix) data type. Default: `float64`. -for ( var i = 0; i < data.length; i++ ) { - data[ i ] = Math.round( Math.random()*100 ); +By default, the function computes the [midmean](http://www.jstor.org/stable/1268431) along the columns (`dim=2`). + +``` javascript +var matrix = require( 'dstructs-matrix' ), + data, + mat, + mu, + 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 ] +*/ + +mu = midmean( mat ); +/* + [ 2 + 7 + 12 + 17 + 22 ] +*/ +``` + +To compute the [midmean](http://www.jstor.org/stable/1268431) along the rows, set the `dim` option to `1`. -console.log( midmean( data ) ); +``` javascript +mu = mean( mat, { + 'dim': 1 +}); +/* + [ 10, 11, 12, 13, 14 ] +*/ ``` -To run the example code from the top-level application directory, +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. -``` bash -$ node ./examples/index.js +``` javascript +mu = midmean( mat, { + 'dim': 1, + 'dtype': 'uint8' +}); +/* + [ 10, 11, 12, 13, 14 ] +*/ + +var dtype = mu.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 = [ 2, 4, 5, 3, 8, 2 ]; + +// Row vector: +mat = matrix( new Int8Array( data ), [1,6], 'int8' ); +mu = midmean( mat ); +// returns 3.5 + +// Column vector: +mat = matrix( new Int8Array( data ), [6,1], 'int8' ); +mu = midmean( mat ); +// returns 3.5 +``` + +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 +mu = midmean( [] ); +// returns null + +mu = midmean( new Int8Array( [] ) ); +// returns null + +mu = midmean( matrix( [0,0] ) ); +// returns null + +mu = midmean( matrix( [0,10] ) ); +// returns null + +mu = midmean( matrix( [10,0] ) ); +// returns null ``` ## Notes @@ -69,7 +171,84 @@ The midmean includes the values located between *but not including* the first an * [1,2,__3,4,5,6__,7,8] -> midmean: 4.5 * [1,2,__3,4,5__,6,7] -> midmean: 4 +## Examples + +``` javascript +var matrix = require( 'dstructs-matrix' ), + midmean = require( 'compute-midmean' ); + +var data, + mat, + mu, + i; + + +// ---- +// Plain arrays... +data = new Array( 1000 ); +for ( i = 0; i < data.length; i++ ) { + data[ i ] = Math.random() * 100; +} +mu = midmean( 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 = midmean( data, { + 'accessor': getValue +}); +console.log( 'Accessors: %d\n', mu ); + +// ---- +// Typed arrays... +data = new Int32Array( 1000 ); +for ( i = 0; i < data.length; i++ ) { + data[ i ] = Math.random() * 100; +} +mu = midmean( data ); +console.log( 'Typed arrays: %d\n', mu ); + + +// ---- +// Matrices (along rows)... +mat = matrix( data, [100,10], 'int32' ); +mu = midmean( mat, { + 'dim': 1 +}); +console.log( 'Matrix (rows): %s\n', mu.toString() ); + + +// ---- +// Matrices (along columns)... +mu = midmean( mat, { + 'dim': 2 +}); +console.log( 'Matrix (columns): %s\n', mu.toString() ); + + +// ---- +// Matrices (custom output data type)... +mu = midmean( mat, { + 'dtype': 'uint8' +}); +console.log( 'Matrix (%s): %s\n', mu.dtype, mu.toString() ); +``` + +To run the example code from the top-level application directory, + +``` bash +$ node ./examples/index.js +``` ## Tests diff --git a/package.json b/package.json index 5f6dfb6..ef84db9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "compute-midmean", "version": "1.0.3", - "description": "Computes the interquartile mean (midmean) of a numeric array.", + "description": "Computes the interquartile mean (midmean).", "author": { "name": "Rebekah Smith", "email": "rebekahjs17@gmail.com" @@ -14,6 +14,10 @@ { "name": "Athan Reines", "email": "kgryte@gmail.com" + }, + { + "name": "Philipp Burckhardt", + "email": "pburckhardt@outlook.com" } ], "scripts": { @@ -59,9 +63,11 @@ }, "devDependencies": { "chai": "3.x.x", - "mocha": "2.x.x", "coveralls": "^2.11.1", - "istanbul": "^0.3.0" + "istanbul": "^0.3.0", + "jshint": "^2.8.0", + "jshint-stylish": "^2.0.0", + "mocha": "2.x.x" }, "license": "MIT" } From 8fdc65f52e775def5be5101fc687aa744a48a1e6 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Tue, 9 Jun 2015 14:09:46 +0200 Subject: [PATCH 5/8] [FIX] borrow slice from prototype --- lib/array.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/array.js b/lib/array.js index 82a56fa..322679d 100644 --- a/lib/array.js +++ b/lib/array.js @@ -28,8 +28,8 @@ function midmean( arr, sorted ) { } if ( !sorted ) { - arr = arr.slice(); - // Borrow prototype method as typed arrays do not have sort + arr = Array.prototype.slice.call( arr ); + // Borrow prototype method as typed arrays do not have sort Array.prototype.sort.call( arr, ascending ); } From 1efcb022d89fd2926b5ec13cde3e4851e492b24e Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 11 Jun 2015 12:16:57 +0200 Subject: [PATCH 6/8] [FIX] accessor.js doc --- lib/accessor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/accessor.js b/lib/accessor.js index 1e9b2e3..d689aff 100644 --- a/lib/accessor.js +++ b/lib/accessor.js @@ -8,9 +8,9 @@ var ascending = require( './ascending.js' ); /** * FUNCTION: midmean( arr, clbk[, sorted] ) -* Computes the interquartile mean (midmean) of a numeric array. +* Computes the interquartile mean (midmean) of an array using an accessor. * -* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} arr - input array +* @param {Array} arr - input array * @param {Function} clbk - accessor function for accessing array values * @param {Boolean} [sorted=false] - boolean flag indicating if the input array is sorted in ascending order * @returns {Number|Null} midmean or null From ee0b983ce53db1791516536ecb2c85961cdc9965 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 11 Jun 2015 15:03:01 +0200 Subject: [PATCH 7/8] [FIX] README --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e40944e..8d705f5 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,14 @@ Computes the [midmean](http://www.jstor.org/stable/1268431). `x` may be either a var unsorted = [ 5, 6, 7, 2, 1, 8, 4, 3 ]; var mean = midmean( unsorted ); // returns 4.5 + +unsorted = new Int8Array( unsorted ); +mean = midmean( unsorted ); +// returns 4.5 ``` +Note: the input array must contain 3 or more elements, otherwise the function returns `null`. + If the input `array` is already `sorted` in __ascending__ order, set the `sorted` option to `true`. ``` javascript @@ -58,7 +64,7 @@ function getValue( d, i ) { return d.x; } -var mu = mean( data, { +var mu = midmean( data, { 'accessor': getValue }); // returns 3.5 @@ -104,7 +110,7 @@ mu = midmean( mat ); To compute the [midmean](http://www.jstor.org/stable/1268431) along the rows, set the `dim` option to `1`. ``` javascript -mu = mean( mat, { +mu = midmean( mat, { 'dim': 1 }); /* @@ -190,7 +196,6 @@ for ( i = 0; i < data.length; i++ ) { data[ i ] = Math.random() * 100; } mu = midmean( data ); -console.log( 'Arrays: %d\n', mu ); // ---- @@ -206,7 +211,6 @@ for ( i = 0; i < data.length; i++ ) { mu = midmean( data, { 'accessor': getValue }); -console.log( 'Accessors: %d\n', mu ); // ---- @@ -216,7 +220,6 @@ for ( i = 0; i < data.length; i++ ) { data[ i ] = Math.random() * 100; } mu = midmean( data ); -console.log( 'Typed arrays: %d\n', mu ); // ---- @@ -225,7 +228,6 @@ mat = matrix( data, [100,10], 'int32' ); mu = midmean( mat, { 'dim': 1 }); -console.log( 'Matrix (rows): %s\n', mu.toString() ); // ---- @@ -233,7 +235,6 @@ console.log( 'Matrix (rows): %s\n', mu.toString() ); mu = midmean( mat, { 'dim': 2 }); -console.log( 'Matrix (columns): %s\n', mu.toString() ); // ---- @@ -241,7 +242,6 @@ console.log( 'Matrix (columns): %s\n', mu.toString() ); mu = midmean( mat, { 'dtype': 'uint8' }); -console.log( 'Matrix (%s): %s\n', mu.dtype, mu.toString() ); ``` To run the example code from the top-level application directory, From 54ceaba09e6386fd175fcd2d0085fbc42ae0334e Mon Sep 17 00:00:00 2001 From: Athan Date: Tue, 1 Jan 2019 10:39:23 -0800 Subject: [PATCH 8/8] Update versions --- .travis.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 29cff27..f50862c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,8 @@ language: node_js node_js: - - '0.12' - - '0.11' - - '0.10' - - '0.8' - - 'iojs' + - '10' + - '8' + - '6' before_install: - npm update -g npm after_script: