calc-stats
Advanced tools
Comparing version 2.0.0 to 2.1.0
const { getOrCreateIterator } = require("iter-fun"); | ||
const fasterMedian = require("faster-median"); | ||
const { add, compare, divide, mean, multiply, pow, sort, subtract, sum } = require("preciso"); | ||
const mediana = require("mediana"); | ||
const computeVariance = ({ count, histogram, mean }) => { | ||
return ( | ||
Object.values(histogram).reduce((sum, { n, ct }) => { | ||
return sum + ct * Math.pow(n - mean, 2); | ||
}, 0) / count | ||
); | ||
const computeVariance = ({ count, histogram, mean_value, precise = false }) => { | ||
if (precise) { | ||
mean_value = mean_value.toString(); | ||
const reduced = Object.values(histogram).reduce((sum, { n, ct }) => { | ||
const diff = subtract(n.toString(), mean_value); | ||
return add(sum, multiply(ct.toString(), pow(diff, "2"))); | ||
}, "0"); | ||
return divide(reduced, count.toString()); | ||
} else { | ||
return ( | ||
Object.values(histogram).reduce((sum, { n, ct }) => { | ||
return sum + ct * Math.pow(n - mean_value, 2); | ||
}, 0) / count | ||
); | ||
} | ||
}; | ||
@@ -33,2 +43,4 @@ | ||
calcUniques = true, | ||
precise = false, | ||
precise_max_decimal_digits = 100, | ||
stats | ||
@@ -88,3 +100,2 @@ } = { debugLevel: 0 } | ||
let needMax = calcMax || calcRange; | ||
let valid = 0; | ||
@@ -95,16 +106,31 @@ let invalid = 0; | ||
let max; | ||
let sum = 0; | ||
let sum = precise ? "0" : 0; | ||
const histogram = {}; | ||
// after it processes filtering | ||
const process = value => { | ||
if (needValid) valid++; | ||
if (needMin && (min === undefined || value < min)) min = value; | ||
if (needMax && (max === undefined || value > max)) max = value; | ||
if (needSum) sum += value; | ||
if (needHistogram) { | ||
if (value in histogram) histogram[value].ct++; | ||
else histogram[value] = { n: value, ct: 1 }; | ||
} | ||
}; | ||
let process; | ||
if (precise) { | ||
process = value => { | ||
value = value.toString(); | ||
if (needValid) valid++; | ||
if (needMin && (min === undefined || compare(value, min) === "<")) min = value; | ||
if (needMax && (max === undefined || compare(value, max) === ">")) max = value; | ||
if (needSum) sum = add(sum, value); | ||
if (needHistogram) { | ||
if (value in histogram) histogram[value].ct++; | ||
else histogram[value] = { n: value.toString(), ct: 1 }; | ||
} | ||
}; | ||
} else { | ||
process = value => { | ||
if (needValid) valid++; | ||
if (needMin && (min === undefined || value < min)) min = value; | ||
if (needMax && (max === undefined || value > max)) max = value; | ||
if (needSum) sum += value; | ||
if (needHistogram) { | ||
if (value in histogram) histogram[value].ct++; | ||
else histogram[value] = { n: value, ct: 1 }; | ||
} | ||
}; | ||
} | ||
@@ -150,20 +176,31 @@ let step; | ||
const results = {}; | ||
if (calcCount) results.count = invalid + valid; | ||
if (calcValid) results.valid = valid; | ||
if (calcInvalid) results.invalid = invalid; | ||
if (calcMedian) results.median = fasterMedian({ counts: histogram, total: valid }); | ||
if (calcMin) results.min = min; | ||
if (calcMax) results.max = max; | ||
if (calcSum) results.sum = sum; | ||
if (calcRange) results.range = max - min; | ||
if (calcCount) results.count = precise ? add(invalid.toString(), valid.toString()) : invalid + valid; | ||
if (calcValid) results.valid = precise ? valid.toString() : valid; | ||
if (calcInvalid) results.invalid = precise ? invalid.toString() : invalid; | ||
if (calcMedian) { | ||
results.median = mediana.calculate({ counts: histogram, precise, total: valid }); | ||
} | ||
if (calcMin) results.min = min; // should already be a string if precise | ||
if (calcMax) results.max = max; // should already be a string if precise | ||
if (calcSum) results.sum = sum; // should already be a string if precise | ||
if (calcRange) results.range = precise ? subtract(max.toString(), min.toString()) : max - min; | ||
if (calcMean || calcVariance || calcStd) { | ||
const mean = sum / valid; | ||
if (calcMean) results.mean = mean; | ||
const mean_value = precise | ||
? divide(sum, valid.toString(), { max_decimal_digits: precise_max_decimal_digits }) | ||
: sum / valid; | ||
if (calcMean) results.mean = mean_value; | ||
if (calcVariance || calcStd) { | ||
const variance = computeVariance({ count: valid, histogram, mean }); | ||
const variance = computeVariance({ count: valid, histogram, mean_value, precise }); | ||
if (calcVariance) results.variance = variance; | ||
if (calcStd) results.std = Math.sqrt(variance); | ||
if (calcStd) results.std = precise ? Math.sqrt(Number(variance)).toString() : Math.sqrt(variance); | ||
} | ||
} | ||
if (calcHistogram) results.histogram = histogram; | ||
if (calcHistogram) { | ||
if (precise) { | ||
Object.values(histogram).forEach(obj => { | ||
obj.ct = obj.ct.toString(); | ||
}); | ||
} | ||
results.histogram = histogram; | ||
} | ||
if (calcMode || calcModes) { | ||
@@ -175,6 +212,6 @@ let highest_count = 0; | ||
if (ct === highest_count) { | ||
modes.push(n); | ||
modes.push(precise ? n.toString() : n); | ||
} else if (ct > highest_count) { | ||
highest_count = ct; | ||
modes = [n]; | ||
modes = [precise ? n.toString() : n]; | ||
} | ||
@@ -186,8 +223,15 @@ } | ||
// compute mean value of all the most popular numbers | ||
if (calcMode) results.mode = modes.reduce((acc, n) => acc + n, 0) / modes.length; | ||
if (calcMode) { | ||
results.mode = precise ? mean(modes) : modes.reduce((acc, n) => acc + n, 0) / modes.length; | ||
} | ||
} | ||
if (calcUniques) | ||
results.uniques = Object.values(histogram) | ||
.map(({ n }) => n) | ||
.sort((a, b) => a - b); | ||
if (calcUniques) { | ||
if (precise) { | ||
results.uniques = sort(Object.keys(histogram)); | ||
} else { | ||
results.uniques = Object.values(histogram) | ||
.map(({ n }) => n) | ||
.sort((a, b) => a - b); | ||
} | ||
} | ||
@@ -208,2 +252,20 @@ return results; | ||
module.exports = calcStats; | ||
if (typeof define === "function" && define.amd) { | ||
define(function () { | ||
return calcStats; | ||
}); | ||
} | ||
if (typeof module === "object") { | ||
module.exports = calcStats; | ||
module.exports.default = calcStats; | ||
module.exports.calcStats = calcStats; | ||
} | ||
if (typeof self === "object") { | ||
self.calcStats = calcStats; | ||
} | ||
if (typeof window === "object") { | ||
window.calcStats = calcStats; | ||
} |
{ | ||
"name": "calc-stats", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "Memory-Aware Statistical Calculations", | ||
"main": "calc-stats.js", | ||
"types": "calc-stats.d.ts", | ||
"files": [ | ||
"calc-stats.d.ts", | ||
"calc-stats.js" | ||
], | ||
"scripts": { | ||
"format": "npx prettier --arrow-parens=avoid --print-width=120 --trailing-comma=none --write *.js", | ||
"test": "node test.js" | ||
"f": "npm run format", | ||
"format": "npx prettier --arrow-parens=avoid --print-width=120 --trailing-comma=none --write *.js *.ts", | ||
"test": "node test.js", | ||
"test:ts": "npx ts-node test.ts" | ||
}, | ||
@@ -36,10 +40,11 @@ "repository": { | ||
"homepage": "https://github.com/DanielJDufour/calc-stats#readme", | ||
"devDependencies": { | ||
"flug": "^1.1.0" | ||
}, | ||
"dependencies": { | ||
"faster-median": "^0.2.0", | ||
"iter-fun": "^0.1.2", | ||
"mediana": "^1.0.3", | ||
"preciso": "^0.7.0", | ||
"quick-resolve": "^0.0.1" | ||
}, | ||
"devDependencies": { | ||
"flug": "^2.3.1" | ||
} | ||
} |
@@ -97,1 +97,11 @@ # calc-stats | ||
``` | ||
## precision | ||
If you want super precise calculations avoiding floating-point arithmetic issues, | ||
set precise to true. Numerical results will be returned as strings to preserve precision. | ||
Precise calculations are performed by [preciso](https://github.com/danieljdufour/preciso). | ||
```js | ||
import calcStats from "calc-stats"; | ||
const results = calcStats(data, { precise: true }); | ||
``` |
22253
5
360
107
4
+ Addedmediana@^1.0.3
+ Addedpreciso@^0.7.0
+ Addedmediana@1.0.3(transitive)
+ Addedpreciso@0.5.00.7.1(transitive)
- Removedfaster-median@^0.2.0
- Removedfaster-median@0.2.1(transitive)