123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- 'use strict';
- function isArray(x) {
- return Array.isArray(x) || ArrayBuffer.isView(x) && !(x instanceof DataView);
- }
- function Summary(data, sorted) {
- if (!(this instanceof Summary)) return new Summary(data, sorted);
- if (!isArray(data)) {
- throw TypeError('data must be an array');
- }
- this._data = data;
- this._sorted = sorted ? data : null;
- this._length = data.length;
- this._cache_sum = null;
- this._cache_mode = null;
- this._cache_mean = null;
- this._cache_quartiles = {};
- this._cache_variance = null;
- this._cache_sd = null;
- this._cache_max = null;
- this._cache_min = null;
- }
- module.exports = Summary;
- //
- // Not all values are in lazy calculated since that wouldn't do any good
- //
- Summary.prototype.data = function() {
- return this._data;
- };
- Summary.prototype.sort = function() {
- if (this._sorted === null) {
- this._sorted = this._data.slice(0).sort(function (a, b) { return a - b; });
- }
- return this._sorted;
- };
- Summary.prototype.size = function () {
- return this._length;
- };
- //
- // Always lazy calculated functions
- //
- Summary.prototype.sum = function () {
- if (this._cache_sum === null) {
- // Numerically stable sum
- // https://en.m.wikipedia.org/wiki/Pairwise_summation
- const partials = [];
- for (let i = 0; i < this._length; i++) {
- partials.push(this._data[i]);
- for (let j = i; j % 2 == 1; j = j >> 1) {
- const p = partials.pop();
- const q = partials.pop();
- partials.push(p + q);
- }
- }
- let total = 0.0;
- for (let i = 0; i < partials.length; i++) {
- total += partials[i];
- }
- this._cache_sum = total;
- }
- return this._cache_sum;
- };
- Summary.prototype.mode = function () {
- if (this._cache_mode === null) {
- const data = this.sort();
- let modeValue = NaN;
- let modeCount = 0;
- let currValue = data[0];
- let currCount = 1;
- // Count the amount of repeat and update mode variables
- for (let i = 1; i < this._length; i++) {
- if (data[i] === currValue) {
- currCount += 1;
- } else {
- if (currCount >= modeCount) {
- modeCount = currCount;
- modeValue = currValue;
- }
- currValue = data[i];
- currCount = 1;
- }
- }
- // Check the last count
- if (currCount >= modeCount) {
- modeCount = currCount;
- modeValue = currValue;
- }
- this._cache_mode = modeValue;
- }
- return this._cache_mode;
- };
- Summary.prototype.mean = function () {
- if (this._cache_mean === null) {
- // Numerically stable mean algorithm
- let mean = 0;
- for (let i = 0; i < this._length; i++) {
- mean += (this._data[i] - mean) / (i+1);
- }
- this._cache_mean = mean;
- }
- return this._cache_mean;
- };
- Summary.prototype.quartile = function (prob) {
- if (!this._cache_quartiles.hasOwnProperty(prob)) {
- const data = this.sort();
- const product = prob * this.size();
- const ceil = Math.ceil(product);
- if (ceil === product) {
- if (ceil === 0) {
- this._cache_quartiles[prob] = data[0];
- } else if (ceil === data.length) {
- this._cache_quartiles[prob] = data[data.length - 1];
- } else {
- this._cache_quartiles[prob] = (data[ceil - 1] + data[ceil]) / 2;
- }
- } else {
- this._cache_quartiles[prob] = data[ceil - 1];
- }
- }
- return this._cache_quartiles[prob];
- };
- Summary.prototype.median = function () {
- return this.quartile(0.5);
- };
- Summary.prototype.variance = function () {
- if (this._cache_variance === null) {
- // Numerically stable variance algorithm
- const mean = this.mean();
- let biasedVariance = 0;
- for (let i = 0; i < this._length; i++) {
- const diff = this._data[i] - mean;
- biasedVariance += (diff * diff - biasedVariance) / (i+1);
- }
- // Debias the variance
- const debiasTerm = ((this._length) / (this._length - 1));
- this._cache_variance = biasedVariance * debiasTerm;
- }
- return this._cache_variance;
- };
- Summary.prototype.sd = function () {
- if (this._cache_sd === null) {
- this._cache_sd = Math.sqrt(this.variance());
- }
- return this._cache_sd;
- };
- Summary.prototype.max = function () {
- if (this._cache_max === null) {
- this._cache_max = this.sort()[this._length - 1];
- }
- return this._cache_max;
- };
- Summary.prototype.min = function () {
- if (this._cache_min === null) {
- this._cache_min = this.sort()[0];
- }
- return this._cache_min;
- };
|