Skip to content

Commit

Permalink
[UPDATE] lib, tests, examples.
Browse files Browse the repository at this point in the history
  • Loading branch information
kgryte committed Dec 9, 2014
1 parent 255bf75 commit 6f692a4
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 17 deletions.
61 changes: 57 additions & 4 deletions README.md
Expand Up @@ -19,18 +19,71 @@ For use in the browser, use [browserify](https://github.com/substack/node-browse
To use the module,

``` javascript
var foo = require( 'compute-incrmstdev' );
var incrmstdev = require( 'compute-incrmstdev' );
```

#### foo( arr )
#### incrmstdev( window )

Returns an initialized method to compute a moving sample standard deviation incrementally. `window` sets the window size, i.e., the number of values over which to compute a moving sample standard deviation.

``` javascript
var mstdev = incrmstdev( 3 );
```

#### mstdev( [value] )

If provided a `value`, the method updates and returns the sample standard deviation of the current window. If not provided a `value`, the method returns the current sample standard deviation.

``` javascript
var sigma;

// Filling window...
sigma = mstdev( 2 );
// stdev is 0

mstdev( 4 );
// stdev is ~1.414

mstdev( 0 );
// stdev is 2

// Window starts sliding...
mstdev( -2 );
// stdev is ~3.082

mstdev( -1 );
// mstdev is 1

sigma = mstdev();
// returns 1
```


## Notes

1. If values have not yet been provided to `mstdev`, `mstdev` returns `null`.
1. The first `W-1` returned sample standard deviations will have less statistical support than subsequent sample standard deviations, as `W` values are needed to fill the window buffer. Until the window is full, the sample standard deviation returned equals the [sample standard deviation](https://github.com/compute-io/stdev) of all values provided thus far.

The use case for this module differs from the conventional [vector](https://github.com/compute-io/mstdev) implementation and the [stream](https://github.com/flow-io/) implementation. Namely, this module decouples the act of updating the moving sample standard deviation from the act of consuming the moving sample standard deviation.

What does this function do?


## Examples

``` javascript
var foo = require( 'compute-incrmstdev' );
var incrmstdev = require( 'compute-incrmstdev' );

// Initialize a method to calculate the moving sample standard deviation incrementally:
var mstdev = incrmstdev( 5 ),
sigma;

// Simulate some data...
for ( var i = 0; i < 1000; i++ ) {
sigma = mstdev( Math.random()*100 );
console.log( sigma );
}
sigma = mstdev();
console.log( sigma );
```

To run the example code from the top-level application directory,
Expand Down
18 changes: 17 additions & 1 deletion examples/index.js
@@ -1,3 +1,19 @@
'use strict';

var module = require( './../lib' );
var incrmstdev = require( './../lib' );

// Initialize a method to calculate the moving sample standard deviation incrementally:
var mstdev = incrmstdev( 5 );

// Simulate some data...
var value, sigma;

console.log( '\nValue\tSample StDev\n' );

for ( var i = 0; i < 100; i++ ) {

value = Math.random() * 100;
sigma = mstdev( value );

console.log( '%d\t%d', value.toFixed( 4 ), sigma.toFixed( 4 ) );
}
77 changes: 70 additions & 7 deletions lib/index.js
Expand Up @@ -30,20 +30,83 @@

// MODULES //

// var module_alias = require( 'module_name' );
var isInteger = require( 'validate.io-integer' );


// FUNCTIONS //
// INCREMENTAL MOVING SAMPLE STANDARD DEVIATION //

/**
* FUNCTION: foo()
* {{ foo description }}.
* FUNCTION: incrmstdev( W )
* Returns a method to compute a moving sample standard deviation incrementally.
*
* @param {Number} W - window size
* @returns {Function} method to compute a moving sample standard deviation incrementally
*/
function foo() {
function incrmstdev( W ) {
if ( !isInteger( W ) || W < 1 ) {
throw new TypeError( 'incrmstdev()::invalid input argument. Window size must be a positive integer.' );
}
var arr = new Array( W ),
n = W - 1,
M2 = 0,
mu = 0,
N = 0,
i = -1,
delta,
tmp,
d1,
d2;
/**
* FUNCTION: incrmstdev( [value] )
* If a `value` is provided, updates and returns the updated sample standard deviation. If no `value` is provided, returns the current sample standard deviation.
*
* @param {Number} [value] - value used to update the moving sample standard deviation
* @returns {Number} sample standard deviation
*/
return function incrmstdev( x ) {
if ( !arguments.length ) {
if ( N === 0 ) {
return null;
}
if ( N === 1 ) {
return 0;
}
if ( N < W ) {
return Math.sqrt( M2 / (N-1) );
}
return Math.sqrt( M2 / n );
}
// Update the index for managing the circular buffer...
i = (i+1) % W;

// Fill up the initial window; else, update the existing window...
if ( N < W ) {
arr[ i ] = x;
N += 1;
delta = x - mu;
mu += delta / N;
M2 += delta * (x - mu);
if ( N === 1 ) {
return 0;
}
return Math.sqrt( M2 / (N-1) );
}
if ( N === 1 ) {
return 0;
}
tmp = arr[ i ];
arr[ i ] = x;
delta = x - tmp;
d1 = tmp - mu;
mu += delta / W;
d2 = x - mu;
M2 += delta * (d1 + d2);

} // end FUNCTION foo()
return Math.sqrt( M2 / n );
};
} // end FUNCTION incrmstdev()


// EXPORTS //

module.exports = foo;
module.exports = incrmstdev;
19 changes: 17 additions & 2 deletions package.json
Expand Up @@ -25,12 +25,27 @@
"keywords": [
"compute.io",
"compute",
"computation"
"computation",
"statistics",
"stats",
"mathematics",
"math",
"sample variance",
"variance",
"dispersion",
"moving variance",
"sample standard deviation",
"standard deviation",
"stdev",
"sliding",
"window"
],
"bugs": {
"url": "https://github.com/compute-io/incrmstdev/issues"
},
"dependencies": {},
"dependencies": {
"validate.io-integer": "^1.0.2"
},
"devDependencies": {
"chai": "1.x.x",
"mocha": "1.x.x",
Expand Down
97 changes: 94 additions & 3 deletions test/test.js
@@ -1,3 +1,4 @@
/* global describe, it, require */
'use strict';

// MODULES //
Expand All @@ -6,7 +7,7 @@ var // Expectation library:
chai = require( 'chai' ),

// Module to be tested:
lib = require( './../lib' );
incrmstdev = require( './../lib' );


// VARIABLES //
Expand All @@ -20,9 +21,99 @@ var expect = chai.expect,
describe( 'compute-incrmstdev', function tests() {

it( 'should export a function', function test() {
expect( lib ).to.be.a( 'function' );
expect( incrmstdev ).to.be.a( 'function' );
});

it( 'should do something' );
it( 'should throw an error if not provided an integer', function test() {
var values = [
'5',
-5,
0,
true,
null,
undefined,
NaN,
[],
{},
function(){}
];

for ( var i = 0; i < values.length; i++ ) {
expect( badValue( values[i] ) ).to.throw( TypeError );
}

function badValue( value ) {
return function() {
incrmstdev( value );
};
}
});

it( 'should return a function', function test() {
expect( incrmstdev( 3 ) ).to.be.a( 'function' );
});

it( 'should compute a moving sample standard deviation incrementally', function test() {
var data,
N,
d,
expected,
actual,
mstdev;

data = [ 2, 3, 4, -1, 3, 1 ];
N = data.length;

mstdev = incrmstdev( 3 );

actual = new Array( N );
for ( var i = 0; i < N; i++ ) {
d = data[ i ];
actual[ i ] = mstdev( d );
}

expected = [ 0, Math.sqrt( 0.5 ), 1, Math.sqrt(7), Math.sqrt(7), 2 ];

assert.deepEqual( actual, expected );
});

it( 'should return the current moving sample standard deviation if provided no arguments', function test() {
var data = [ 2, 3, 10 ],
len = data.length,
mstdev = incrmstdev( 3 ),
i;

for ( i = 0; i < len-1; i++ ) {
mstdev( data[ i ] );
}
assert.strictEqual( mstdev(), Math.sqrt( 0.5 ) );

for ( i = 0; i < len; i++ ) {
mstdev( data[ i ] );
}
assert.closeTo( mstdev(), Math.sqrt(19), 1e-10 );
});

it( 'should return null if asked for a moving sample standard deviation when not having received any data', function test() {
var mstdev = incrmstdev( 3 );
assert.isNull( mstdev() );
});

it( 'should return 0 if asked for a moving sample standard deviation when having received only a single datum', function test() {
var mstdev = incrmstdev( 3 );
mstdev( 4 );
assert.strictEqual( mstdev(), 0 );
});

it( 'should always return 0 if provided a window size equal to 1', function test() {
var mstdev = incrmstdev( 1 );
mstdev( 4 );
assert.strictEqual( mstdev(), 0 );
assert.strictEqual( mstdev( 5 ), 0 );
assert.strictEqual( mstdev( 2 ), 0 );
for ( var i = 0; i < 100; i++ ) {
assert.strictEqual( mstdev( i ), 0 );
}
});

});

0 comments on commit 6f692a4

Please sign in to comment.