UNPKG

flow-iqr

Version:

Reduce transform stream which calculates the interquartile range over streamed data.

176 lines (145 loc) 3.53 kB
/** * * STREAM: interquartile range * * * DESCRIPTION: * - Reduce transform stream which computes the interquartile range over streamed data. * * * NOTES: * [1] In order to compute the interquartile range exactly, all values must be buffered into memory. As a result, this stream is a sink stream, and caution should be used for large datasets. * * * TODO: * [1] * * * HISTORY: * - 2014/06/13: Created. [AReines]. * * * DEPENDENCIES: * [1] event-stream * [2] flow-map * * * LICENSE: * MIT * * Copyright (c) 2014. Athan Reines. * * * AUTHOR: * Athan Reines. athan@nodeprime.com. 2014. * */ (function() { 'use strict'; // MODULES // var // Event stream module: eventStream = require( 'event-stream' ), // Map transform stream: mapStream = require( 'flow-map' ); // FUNCTIONS // /** * FUNCTION: transform( data ) * Defines the data transformation. * * @private * @param {number} data - numeric stream data * @returns {number} transformed data */ function transform( data ) { var qValues; // Convert a comma delimited string into an array: data = JSON.parse( '[' + data + ']' ); // Compute the quartiles: qValues = quartiles( data ); // Return the interquartile range: return qValues[1] - qValues[0]; } // end METHOD transform() /** * FUNCTION: quartiles( vector ) * Computes quartiles for a 1d dataset array. * * @private * @param {array} vector - 1d array * @returns {array} quartiles as a 2-element array */ function quartiles( vector ) { var qValues = new Array( 2 ), vec; // Create a copy of the input vector: vec = vector.slice(); // Sort the input vector: vec.sort( function ( a, b ) { return a - b; }); qValues[ 0 ] = quantile( vec, 0.25 ); qValues[ 1 ] = quantile( vec, 0.75 ); return qValues; } // end FUNCTION quantiles() /** * FUNCTION: quantile( vector, percent ) * Finds a quantile value. * * @private * @param {array} vector - 1d array * @param {number} percent - quantile percent [0,1] * @returns {number} quantile value */ function quantile( vector, percent ) { var numValues = vector.length, id, value; // Calculate the vector index marking the quantile: id = ( numValues * percent ) - 1; // Is the index an integer? if ( id === parseInt( id, 10 ) ) { // Value is the average between the value at id and id+1: value = ( vector[ id ] + vector[ id+1 ] ) / 2.0; } else { // Round up to the next index: id = Math.ceil( id ); value = vector[ id ]; } return value; } // end FUNCTION quantile() // STREAM // /** * FUNCTION: Stream() * Stream constructor. * * @returns {object} Stream instance */ function Stream() { return this; } // end FUNCTION stream() /** * METHOD: stream() * Returns a JSON data transformation stream for calculating the statistic. */ Stream.prototype.stream = function() { var jStream, sStream, qStream, pStream; // Create a stream to comma-deliminate all incoming data values: jStream = eventStream.join( ',' ); // Create a sink stream to buffer all data values into memory: sStream = eventStream.wait(); // Create a map transform stream to calculate the quartiles and interquartile range: qStream = mapStream() .map( transform ) .stream(); // Create a stream pipeline: pStream = eventStream.pipeline( jStream, sStream, qStream ); // Return the pipeline: return pStream; }; // end METHOD stream() // EXPORTS // module.exports = function createStream() { return new Stream(); }; })();