diff --git a/docs/underscore.collections.statistics.md b/docs/underscore.collections.statistics.md new file mode 100644 index 0000000..a9d3a85 --- /dev/null +++ b/docs/underscore.collections.statistics.md @@ -0,0 +1,169 @@ +### statistics + +> Statisctics Functions. View Annotated Source + +#### mean + +Signature: `_.mean(... array:Array ...)` + +The `_.mean` function finds out the average value from the array of numbers. + +Link for reference Mean + +```javascript + +_.mean([]); +//=> 0 + +_.mean([0, 1, 2, 3, 4]); +//=> 2 + +_.mean(null) +//=> 0 + +``` + +#### median + +Signature: `_.median(... array:Array ...)` + +The `_.median` function finds out the middle value from the array of numbers. + +Calulation of median is done using the following method. + +If the array is odd length then median is the middle element. + +If the array is even numbers then average of middle two numbers is the median value. + +Link for reference Median + +```javascript + +_.median([]); +//=> undefined + +_.median([1, 2, 3]); +//=> 2 + +_.median([1, 2, 3, 4]) +// => 2.5 + +``` + +#### sum + +Signature: `_.sum(... array:Array ...)` + +The `_.sum` function calculates the sum of the given array. + +```javascript + +_.sum([]); +//=> 0 + +_.sum([0, 1, 2, 3, 4]); +//=> 10 +``` + +#### variance + +Signature: `_.variance(... array:Array ...)` + +The `_.variance` function return the variance of the numeric elements of the collection. + +Link for reference Variance + +```javascript + +_.variance([]); +//=> 0 + +_.variance([0, 1, 2, 3, 4]); +//=> 1.25 +``` + +#### standardDeviation + +Signature: `_.standardDeviation(... array:Array ...)` + +The `_.standardDeviation` function return the standard deviation of the numeric elements of the collection. + +Link for reference Standard Deviation + +```javascript + +_.standardDeviation([]); +//=> 0 + +_.standardDeviation([1, 2, 3, 4]); +//=> 1.118 +``` + +#### standardError + +Signature: `_.standardError(... array:Array ...)` + +The `_.standardError` function return the standard error of the numeric elements of the collection. + +Link for reference Standard Error + +```javascript + +_.standardError([]); +//=> 0 + +_.standardError([1, 2, 3, 4]); +//=> 0.64 +``` + +#### mode + +Signature: `_.mode(... array:Array ...)` + +The `_.mode` function return the element (or element-based computation) that appears most frequently in the collection. + +Link for reference Mode + +```javascript + +_.mode([]); +//=> undefined + +_.mode([1, 1, 3, 4]); +//=> 1 +``` + +#### statRange + +Signature: `_.statRange(... array:Array ...)` + +The `_.statRange` function return the difference of the max and min value of the elements in the array. + +Link for reference Range + +```javascript + +_.statRange([]); +//=> -Infinity + +_.statRange([1, 1, 3, 4]); +//=> 3 +``` + +#### percentile + +Signature: `_.percentile(... array:Array ...,percentileVal:number)` + +The `_.percentile` function return the percentile value of the numeric elements from the collection like 50th,75th,99th etc. + +Link for reference Percentile + + +```javascript + +_.percentile([], 10); +//=> 0 + +_.percentile([1, 1, 3, 4], 50); +//=> 2 +``` \ No newline at end of file diff --git a/modules/index.collections.statistics.js b/modules/index.collections.statistics.js new file mode 100644 index 0000000..41c88de --- /dev/null +++ b/modules/index.collections.statistics.js @@ -0,0 +1,10 @@ +//Statistical Function +export { default as sum } from './sum.js'; +export { default as mean } from './mean.js'; +export { default as median } from './median.js'; +export { default as standardDeviation } from './standardDeviation.js'; +export { default as variance } from './variance.js'; +export { default as mode } from './mode.js'; +export { default as standardError } from './standardError.js'; +export { default as statRange } from './statRange.js'; +export { default as percentile } from './percentile.js'; diff --git a/modules/mean.js b/modules/mean.js new file mode 100644 index 0000000..9d3acbc --- /dev/null +++ b/modules/mean.js @@ -0,0 +1,11 @@ +import size from 'underscore/modules/size.js'; +import sum from './sum.js'; + +// Compute the average of the numbers obtained from the collection +export default function mean(collection, iteratee, context) { + var length = size(collection); + + if (length < 1) return 0; + + return sum(collection, iteratee, context) / length; +} diff --git a/modules/median.js b/modules/median.js new file mode 100644 index 0000000..2dffc34 --- /dev/null +++ b/modules/median.js @@ -0,0 +1,19 @@ +import map from 'underscore/modules/map.js' +import isEmpty from 'underscore/modules/isEmpty'; + +// https://en.wikipedia.org/wiki/Median +// Calulation of median is done using the following method. +// If the array has odd numbers then median is the middle element. +// If the array has even numbers then average of middle two numbers is the median value. +export default function median(collection, iteratee, context) { + if (isEmpty(collection)) return undefined; + + if (typeof iteratee == 'number' && collection != null && typeof collection[0] != 'object') { + iteratee = null; + } + var tmpArr = map(collection, iteratee, context).sort(); + + return tmpArr.length % 2 ? + tmpArr[Math.floor(tmpArr.length / 2)] : + (tmpArr[tmpArr.length / 2 - 1] + tmpArr[tmpArr.length / 2]) / 2 +} diff --git a/modules/mode.js b/modules/mode.js new file mode 100644 index 0000000..fd77981 --- /dev/null +++ b/modules/mode.js @@ -0,0 +1,16 @@ +import isEmpty from 'underscore/modules/isEmpty'; +import groupBy from 'underscore/modules/groupBy.js'; +import max from 'underscore/modules/max.js'; +import first from 'underscore/modules/first.js'; + +// Return the element (or element-based computation) that appears most frequently in the collection. +// https://en.wikipedia.org/wiki/Mode_(statistics) +export default function mode(collection, iteratee, context) { + if (isEmpty(collection)) return; + + if (typeof iteratee == 'number' && collection != null && typeof collection[0] != 'object') { + iteratee = null; + } + var groups = groupBy(collection, iteratee, context); + return first(max(groups, 'length')); +} diff --git a/modules/percentile.js b/modules/percentile.js new file mode 100644 index 0000000..07531bd --- /dev/null +++ b/modules/percentile.js @@ -0,0 +1,21 @@ +import isEmpty from 'underscore/modules/isEmpty'; +import sortBy from 'underscore/modules/sortBy.js'; + +// Return the percentile value of the numeric elements from the collection +//Ex : 50th,75th,99th etc. +//https://en.wikipedia.org/wiki/Percentile +export default function percentile(collection, percentile) { + if (isEmpty(collection)) return 0; + if (typeof percentile !== 'number') throw new TypeError('Percentile must be a number between 0 - 100'); + if (percentile <= 0) return collection[0]; + if (percentile >= 100) return collection[collection.length - 1]; + + collection = sortBy(collection); + var index = (percentile/100) * (collection.length - 1), + lowerIndex = Math.floor(index), + upperIndex = lowerIndex + 1, + weight = index % 1; + + if (upperIndex >= collection.length) return collection[lowerIndex]; + return collection[lowerIndex] * (1 - weight) + collection[upperIndex] * weight; +} diff --git a/modules/standardDeviation.js b/modules/standardDeviation.js new file mode 100644 index 0000000..1994ecf --- /dev/null +++ b/modules/standardDeviation.js @@ -0,0 +1,7 @@ +import variance from './variance.js'; + +// Return the standardDeviation based on element-based computation. +// https://en.wikipedia.org/wiki/Standard_deviation +export default function standardDeviation(collection, iteratee, context) { + return Math.sqrt(variance(collection, iteratee, context)); +} diff --git a/modules/standardError.js b/modules/standardError.js new file mode 100644 index 0000000..1b61c0f --- /dev/null +++ b/modules/standardError.js @@ -0,0 +1,8 @@ +import variance from './variance.js'; +import size from 'underscore/modules/size.js'; + +// Return the standard error of the mean based on element-based computation. +// https://en.wikipedia.org/wiki/Standard_error +export default function standardError(collection, iteratee, context) { + return Math.sqrt(variance(collection, iteratee, context) / (size(collection) - 1)); +} diff --git a/modules/statRange.js b/modules/statRange.js new file mode 100644 index 0000000..5c77d42 --- /dev/null +++ b/modules/statRange.js @@ -0,0 +1,6 @@ +import max from 'underscore/modules/max.js'; +import min from 'underscore/modules/min.js'; + +export default function statRange(collection, iteratee, context){ + return max(collection, iteratee, context) - min(collection, iteratee, context); +} diff --git a/modules/sum.js b/modules/sum.js new file mode 100644 index 0000000..3577d30 --- /dev/null +++ b/modules/sum.js @@ -0,0 +1,22 @@ +import isArrayLike from 'underscore/modules/_isArrayLike.js'; +import values from 'underscore/modules/values.js'; +import 'underscore/modules/iteratee.js'; +import _ from 'underscore/modules/underscore.js'; +import find from 'underscore/modules/find.js'; + +// Return the sum of elements (or element-based computation). +export default function sum(collection, iteratee, context) { + var result = 0; + if (iteratee == null || typeof iteratee == 'number' && collection != null && typeof collection[0] != 'object') { + collection = isArrayLike(collection) ? collection : values(collection); + for (var i = 0, length = collection.length; i < length; i++) { + result += collection[i]; + } + } else { + iteratee = _.iteratee(iteratee, context); + find(collection, function(v, index, list) { + result += iteratee(v, index, list);; + }); + } + return result; +} diff --git a/modules/variance.js b/modules/variance.js new file mode 100644 index 0000000..ddce9f9 --- /dev/null +++ b/modules/variance.js @@ -0,0 +1,24 @@ +import 'underscore/modules/iteratee.js'; +import _ from 'underscore/modules/underscore.js'; +import mean from './mean.js'; + +// Return the variance of the numeric elements of the collection, +// optionally after transforming them through `iteratee`. +// https://en.wikipedia.org/wiki/Variance +export default function variance(collection, iteratee, context) { + if (typeof iteratee == 'number' && collection != null && typeof collection[0] != 'object') { + iteratee = null; + } + iteratee = _.iteratee(iteratee, context); + + var computed = []; + var avg = mean(collection, function(value, key, collection) { + var result = iteratee(value, key, collection); + computed.push(result); + return result; + }); + return mean(computed, function(value) { + var difference = value - avg; + return difference * difference; + }); +} diff --git a/test/collections.statistics.js b/test/collections.statistics.js new file mode 100644 index 0000000..bc2fc99 --- /dev/null +++ b/test/collections.statistics.js @@ -0,0 +1,86 @@ +(function() { + var _ = typeof require == 'function' ? require('..') : window._; + QUnit.module('Statistics'); + QUnit.test('mean', function(assert) { + assert.strictEqual(_.mean(null), 0, 'can handle null/undefined'); + assert.strictEqual(_.mean(void 0), 0, 'can handle undefined'); + assert.strictEqual(_.mean([1, 2, 3]), 2, "Avearge of the numbers in the collection"); + assert.strictEqual(_.mean({}), 0, 'Avearge value of an empty object'); + assert.strictEqual(_.mean([]), 0, 'Avearge value of an empty array'); + }); + + QUnit.test('median', function(assert) { + assert.strictEqual(_.median(null), undefined, 'can handle null/undefined'); + assert.strictEqual(_.median(void 0), undefined, 'can handle undefined'); + assert.strictEqual(_.median([1, 2, 3]), 2, "Median of the numbers in the collection"); + assert.strictEqual(_.median([1, 2, 3, 4]), 2.5, "Median of the numbers in the collection"); + assert.strictEqual(_.median({}), undefined, 'Median value of an empty object'); + assert.strictEqual(_.median([]), undefined, 'Median value of an empty array'); + }); + + QUnit.test('sum', function(assert) { + assert.strictEqual(_.sum(null), 0, 'can handle null/undefined'); + assert.strictEqual(_.sum(void 0), 0, 'can handle undefined'); + assert.strictEqual(_.sum([1, 2, 3]), 6, "SUM of the numbers in the collection"); + assert.strictEqual(_.sum([1, 2, 3, 4]), 10, "SUM of the numbers in the collection"); + assert.strictEqual(_.sum({}), 0, 'sum value of an empty object'); + assert.strictEqual(_.sum([]), 0, 'sum value of an empty array'); + }); + + QUnit.test('variance', function(assert) { + assert.strictEqual(_.variance(null), 0, 'can handle null/undefined'); + assert.strictEqual(_.variance(void 0), 0, 'can handle undefined'); + assert.strictEqual(_.variance([0, 1, 2, 3, 4]), 2, "variance of the numbers in the collection"); + assert.strictEqual(_.variance([1, 2, 3, 4]), 1.25, "variance of the numbers in the collection"); + assert.strictEqual(_.variance({}), 0, 'variance value of an empty object'); + assert.strictEqual(_.variance([]), 0, 'variance value of an empty array'); + }); + + QUnit.test('standardDeviation', function(assert) { + assert.strictEqual(_.standardDeviation(null), 0, 'can handle null/undefined'); + assert.strictEqual(_.standardDeviation(void 0), 0, 'can handle undefined'); + assert.strictEqual(_.standardDeviation([0, 1, 2, 3, 4]), 1.4142135623730951, "Standard Deviation of the numbers in the collection"); + assert.strictEqual(_.standardDeviation([1, 2, 3, 4]), 1.118033988749895, "Standard Deviation of the numbers in the collection"); + assert.strictEqual(_.standardDeviation({}), 0, 'Standard Deviation value of an empty object'); + assert.strictEqual(_.standardDeviation([]), 0, 'Standard Deviation value of an empty array'); + }); + + QUnit.test('standardError', function(assert) { + assert.strictEqual(_.standardError(null), 0, 'can handle null/undefined'); + assert.strictEqual(_.standardError(void 0), 0, 'can handle undefined'); + assert.strictEqual(_.standardError([0, 1, 2, 3, 4]), 0.7071067811865476, "Standard Error of the numbers in the collection"); + assert.strictEqual(_.standardError([1, 2, 3, 4]), 0.6454972243679028, "Standard Error of the numbers in the collection"); + assert.strictEqual(_.standardError({}), 0, 'Standard Error value of an empty object'); + assert.strictEqual(_.standardError([]), 0, 'Standard Error value of an empty array'); + }); + + QUnit.test('mode', function(assert) { + assert.strictEqual(_.mode(null), undefined, 'can handle null/undefined'); + assert.strictEqual(_.mode(void 0), undefined, 'can handle undefined'); + assert.strictEqual(_.mode([0, 1, 2, 3, 4]), 0, "Mode of the numbers in the collection"); + assert.strictEqual(_.mode([1, 1, 3, 4]), 1, "Mode of the numbers in the collection"); + assert.strictEqual(_.mode({}), undefined, 'Mode value of an empty object'); + assert.strictEqual(_.mode([]), undefined, 'Mode value of an empty array'); + }); + + QUnit.test('statRange', function(assert) { + assert.strictEqual(_.statRange(null), -Infinity, 'can handle null/undefined'); + assert.strictEqual(_.statRange(void 0), -Infinity, 'can handle undefined'); + assert.strictEqual(_.statRange([0, 1, 2, 3, 4]), 4, "Stat Range of the numbers in the collection"); + assert.strictEqual(_.statRange([1, 1, 3, 4]), 3, "Stat Range of the numbers in the collection"); + assert.strictEqual(_.statRange({}), -Infinity, 'Stat Range value of an empty object'); + assert.strictEqual(_.statRange([]), -Infinity, 'Stat Range value of an empty array'); + }); + + QUnit.test('percentile', function(assert) { + assert.strictEqual(_.percentile(null, 25), 0, 'can handle null/undefined'); + assert.strictEqual(_.percentile(void 0, 50), 0, 'can handle undefined'); + assert.strictEqual(_.percentile([0, 1, 2, 3, 4], 75), 3, "75th percentile of the numbers in the collection"); + assert.strictEqual(_.percentile([1, 1, 3, 4], 50), 2, "50th of the numbers in the collection"); + assert.strictEqual(_.percentile({}, 10), 0, 'Percentile value of an empty object'); + assert.strictEqual(_.percentile([], 50), 0, 'Percentile value of an empty array'); + assert.raises(function() { + _.percentile([1, 1, 3, 4], "50"); + }, TypeError, 'Percentile must be a number between 0 - 100'); + }); +}());