From d51dfe4604e815e14058605746e83363a12237b6 Mon Sep 17 00:00:00 2001 From: in0068 Date: Thu, 15 Apr 2021 15:39:07 +0530 Subject: [PATCH 1/4] Statistic Module added --- modules/index.collections.statistics.js | 10 +++ modules/mean.js | 11 ++++ modules/median.js | 19 ++++++ modules/mode.js | 16 +++++ modules/percentile.js | 21 ++++++ modules/standardDeviation.js | 7 ++ modules/standardError.js | 8 +++ modules/statRange.js | 6 ++ modules/sum.js | 22 +++++++ modules/variance.js | 24 +++++++ test/collections.statistics.js | 86 +++++++++++++++++++++++++ 11 files changed, 230 insertions(+) create mode 100644 modules/index.collections.statistics.js create mode 100644 modules/mean.js create mode 100644 modules/median.js create mode 100644 modules/mode.js create mode 100644 modules/percentile.js create mode 100644 modules/standardDeviation.js create mode 100644 modules/standardError.js create mode 100644 modules/statRange.js create mode 100644 modules/sum.js create mode 100644 modules/variance.js create mode 100644 test/collections.statistics.js 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..6601d9f --- /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 = cb(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'); + }); +}()); From b5acf755809e0e1bed4a406566cf5896e27d2a63 Mon Sep 17 00:00:00 2001 From: in0068 Date: Thu, 21 Oct 2021 11:22:02 +0530 Subject: [PATCH 2/4] Docs added --- docs/underscore.collections.statistics.md | 151 ++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 docs/underscore.collections.statistics.md diff --git a/docs/underscore.collections.statistics.md b/docs/underscore.collections.statistics.md new file mode 100644 index 0000000..1631f42 --- /dev/null +++ b/docs/underscore.collections.statistics.md @@ -0,0 +1,151 @@ +### statistics + +> Statisctics Functions. View Annotated Source + +#### mean + +Signature: `_.mean(... arrays:Array ...)` + +The `_.mean` function finds out the average value from the array of numbers. + +```javascript + +_.mean([]); +//=> 0 + +_.mean([0, 1, 2, 3, 4]); +//=> 2 + +_.mean(null) +//=> 0 + +``` + +#### median + +Signature: `_.median(... arrays: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 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. +```javascript + +_.median([]); +//=> undefined + +_.median([1, 2, 3]); +//=> 2 + +_.median([1, 2, 3, 4]) +// => 2.5 + +``` + +#### sum + +Signature: `_.sum(... arrays:Array ...)` + +The `_.sum` function calculates the sum of the given arrays. + +```javascript + +_.sum([]); +//=> 0 + +_.sum([0, 1, 2, 3, 4]); +//=> 10 +``` + +#### variance + +Signature: `_.variance(... arrays:Array ...)` + +The `_.variance` function return the variance of the numeric elements of the collection. + +```javascript + +_.variance([]); +//=> 0 + +_.variance([0, 1, 2, 3, 4]); +//=> 1.25 +``` + +#### standardDeviation + +Signature: `_.standardDeviation(... arrays:Array ...)` + +The `_.standardDeviation` function return the standard deviation of the numeric elements of the collection. + +```javascript + +_.standardDeviation([]); +//=> 0 + +_.standardDeviation([1, 2, 3, 4]); +//=> 1.118 +``` + +#### standardError + +Signature: `_.standardError(... arrays:Array ...)` + +The `_.standardError` function return the standard error of the numeric elements of the collection. + +```javascript + +_.standardError([]); +//=> 0 + +_.standardError([1, 2, 3, 4]); +//=> 0.64 +``` + +#### mode + +Signature: `_.mode(... arrays:Array ...)` + +The `_.mode` function return the element (or element-based computation) that appears most frequently in the collection. + +```javascript + +_.mode([]); +//=> undefined + +_.mode([1, 1, 3, 4]); +//=> 1 +``` + +#### statRange + +Signature: `_.statRange(... arrays:Array ...)` + +The `_.statRange` function return the difference of the max and min value of the elements in the array. + +```javascript + +_.statRange([]); +//=> -Infinity + +_.statRange([1, 1, 3, 4]); +//=> 3 +``` + +#### percentile + +Signature: `_.percentile(... arrays:Array ...,percentileval:number)` + +The `_.percentile` function return the percentile value of the numeric elements from the collection like 50th,75th,99th etc. + +```javascript + +_.percentile([], 10); +//=> 0 + +_.percentile([1, 1, 3, 4], 50); +//=> 2 +``` \ No newline at end of file From a778819b0c5e5400551651098480daed63f10633 Mon Sep 17 00:00:00 2001 From: in0068 Date: Thu, 21 Oct 2021 11:37:20 +0530 Subject: [PATCH 3/4] Docs added and Changes made as suggested --- modules/variance.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/variance.js b/modules/variance.js index 6601d9f..1d2e85f 100644 --- a/modules/variance.js +++ b/modules/variance.js @@ -9,7 +9,7 @@ export default function variance(collection, iteratee, context) { if (typeof iteratee == 'number' && collection != null && typeof collection[0] != 'object') { iteratee = null; } - iteratee = cb(iteratee, context); + iteratee = iteratee(iteratee, context); var computed = []; var avg = mean(collection, function(value, key, collection) { From 14c51c69cc37216740b2ed101f42199eb5c5e33d Mon Sep 17 00:00:00 2001 From: in0068 Date: Fri, 22 Oct 2021 09:45:55 +0530 Subject: [PATCH 4/4] Changes made as suggested --- docs/underscore.collections.statistics.md | 42 ++++++++++++++++------- modules/variance.js | 2 +- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/docs/underscore.collections.statistics.md b/docs/underscore.collections.statistics.md index 1631f42..a9d3a85 100644 --- a/docs/underscore.collections.statistics.md +++ b/docs/underscore.collections.statistics.md @@ -4,10 +4,12 @@ #### mean -Signature: `_.mean(... arrays:Array ...)` +Signature: `_.mean(... array:Array ...)` The `_.mean` function finds out the average value from the array of numbers. +Link for reference Mean + ```javascript _.mean([]); @@ -23,15 +25,18 @@ _.mean(null) #### median -Signature: `_.median(... arrays:Array ...)` +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 has odd numbers then median is the middle element. +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 -If the array has even numbers then average of middle two numbers is the median value. ```javascript _.median([]); @@ -47,9 +52,9 @@ _.median([1, 2, 3, 4]) #### sum -Signature: `_.sum(... arrays:Array ...)` +Signature: `_.sum(... array:Array ...)` -The `_.sum` function calculates the sum of the given arrays. +The `_.sum` function calculates the sum of the given array. ```javascript @@ -62,10 +67,12 @@ _.sum([0, 1, 2, 3, 4]); #### variance -Signature: `_.variance(... arrays:Array ...)` +Signature: `_.variance(... array:Array ...)` The `_.variance` function return the variance of the numeric elements of the collection. +Link for reference Variance + ```javascript _.variance([]); @@ -77,10 +84,12 @@ _.variance([0, 1, 2, 3, 4]); #### standardDeviation -Signature: `_.standardDeviation(... arrays:Array ...)` +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([]); @@ -92,10 +101,12 @@ _.standardDeviation([1, 2, 3, 4]); #### standardError -Signature: `_.standardError(... arrays:Array ...)` +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([]); @@ -107,10 +118,12 @@ _.standardError([1, 2, 3, 4]); #### mode -Signature: `_.mode(... arrays:Array ...)` +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([]); @@ -122,10 +135,12 @@ _.mode([1, 1, 3, 4]); #### statRange -Signature: `_.statRange(... arrays:Array ...)` +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([]); @@ -137,10 +152,13 @@ _.statRange([1, 1, 3, 4]); #### percentile -Signature: `_.percentile(... arrays:Array ...,percentileval:number)` +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); diff --git a/modules/variance.js b/modules/variance.js index 1d2e85f..ddce9f9 100644 --- a/modules/variance.js +++ b/modules/variance.js @@ -9,7 +9,7 @@ export default function variance(collection, iteratee, context) { if (typeof iteratee == 'number' && collection != null && typeof collection[0] != 'object') { iteratee = null; } - iteratee = iteratee(iteratee, context); + iteratee = _.iteratee(iteratee, context); var computed = []; var avg = mean(collection, function(value, key, collection) {