Last active
September 25, 2021 03:46
-
-
Save rikyperdana/a7349c790cf5b034a1b77db64415e73c to your computer and use it in GitHub Desktop.
Statistics in JS with Functional Paradigm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var | |
| withThis = (obj, cb) => cb(obj), | |
| get = prop => obj => obj[prop], | |
| add = array => array.reduce((acc, inc) => acc + inc), | |
| sub = array => array.reduce((acc, inc) => acc - inc), | |
| mul = array => array.reduce((acc, inc) => acc * inc), | |
| pow = asc => num => Math.pow(num, asc), | |
| range = array => Math.max(...array) - Math.min(...array), | |
| between = (low, middle, high) => | |
| (low <= middle) && (middle <= high), | |
| classes = array => Math.round(1 + (3.3 * Math.log10(array.length))), | |
| interval = array => Math.round(range(array) / classes(array)), | |
| makeArray = num => [...Array(num).keys()], // generate [0, 1, 2, ...num] | |
| sort = array => array.sort((a, b) => a - b), | |
| /*---------------------------------------------------------------------------------------*/ | |
| mean = array => add(array) / array.length, | |
| ors = array => array.find(Boolean) | |
| median = array => withThis( | |
| array.sort((a, b) => a - b), | |
| sorted => ors([ | |
| sorted.length === 1 && sorted[0], | |
| sorted.length === 2 && add(sorted) / 2, | |
| ]) || median(sorted.slice(1, sorted.length - 1)) | |
| ) | |
| median([2, 1, 2, 3]) // result: 2 | |
| /*----------------------------------------------------------------*/ | |
| distFreq = array => withThis({ | |
| m: Math.min(...array), i: interval(array) | |
| }, ({m, i}) => | |
| makeArray(classes(array)) | |
| .map(j => withThis({ | |
| bot: m + (i * j), | |
| top: m + (i * j) + i - 1, | |
| }, ({bot, top}) => ({ | |
| bot, top, fre: array.reduce((acc, inc) => | |
| between(bot, inc, top) ? acc + 1 : acc | |
| , 0) | |
| }))) | |
| ), | |
| data = [ | |
| 78, 72, 74, 79, 74, 71, 75, 74, 72, 68, | |
| 72, 73, 72, 74, 75, 74, 73, 74, 65, 72, | |
| 66, 75, 80, 69, 82, 73, 74, 72, 79, 71, | |
| 70, 75, 71, 70, 70, 70, 75, 76, 77, 67 | |
| ] | |
| distFreq(data) // call the function | |
| /* // get the result | |
| [ | |
| {"bot": 65, "top": 67, "fre": 3 }, | |
| {"bot": 68, "top": 70, "fre": 6 }, | |
| {"bot": 71, "top": 73, "fre": 12 }, | |
| {"bot": 74, "top": 76, "fre": 13 }, | |
| {"bot": 77, "top": 79, "fre": 4 }, | |
| {"bot": 80, "top": 82, "fre": 2 } | |
| ] */ | |
| /*---------------------------------------------------------------------------------------*/ | |
| distLength = dist => add(dist.map(get('fre'))) | |
| /*---------------------------------------------------------------------------------------*/ | |
| distRelative = (dist, percent) => dist.map( | |
| i => Object.assign(i, { | |
| rel: i.fre / add(dist.map(get('fre'))) | |
| * (percent ? 100 : 1) | |
| }) | |
| ) | |
| distRelative(distFreq(data)) // with decimal | |
| /* // get the result | |
| [ | |
| {"bot": 65, "top": 67, "fre": 3, "rel": 0.075 }, | |
| {"bot": 68, "top": 70, "fre": 6, "rel": 0.15 }, | |
| {"bot": 71, "top": 73, "fre": 12, "rel": 0.3 }, | |
| {"bot": 74, "top": 76, "fre": 13, "rel": 0.325 }, | |
| {"bot": 77, "top": 79, "fre": 4, "rel": 0.1 }, | |
| {"bot": 80, "top": 82, "fre": 2, "rel": 0.05 } | |
| ] */ | |
| distRelative(distFreq(data), true) // with percentage | |
| /* // get the result | |
| [ | |
| {"bot": 65, "top": 67, "fre": 3, "rel": 7.5 }, | |
| {"bot": 68, "top": 70, "fre": 6, "rel": 15 }, | |
| {"bot": 71, "top": 73, "fre": 12, "rel": 30 }, | |
| {"bot": 74, "top": 76, "fre": 13, "rel": 32.5 }, | |
| {"bot": 77, "top": 79, "fre": 4, "rel": 10 }, | |
| {"bot": 80, "top": 82, "fre": 2, "rel": 5 } | |
| ] */ | |
| /*---------------------------------------------------------------------------------------*/ | |
| distCumulative = dist => dist.reduce( | |
| (acc, inc) => [...acc, Object.assign(inc, { | |
| cumA: inc.fre + add([0, ...acc.map(get('fre'))]), | |
| cumD: sub([ | |
| add(dist.map(get('fre'))), | |
| add([0, ...acc.map(get('fre'))]), | |
| ]) | |
| })], | |
| []) | |
| distCumulative(distFreq(data)) // call the function | |
| /* // get the result | |
| [ | |
| {"bot": 65, "top": 67, "fre": 3, "cumA": 3, "cumD": 40 }, | |
| {"bot": 68, "top": 70, "fre": 6, "cumA": 9, "cumD": 37 }, | |
| {"bot": 71, "top": 73, "fre": 12, "cumA": 21, "cumD": 31 }, | |
| {"bot": 74, "top": 76, "fre": 13, "cumA": 34, "cumD": 19 }, | |
| {"bot": 77, "top": 79, "fre": 4, "cumA": 38, "cumD": 6 }, | |
| {"bot": 80, "top": 82, "fre": 2, "cumA": 40, "cumD": 2 } | |
| ] */ | |
| /*---------------------------------------------------------------------------------------*/ | |
| distMean = dist => add(dist.map( | |
| i => (i.bot + ( | |
| (i.top - i.bot) / 2 | |
| )) * i.fre | |
| )) / add(dist.map(get('fre'))) | |
| distMean(distFreq(data)) // 73.125 | |
| /*---------------------------------------------------------------------------------------*/ | |
| distMedian = dist => withThis( | |
| dist[dist.length - 1].cumA, | |
| length => withThis( | |
| dist.find(i => i.cumA >= length / 2), | |
| medClass => (medClass.bot - 0.5) + ( | |
| (length/2 - dist.find( | |
| i => i.top === medClass.bot - 1 | |
| ).cumA) / medClass.fre | |
| ) * (medClass.top - medClass.bot + 1) | |
| ) | |
| ) | |
| distMedian(distCumulative(distFreq(data))) // 73.25 | |
| /*---------------------------------------------------------------------------------------*/ | |
| mode = array => +_.toPairs(array.reduce( | |
| (acc, inc) => _.assign(acc, { | |
| [inc]: acc[inc]+1 || 1 | |
| }) | |
| , {})) | |
| .sort((a, b) => b[1] - a[1]) | |
| [0][0], | |
| distMode = dist => withThis( | |
| dist.reduce((acc, inc) => | |
| inc.fre > acc.fre ? inc : acc | |
| ), mostFre => withThis({ | |
| prev: dist.find(i => i.top === mostFre.bot - 1), | |
| next: dist.find(i => i.bot === mostFre.top + 1) | |
| }, ({prev, next}) => ( | |
| (mostFre.bot - 0.5) + ( | |
| (mostFre.fre - prev.fre) / ( | |
| (mostFre.fre - prev.fre) + | |
| (mostFre.fre - next.fre) | |
| ) | |
| ) * (mostFre.top - mostFre.bot + 1) | |
| )) | |
| ) | |
| distMode(distFreq(data)) // 73.8 | |
| /*---------------------------------------------------------------------------------------*/ | |
| randomize = digits => x => Math.round( | |
| Math.random() * Math.pow(10, digits) | |
| ) | |
| randomize(5)() // get 92836 | |
| distFreq(makeArray(100).map(randomize(2))) // call this | |
| /* // get the result | |
| [ // shall be abnormally distributed with utmost certainty | |
| {"bot": 1, "top": 12, "fre": 10 }, | |
| {"bot": 13, "top": 24, "fre": 5 }, | |
| {"bot": 25, "top": 36, "fre": 17 }, | |
| {"bot": 37, "top": 48, "fre": 12 }, | |
| {"bot": 49, "top": 60, "fre": 17 }, | |
| {"bot": 61, "top": 72, "fre": 13 }, | |
| {"bot": 73, "top": 84, "fre": 15 }, | |
| {"bot": 85, "top": 96, "fre": 7 } | |
| ] */ | |
| /*---------------------------------------------------------------------------------------*/ | |
| fractile = (parts, nth, array) => withThis( | |
| (nth * (array.length + 1) / parts), | |
| decimal => withThis(Math.floor(decimal), | |
| even => withThis(sort(array), | |
| sorted => sorted[even - 1] + ( | |
| (decimal - even) * ( | |
| sorted[even] - sorted[even - 1] | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| fractile(4, 1, data) // 1st le is 71 | |
| fractile(10, 3, data) // 3rd Decile is 71.3 | |
| fractile(100, 82, data) // 82nd Percentile is 75.62 | |
| /*---------------------------------------------------------------------------------------*/ | |
| distFractile = (parts, num, distCum) => withThis( | |
| distCum.reduce( | |
| (acc, inc) => inc.cumA > acc.cumA ? inc : acc | |
| ), tail => withThis( | |
| distCum.find( | |
| i => i.cumA >= (num / parts * tail.cumA) | |
| ), qClass => | |
| (qClass.bot - 0.5) + ( | |
| ( | |
| (num / parts * tail.cumA) - | |
| (qClass.cumA - qClass.fre) | |
| ) / qClass.fre | |
| ) * (qClass.top - qClass.bot + 1) | |
| ) | |
| ) | |
| distFractile(4, 1, distCumulative(distFreq(data))) // 1st Quartile is 70.75 | |
| distFractile(4, 2, distCumulative(distFreq(data))) // 2nd Quartile is 73.25 | |
| distFractile(10, 5, distCumulative(distFreq(data))) // 5th Decile is 73.25 | |
| distFractile(100, 50, distCumulative(distFreq(data))) // 50th Percentile is 73.25 | |
| /*---------------------------------------------------------------------------------------*/ | |
| meanGeometric = array => | |
| pow(1 / array.length)(mul(array)) | |
| meanGeometric([2, 4, 8, 16, 32]) // 8 | |
| /*---------------------------------------------------------------------------------------*/ | |
| meanGrowth = (pt, po, t) => | |
| (Math.pow((pt / po), 1 / t) - 1) * 100 | |
| predictGrowth = (xbar, po, t) => | |
| po * Math.pow((1 + (xbar / 100)), t) | |
| /*----------------------------------------------------------------*/ | |
| distRange = dist => | |
| (dist[dist.length - 1].top + 0.5) - | |
| (dist[0].bot - 0.5) | |
| /*---------------------------------------------------------------------------------------*/ | |
| devMean = array => withThis( | |
| mean(array), meanVal => add( | |
| array.map(i => i - meanVal) | |
| .map(Math.abs) | |
| ) / array.length | |
| ) | |
| /*----------------------------------------------------------------*/ | |
| distDevMean = dist => withThis( | |
| distMean(dist), meanVal => add( | |
| dist.map(i => i.fre * Math.abs( | |
| (i.bot + ((i.top - i.bot) / 2)) - meanVal | |
| )) | |
| ) / add(dist.map(get('fre'))) | |
| ) | |
| distDevMean(distFreq(data)) // get 2.98125 | |
| /*---------------------------------------------------------------------------------------*/ | |
| variance = array => withThis( | |
| mean(array), meanVal => add( | |
| array.map(i => i - meanVal) | |
| .map(pow(2)) | |
| ) / (array.length - ( | |
| array.length > 30 ? 0 : 1 | |
| )) | |
| ) | |
| variance(data) // get 13.069375 | |
| /*---------------------------------------------------------------------------------------*/ | |
| distVariance = dist => withThis( | |
| distMean(dist), meanVal => add( | |
| dist.map(i => i.fre * pow(2)( | |
| (i.bot + ((i.top - i.bot) / 2)) - meanVal | |
| )) | |
| ) / distLength(dist) - ( | |
| distLength(dist) > 30 ? 0 : 1 | |
| ) | |
| ) | |
| distVariance(distFreq(data)) // get 13.359375 | |
| /*---------------------------------------------------------------------------------------*/ | |
| stanDev = pow(1/2) | |
| stanDev(variance(data)) // get 3.6151 | |
| stanDev(distVariance(distFreq(data))) // get 3.6550 | |
| /*---------------------------------------------------------------------------------------*/ | |
| iQR = array => fractile(4, 3, array) - fractile(4, 1, array), | |
| distIQR = dist => | |
| distFractile(4, 3, distCumulative(dist)) - | |
| distFractile(4, 1, distCumulative(dist)) | |
| iQR(data) // get 4 | |
| distIQR(distFreq(data)) // get 4.8269 | |
| /*---------------------------------------------------------------------------------------*/ | |
| skewMod = array => | |
| (mean(array) - mode(array)) / | |
| stanDev(variance(array)), | |
| distSkewMod = dist => | |
| (distMean(dist) - distMode(dist)) / | |
| stanDev(distVariance(dist)) | |
| skewMod(data) // get -0.2558 | |
| distSkewMod(distFreq(data)) // get 0.1846 | |
| /*---------------------------------------------------------------------------------------*/ | |
| skewMed = array => | |
| (mean(array) - median(array)) | |
| * 3 / stanDev(variance(array)) | |
| distSkewMed = dist => | |
| (distMean(dist) - distMedian(distCumulative(dist))) | |
| * 3 / stanDev(distVariance(dist)) | |
| skewMed(data) // get 0.0622 | |
| distSkewMed(distFreq(data)) // get -0.1025 | |
| /*---------------------------------------------------------------------------------------*/ | |
| skewBow = (first, second, third) => | |
| ((third - second) - (second - first)) / | |
| ((third - second) + (second - first)), | |
| skewBow( | |
| fractile(4, 1, data), | |
| fractile(4, 2, data), | |
| fractile(4, 3, data) | |
| ) // get 0 | |
| skewBow( | |
| distFractile(4, 1, distCumulative(distFreq(data))), | |
| distFractile(4, 2, distCumulative(distFreq(data))), | |
| distFractile(4, 3, distCumulative(distFreq(data))), | |
| ) // get -0.0358 | |
| /*---------------------------------------------------------------------------------------*/ | |
| skewMom = array => withThis( | |
| mean(array), meanVal => add( | |
| array.map(i => i - meanVal) | |
| .map(Math.abs).map(pow(3)) | |
| ) / array.length | |
| / pow(3)(stanDev(variance(array))) | |
| ) | |
| distSkewMom = dist => withThis( | |
| { | |
| meanVal: distMean(dist), | |
| midVal: i => i.bot + ((i.top - i.bot) / 2) | |
| }, ({meanVal, midVal}) => add( | |
| dist.map(i => i.fre * pow(3)( | |
| midVal(i) - meanVal | |
| )) | |
| ) / distLength(dist) | |
| / pow(3)(stanDev(distVariance(dist))) | |
| ) | |
| /*---------------------------------------------------------------------------------------*/ | |
| kurtMom = array => withThis( | |
| mean(array), meanVal => add( | |
| array.map(i => i - meanVal) | |
| .map(Math.abs).map(pow(4)) | |
| ) / array.length | |
| / pow(4)(stanDev(variance(array))) | |
| ) | |
| distKurtMom = dist => withThis( | |
| { | |
| meanVal: distMean(dist), | |
| midVal: i => i.bot + ((i.top - i.bot) / 2) | |
| }, ({meanVal, midVal}) => add( | |
| dist.map(i => i.fre * pow(4)( | |
| midVal(i) - meanVal | |
| )) | |
| ) / distLength(dist) | |
| / pow(4)(stanDev(distVariance(dist))) | |
| ) | |
| kurtMom(data) // get 3.1558 | |
| distKurtMom(distFreq(data)) // get 2.7454 | |
| /*---------------------------------------------------------------------------------------*/ | |
| kurtPer = (q1, q3, p10, p90) => | |
| ((q3 - q1) / 2) / (p90 - p10) | |
| arr1 = [ | |
| fractile(4, 1, data), | |
| fractile(4, 3, data), | |
| fractile(100, 10, data), | |
| fractile(100, 90, data) | |
| ] // get [71, 75, 68.1, 78.9] | |
| arr2 = [ | |
| distFractile(4, 1, distCumulative(distFreq(data))), | |
| distFractile(4, 3, distCumulative(distFreq(data))), | |
| distFractile(100, 10, distCumulative(distFreq(data))), | |
| distFractile(100, 90, distCumulative(distFreq(data))) | |
| ] // get [70.75, 75.57, 68, 78] | |
| kurtPer(...arr1) // get 0.1852 | |
| kurtPer(...arr2) // get 0.2413 | |
| /*---------------------------------------------------------------------------------------*/ | |
| zConvert = array => withThis( | |
| {m: mean(array), sd: stanDev(variance(array))}, | |
| ({m, sd}) => array.map(i => (i - m) / sd) | |
| ) | |
| zConvert([5, 4, 8, 7, 1]) // get [0, -0.365, 1.095, 0.73, -1.46] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment