UNPKG

@decidables/detectable-math

Version:

detectable-math: Equations for calculating Signal Detection Theory

1,400 lines (1,282 loc) 161 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.detectableMath = factory()); })(this, (function () { 'use strict'; function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var jstat$1 = {exports: {}}; var jstat = jstat$1.exports; (function (module, exports) { (function (window, factory) { { module.exports = factory(); } })(jstat, function () { var jStat = function (Math, undefined$1) { // For quick reference. var concat = Array.prototype.concat; var slice = Array.prototype.slice; var toString = Object.prototype.toString; // Calculate correction for IEEE error // TODO: This calculation can be improved. function calcRdx(n, m) { var val = n > m ? n : m; return Math.pow(10, 17 - ~~(Math.log(val > 0 ? val : -val) * Math.LOG10E)); } var isArray = Array.isArray || function isArray(arg) { return toString.call(arg) === '[object Array]'; }; function isFunction(arg) { return toString.call(arg) === '[object Function]'; } function isNumber(num) { return typeof num === 'number' ? num - num === 0 : false; } // Converts the jStat matrix to vector. function toVector(arr) { return concat.apply([], arr); } // The one and only jStat constructor. function jStat() { return new jStat._init(arguments); } // TODO: Remove after all references in src files have been removed. jStat.fn = jStat.prototype; // By separating the initializer from the constructor it's easier to handle // always returning a new instance whether "new" was used or not. jStat._init = function _init(args) { // If first argument is an array, must be vector or matrix. if (isArray(args[0])) { // Check if matrix. if (isArray(args[0][0])) { // See if a mapping function was also passed. if (isFunction(args[1])) args[0] = jStat.map(args[0], args[1]); // Iterate over each is faster than this.push.apply(this, args[0]. for (var i = 0; i < args[0].length; i++) this[i] = args[0][i]; this.length = args[0].length; // Otherwise must be a vector. } else { this[0] = isFunction(args[1]) ? jStat.map(args[0], args[1]) : args[0]; this.length = 1; } // If first argument is number, assume creation of sequence. } else if (isNumber(args[0])) { this[0] = jStat.seq.apply(null, args); this.length = 1; // Handle case when jStat object is passed to jStat. } else if (args[0] instanceof jStat) { // Duplicate the object and pass it back. return jStat(args[0].toArray()); // Unexpected argument value, return empty jStat object. // TODO: This is strange behavior. Shouldn't this throw or some such to let // the user know they had bad arguments? } else { this[0] = []; this.length = 1; } return this; }; jStat._init.prototype = jStat.prototype; jStat._init.constructor = jStat; // Utility functions. // TODO: for internal use only? jStat.utils = { calcRdx: calcRdx, isArray: isArray, isFunction: isFunction, isNumber: isNumber, toVector: toVector }; jStat._random_fn = Math.random; jStat.setRandom = function setRandom(fn) { if (typeof fn !== 'function') throw new TypeError('fn is not a function'); jStat._random_fn = fn; }; // Easily extend the jStat object. // TODO: is this seriously necessary? jStat.extend = function extend(obj) { var i, j; if (arguments.length === 1) { for (j in obj) jStat[j] = obj[j]; return this; } for (i = 1; i < arguments.length; i++) { for (j in arguments[i]) obj[j] = arguments[i][j]; } return obj; }; // Returns the number of rows in the matrix. jStat.rows = function rows(arr) { return arr.length || 1; }; // Returns the number of columns in the matrix. jStat.cols = function cols(arr) { return arr[0].length || 1; }; // Returns the dimensions of the object { rows: i, cols: j } jStat.dimensions = function dimensions(arr) { return { rows: jStat.rows(arr), cols: jStat.cols(arr) }; }; // Returns a specified row as a vector or return a sub matrix by pick some rows jStat.row = function row(arr, index) { if (isArray(index)) { return index.map(function (i) { return jStat.row(arr, i); }); } return arr[index]; }; // return row as array // rowa([[1,2],[3,4]],0) -> [1,2] jStat.rowa = function rowa(arr, i) { return jStat.row(arr, i); }; // Returns the specified column as a vector or return a sub matrix by pick some // columns jStat.col = function col(arr, index) { if (isArray(index)) { var submat = jStat.arange(arr.length).map(function () { return new Array(index.length); }); index.forEach(function (ind, i) { jStat.arange(arr.length).forEach(function (j) { submat[j][i] = arr[j][ind]; }); }); return submat; } var column = new Array(arr.length); for (var i = 0; i < arr.length; i++) column[i] = [arr[i][index]]; return column; }; // return column as array // cola([[1,2],[3,4]],0) -> [1,3] jStat.cola = function cola(arr, i) { return jStat.col(arr, i).map(function (a) { return a[0]; }); }; // Returns the diagonal of the matrix jStat.diag = function diag(arr) { var nrow = jStat.rows(arr); var res = new Array(nrow); for (var row = 0; row < nrow; row++) res[row] = [arr[row][row]]; return res; }; // Returns the anti-diagonal of the matrix jStat.antidiag = function antidiag(arr) { var nrow = jStat.rows(arr) - 1; var res = new Array(nrow); for (var i = 0; nrow >= 0; nrow--, i++) res[i] = [arr[i][nrow]]; return res; }; // Transpose a matrix or array. jStat.transpose = function transpose(arr) { var obj = []; var objArr, rows, cols, j, i; // Make sure arr is in matrix format. if (!isArray(arr[0])) arr = [arr]; rows = arr.length; cols = arr[0].length; for (i = 0; i < cols; i++) { objArr = new Array(rows); for (j = 0; j < rows; j++) objArr[j] = arr[j][i]; obj.push(objArr); } // If obj is vector, return only single array. return obj.length === 1 ? obj[0] : obj; }; // Map a function to an array or array of arrays. // "toAlter" is an internal variable. jStat.map = function map(arr, func, toAlter) { var row, nrow, ncol, res, col; if (!isArray(arr[0])) arr = [arr]; nrow = arr.length; ncol = arr[0].length; res = toAlter ? arr : new Array(nrow); for (row = 0; row < nrow; row++) { // if the row doesn't exist, create it if (!res[row]) res[row] = new Array(ncol); for (col = 0; col < ncol; col++) res[row][col] = func(arr[row][col], row, col); } return res.length === 1 ? res[0] : res; }; // Cumulatively combine the elements of an array or array of arrays using a function. jStat.cumreduce = function cumreduce(arr, func, toAlter) { var row, nrow, ncol, res, col; if (!isArray(arr[0])) arr = [arr]; nrow = arr.length; ncol = arr[0].length; res = toAlter ? arr : new Array(nrow); for (row = 0; row < nrow; row++) { // if the row doesn't exist, create it if (!res[row]) res[row] = new Array(ncol); if (ncol > 0) res[row][0] = arr[row][0]; for (col = 1; col < ncol; col++) res[row][col] = func(res[row][col - 1], arr[row][col]); } return res.length === 1 ? res[0] : res; }; // Destructively alter an array. jStat.alter = function alter(arr, func) { return jStat.map(arr, func, true); }; // Generate a rows x cols matrix according to the supplied function. jStat.create = function create(rows, cols, func) { var res = new Array(rows); var i, j; if (isFunction(cols)) { func = cols; cols = rows; } for (i = 0; i < rows; i++) { res[i] = new Array(cols); for (j = 0; j < cols; j++) res[i][j] = func(i, j); } return res; }; function retZero() { return 0; } // Generate a rows x cols matrix of zeros. jStat.zeros = function zeros(rows, cols) { if (!isNumber(cols)) cols = rows; return jStat.create(rows, cols, retZero); }; function retOne() { return 1; } // Generate a rows x cols matrix of ones. jStat.ones = function ones(rows, cols) { if (!isNumber(cols)) cols = rows; return jStat.create(rows, cols, retOne); }; // Generate a rows x cols matrix of uniformly random numbers. jStat.rand = function rand(rows, cols) { if (!isNumber(cols)) cols = rows; return jStat.create(rows, cols, jStat._random_fn); }; function retIdent(i, j) { return i === j ? 1 : 0; } // Generate an identity matrix of size row x cols. jStat.identity = function identity(rows, cols) { if (!isNumber(cols)) cols = rows; return jStat.create(rows, cols, retIdent); }; // Tests whether a matrix is symmetric jStat.symmetric = function symmetric(arr) { var size = arr.length; var row, col; if (arr.length !== arr[0].length) return false; for (row = 0; row < size; row++) { for (col = 0; col < size; col++) if (arr[col][row] !== arr[row][col]) return false; } return true; }; // Set all values to zero. jStat.clear = function clear(arr) { return jStat.alter(arr, retZero); }; // Generate sequence. jStat.seq = function seq(min, max, length, func) { if (!isFunction(func)) func = false; var arr = []; var hival = calcRdx(min, max); var step = (max * hival - min * hival) / ((length - 1) * hival); var current = min; var cnt; // Current is assigned using a technique to compensate for IEEE error. // TODO: Needs better implementation. for (cnt = 0; current <= max && cnt < length; cnt++, current = (min * hival + step * hival * cnt) / hival) { arr.push(func ? func(current, cnt) : current); } return arr; }; // arange(5) -> [0,1,2,3,4] // arange(1,5) -> [1,2,3,4] // arange(5,1,-1) -> [5,4,3,2] jStat.arange = function arange(start, end, step) { var rl = []; var i; step = step || 1; if (end === undefined$1) { end = start; start = 0; } if (start === end || step === 0) { return []; } if (start < end && step < 0) { return []; } if (start > end && step > 0) { return []; } if (step > 0) { for (i = start; i < end; i += step) { rl.push(i); } } else { for (i = start; i > end; i += step) { rl.push(i); } } return rl; }; // A=[[1,2,3],[4,5,6],[7,8,9]] // slice(A,{row:{end:2},col:{start:1}}) -> [[2,3],[5,6]] // slice(A,1,{start:1}) -> [5,6] // as numpy code A[:2,1:] jStat.slice = function () { function _slice(list, start, end, step) { // note it's not equal to range.map mode it's a bug var i; var rl = []; var length = list.length; if (start === undefined$1 && end === undefined$1 && step === undefined$1) { return jStat.copy(list); } start = start || 0; end = end || list.length; start = start >= 0 ? start : length + start; end = end >= 0 ? end : length + end; step = step || 1; if (start === end || step === 0) { return []; } if (start < end && step < 0) { return []; } if (start > end && step > 0) { return []; } if (step > 0) { for (i = start; i < end; i += step) { rl.push(list[i]); } } else { for (i = start; i > end; i += step) { rl.push(list[i]); } } return rl; } function slice(list, rcSlice) { var colSlice, rowSlice; rcSlice = rcSlice || {}; if (isNumber(rcSlice.row)) { if (isNumber(rcSlice.col)) return list[rcSlice.row][rcSlice.col]; var row = jStat.rowa(list, rcSlice.row); colSlice = rcSlice.col || {}; return _slice(row, colSlice.start, colSlice.end, colSlice.step); } if (isNumber(rcSlice.col)) { var col = jStat.cola(list, rcSlice.col); rowSlice = rcSlice.row || {}; return _slice(col, rowSlice.start, rowSlice.end, rowSlice.step); } rowSlice = rcSlice.row || {}; colSlice = rcSlice.col || {}; var rows = _slice(list, rowSlice.start, rowSlice.end, rowSlice.step); return rows.map(function (row) { return _slice(row, colSlice.start, colSlice.end, colSlice.step); }); } return slice; }(); // A=[[1,2,3],[4,5,6],[7,8,9]] // sliceAssign(A,{row:{start:1},col:{start:1}},[[0,0],[0,0]]) // A=[[1,2,3],[4,0,0],[7,0,0]] jStat.sliceAssign = function sliceAssign(A, rcSlice, B) { var nl, ml; if (isNumber(rcSlice.row)) { if (isNumber(rcSlice.col)) return A[rcSlice.row][rcSlice.col] = B; rcSlice.col = rcSlice.col || {}; rcSlice.col.start = rcSlice.col.start || 0; rcSlice.col.end = rcSlice.col.end || A[0].length; rcSlice.col.step = rcSlice.col.step || 1; nl = jStat.arange(rcSlice.col.start, Math.min(A.length, rcSlice.col.end), rcSlice.col.step); var m = rcSlice.row; nl.forEach(function (n, i) { A[m][n] = B[i]; }); return A; } if (isNumber(rcSlice.col)) { rcSlice.row = rcSlice.row || {}; rcSlice.row.start = rcSlice.row.start || 0; rcSlice.row.end = rcSlice.row.end || A.length; rcSlice.row.step = rcSlice.row.step || 1; ml = jStat.arange(rcSlice.row.start, Math.min(A[0].length, rcSlice.row.end), rcSlice.row.step); var n = rcSlice.col; ml.forEach(function (m, j) { A[m][n] = B[j]; }); return A; } if (B[0].length === undefined$1) { B = [B]; } rcSlice.row.start = rcSlice.row.start || 0; rcSlice.row.end = rcSlice.row.end || A.length; rcSlice.row.step = rcSlice.row.step || 1; rcSlice.col.start = rcSlice.col.start || 0; rcSlice.col.end = rcSlice.col.end || A[0].length; rcSlice.col.step = rcSlice.col.step || 1; ml = jStat.arange(rcSlice.row.start, Math.min(A.length, rcSlice.row.end), rcSlice.row.step); nl = jStat.arange(rcSlice.col.start, Math.min(A[0].length, rcSlice.col.end), rcSlice.col.step); ml.forEach(function (m, i) { nl.forEach(function (n, j) { A[m][n] = B[i][j]; }); }); return A; }; // [1,2,3] -> // [[1,0,0],[0,2,0],[0,0,3]] jStat.diagonal = function diagonal(diagArray) { var mat = jStat.zeros(diagArray.length, diagArray.length); diagArray.forEach(function (t, i) { mat[i][i] = t; }); return mat; }; // return copy of A jStat.copy = function copy(A) { return A.map(function (row) { if (isNumber(row)) return row; return row.map(function (t) { return t; }); }); }; // TODO: Go over this entire implementation. Seems a tragic waste of resources // doing all this work. Instead, and while ugly, use new Function() to generate // a custom function for each static method. // Quick reference. var jProto = jStat.prototype; // Default length. jProto.length = 0; // For internal use only. // TODO: Check if they're actually used, and if they are then rename them // to _* jProto.push = Array.prototype.push; jProto.sort = Array.prototype.sort; jProto.splice = Array.prototype.splice; jProto.slice = Array.prototype.slice; // Return a clean array. jProto.toArray = function toArray() { return this.length > 1 ? slice.call(this) : slice.call(this)[0]; }; // Map a function to a matrix or vector. jProto.map = function map(func, toAlter) { return jStat(jStat.map(this, func, toAlter)); }; // Cumulatively combine the elements of a matrix or vector using a function. jProto.cumreduce = function cumreduce(func, toAlter) { return jStat(jStat.cumreduce(this, func, toAlter)); }; // Destructively alter an array. jProto.alter = function alter(func) { jStat.alter(this, func); return this; }; // Extend prototype with methods that have no argument. (function (funcs) { for (var i = 0; i < funcs.length; i++) (function (passfunc) { jProto[passfunc] = function (func) { var self = this, results; // Check for callback. if (func) { setTimeout(function () { func.call(self, jProto[passfunc].call(self)); }); return this; } results = jStat[passfunc](this); return isArray(results) ? jStat(results) : results; }; })(funcs[i]); })('transpose clear symmetric rows cols dimensions diag antidiag'.split(' ')); // Extend prototype with methods that have one argument. (function (funcs) { for (var i = 0; i < funcs.length; i++) (function (passfunc) { jProto[passfunc] = function (index, func) { var self = this; // check for callback if (func) { setTimeout(function () { func.call(self, jProto[passfunc].call(self, index)); }); return this; } return jStat(jStat[passfunc](this, index)); }; })(funcs[i]); })('row col'.split(' ')); // Extend prototype with simple shortcut methods. (function (funcs) { for (var i = 0; i < funcs.length; i++) (function (passfunc) { jProto[passfunc] = function () { return jStat(jStat[passfunc].apply(null, arguments)); }; })(funcs[i]); })('create zeros ones rand identity'.split(' ')); // Exposing jStat. return jStat; }(Math); (function (jStat, Math) { var isFunction = jStat.utils.isFunction; // Ascending functions for sort function ascNum(a, b) { return a - b; } function clip(arg, min, max) { return Math.max(min, Math.min(arg, max)); } // sum of an array jStat.sum = function sum(arr) { var sum = 0; var i = arr.length; while (--i >= 0) sum += arr[i]; return sum; }; // sum squared jStat.sumsqrd = function sumsqrd(arr) { var sum = 0; var i = arr.length; while (--i >= 0) sum += arr[i] * arr[i]; return sum; }; // sum of squared errors of prediction (SSE) jStat.sumsqerr = function sumsqerr(arr) { var mean = jStat.mean(arr); var sum = 0; var i = arr.length; var tmp; while (--i >= 0) { tmp = arr[i] - mean; sum += tmp * tmp; } return sum; }; // sum of an array in each row jStat.sumrow = function sumrow(arr) { var sum = 0; var i = arr.length; while (--i >= 0) sum += arr[i]; return sum; }; // product of an array jStat.product = function product(arr) { var prod = 1; var i = arr.length; while (--i >= 0) prod *= arr[i]; return prod; }; // minimum value of an array jStat.min = function min(arr) { var low = arr[0]; var i = 0; while (++i < arr.length) if (arr[i] < low) low = arr[i]; return low; }; // maximum value of an array jStat.max = function max(arr) { var high = arr[0]; var i = 0; while (++i < arr.length) if (arr[i] > high) high = arr[i]; return high; }; // unique values of an array jStat.unique = function unique(arr) { var hash = {}, _arr = []; for (var i = 0; i < arr.length; i++) { if (!hash[arr[i]]) { hash[arr[i]] = true; _arr.push(arr[i]); } } return _arr; }; // mean value of an array jStat.mean = function mean(arr) { return jStat.sum(arr) / arr.length; }; // mean squared error (MSE) jStat.meansqerr = function meansqerr(arr) { return jStat.sumsqerr(arr) / arr.length; }; // geometric mean of an array jStat.geomean = function geomean(arr) { var logs = arr.map(Math.log); var meanOfLogs = jStat.mean(logs); return Math.exp(meanOfLogs); }; // median of an array jStat.median = function median(arr) { var arrlen = arr.length; var _arr = arr.slice().sort(ascNum); // check if array is even or odd, then return the appropriate return !(arrlen & 1) ? (_arr[arrlen / 2 - 1] + _arr[arrlen / 2]) / 2 : _arr[arrlen / 2 | 0]; }; // cumulative sum of an array jStat.cumsum = function cumsum(arr) { return jStat.cumreduce(arr, function (a, b) { return a + b; }); }; // cumulative product of an array jStat.cumprod = function cumprod(arr) { return jStat.cumreduce(arr, function (a, b) { return a * b; }); }; // successive differences of a sequence jStat.diff = function diff(arr) { var diffs = []; var arrLen = arr.length; var i; for (i = 1; i < arrLen; i++) diffs.push(arr[i] - arr[i - 1]); return diffs; }; // ranks of an array jStat.rank = function (arr) { var i; var distinctNumbers = []; var numberCounts = {}; for (i = 0; i < arr.length; i++) { var number = arr[i]; if (numberCounts[number]) { numberCounts[number]++; } else { numberCounts[number] = 1; distinctNumbers.push(number); } } var sortedDistinctNumbers = distinctNumbers.sort(ascNum); var numberRanks = {}; var currentRank = 1; for (i = 0; i < sortedDistinctNumbers.length; i++) { var number = sortedDistinctNumbers[i]; var count = numberCounts[number]; var first = currentRank; var last = currentRank + count - 1; var rank = (first + last) / 2; numberRanks[number] = rank; currentRank += count; } return arr.map(function (number) { return numberRanks[number]; }); }; // mode of an array // if there are multiple modes of an array, return all of them // is this the appropriate way of handling it? jStat.mode = function mode(arr) { var arrLen = arr.length; var _arr = arr.slice().sort(ascNum); var count = 1; var maxCount = 0; var numMaxCount = 0; var mode_arr = []; var i; for (i = 0; i < arrLen; i++) { if (_arr[i] === _arr[i + 1]) { count++; } else { if (count > maxCount) { mode_arr = [_arr[i]]; maxCount = count; numMaxCount = 0; } // are there multiple max counts else if (count === maxCount) { mode_arr.push(_arr[i]); numMaxCount++; } // resetting count for new value in array count = 1; } } return numMaxCount === 0 ? mode_arr[0] : mode_arr; }; // range of an array jStat.range = function range(arr) { return jStat.max(arr) - jStat.min(arr); }; // variance of an array // flag = true indicates sample instead of population jStat.variance = function variance(arr, flag) { return jStat.sumsqerr(arr) / (arr.length - (flag ? 1 : 0)); }; // pooled variance of an array of arrays jStat.pooledvariance = function pooledvariance(arr) { var sumsqerr = arr.reduce(function (a, samples) { return a + jStat.sumsqerr(samples); }, 0); var count = arr.reduce(function (a, samples) { return a + samples.length; }, 0); return sumsqerr / (count - arr.length); }; // deviation of an array jStat.deviation = function (arr) { var mean = jStat.mean(arr); var arrlen = arr.length; var dev = new Array(arrlen); for (var i = 0; i < arrlen; i++) { dev[i] = arr[i] - mean; } return dev; }; // standard deviation of an array // flag = true indicates sample instead of population jStat.stdev = function stdev(arr, flag) { return Math.sqrt(jStat.variance(arr, flag)); }; // pooled standard deviation of an array of arrays jStat.pooledstdev = function pooledstdev(arr) { return Math.sqrt(jStat.pooledvariance(arr)); }; // mean deviation (mean absolute deviation) of an array jStat.meandev = function meandev(arr) { var mean = jStat.mean(arr); var a = []; for (var i = arr.length - 1; i >= 0; i--) { a.push(Math.abs(arr[i] - mean)); } return jStat.mean(a); }; // median deviation (median absolute deviation) of an array jStat.meddev = function meddev(arr) { var median = jStat.median(arr); var a = []; for (var i = arr.length - 1; i >= 0; i--) { a.push(Math.abs(arr[i] - median)); } return jStat.median(a); }; // coefficient of variation jStat.coeffvar = function coeffvar(arr) { return jStat.stdev(arr) / jStat.mean(arr); }; // quartiles of an array jStat.quartiles = function quartiles(arr) { var arrlen = arr.length; var _arr = arr.slice().sort(ascNum); return [_arr[Math.round(arrlen / 4) - 1], _arr[Math.round(arrlen / 2) - 1], _arr[Math.round(arrlen * 3 / 4) - 1]]; }; // Arbitary quantiles of an array. Direct port of the scipy.stats // implementation by Pierre GF Gerard-Marchant. jStat.quantiles = function quantiles(arr, quantilesArray, alphap, betap) { var sortedArray = arr.slice().sort(ascNum); var quantileVals = [quantilesArray.length]; var n = arr.length; var i, p, m, aleph, k, gamma; if (typeof alphap === 'undefined') alphap = 3 / 8; if (typeof betap === 'undefined') betap = 3 / 8; for (i = 0; i < quantilesArray.length; i++) { p = quantilesArray[i]; m = alphap + p * (1 - alphap - betap); aleph = n * p + m; k = Math.floor(clip(aleph, 1, n - 1)); gamma = clip(aleph - k, 0, 1); quantileVals[i] = (1 - gamma) * sortedArray[k - 1] + gamma * sortedArray[k]; } return quantileVals; }; // Return the k-th percentile of values in a range, where k is in the range 0..1, inclusive. // Passing true for the exclusive parameter excludes both endpoints of the range. jStat.percentile = function percentile(arr, k, exclusive) { var _arr = arr.slice().sort(ascNum); var realIndex = k * (_arr.length + (exclusive ? 1 : -1)) + (exclusive ? 0 : 1); var index = parseInt(realIndex); var frac = realIndex - index; if (index + 1 < _arr.length) { return _arr[index - 1] + frac * (_arr[index] - _arr[index - 1]); } else { return _arr[index - 1]; } }; // The percentile rank of score in a given array. Returns the percentage // of all values in the input array that are less than (kind='strict') or // less or equal than (kind='weak') score. Default is weak. jStat.percentileOfScore = function percentileOfScore(arr, score, kind) { var counter = 0; var len = arr.length; var strict = false; var value, i; if (kind === 'strict') strict = true; for (i = 0; i < len; i++) { value = arr[i]; if (strict && value < score || !strict && value <= score) { counter++; } } return counter / len; }; // Histogram (bin count) data jStat.histogram = function histogram(arr, binCnt) { binCnt = binCnt || 4; var first = jStat.min(arr); var binWidth = (jStat.max(arr) - first) / binCnt; var len = arr.length; var bins = []; var i; for (i = 0; i < binCnt; i++) bins[i] = 0; for (i = 0; i < len; i++) bins[Math.min(Math.floor((arr[i] - first) / binWidth), binCnt - 1)] += 1; return bins; }; // covariance of two arrays jStat.covariance = function covariance(arr1, arr2) { var u = jStat.mean(arr1); var v = jStat.mean(arr2); var arr1Len = arr1.length; var sq_dev = new Array(arr1Len); var i; for (i = 0; i < arr1Len; i++) sq_dev[i] = (arr1[i] - u) * (arr2[i] - v); return jStat.sum(sq_dev) / (arr1Len - 1); }; // (pearson's) population correlation coefficient, rho jStat.corrcoeff = function corrcoeff(arr1, arr2) { return jStat.covariance(arr1, arr2) / jStat.stdev(arr1, 1) / jStat.stdev(arr2, 1); }; // (spearman's) rank correlation coefficient, sp jStat.spearmancoeff = function (arr1, arr2) { arr1 = jStat.rank(arr1); arr2 = jStat.rank(arr2); //return pearson's correlation of the ranks: return jStat.corrcoeff(arr1, arr2); }; // statistical standardized moments (general form of skew/kurt) jStat.stanMoment = function stanMoment(arr, n) { var mu = jStat.mean(arr); var sigma = jStat.stdev(arr); var len = arr.length; var skewSum = 0; for (var i = 0; i < len; i++) skewSum += Math.pow((arr[i] - mu) / sigma, n); return skewSum / arr.length; }; // (pearson's) moment coefficient of skewness jStat.skewness = function skewness(arr) { return jStat.stanMoment(arr, 3); }; // (pearson's) (excess) kurtosis jStat.kurtosis = function kurtosis(arr) { return jStat.stanMoment(arr, 4) - 3; }; var jProto = jStat.prototype; // Extend jProto with method for calculating cumulative sums and products. // This differs from the similar extension below as cumsum and cumprod should // not be run again in the case fullbool === true. // If a matrix is passed, automatically assume operation should be done on the // columns. (function (funcs) { for (var i = 0; i < funcs.length; i++) (function (passfunc) { // If a matrix is passed, automatically assume operation should be done on // the columns. jProto[passfunc] = function (fullbool, func) { var arr = []; var i = 0; var tmpthis = this; // Assignment reassignation depending on how parameters were passed in. if (isFunction(fullbool)) { func = fullbool; fullbool = false; } // Check if a callback was passed with the function. if (func) { setTimeout(function () { func.call(tmpthis, jProto[passfunc].call(tmpthis, fullbool)); }); return this; } // Check if matrix and run calculations. if (this.length > 1) { tmpthis = fullbool === true ? this : this.transpose(); for (; i < tmpthis.length; i++) arr[i] = jStat[passfunc](tmpthis[i]); return arr; } // Pass fullbool if only vector, not a matrix. for variance and stdev. return jStat[passfunc](this[0], fullbool); }; })(funcs[i]); })('cumsum cumprod'.split(' ')); // Extend jProto with methods which don't require arguments and work on columns. (function (funcs) { for (var i = 0; i < funcs.length; i++) (function (passfunc) { // If a matrix is passed, automatically assume operation should be done on // the columns. jProto[passfunc] = function (fullbool, func) { var arr = []; var i = 0; var tmpthis = this; // Assignment reassignation depending on how parameters were passed in. if (isFunction(fullbool)) { func = fullbool; fullbool = false; } // Check if a callback was passed with the function. if (func) { setTimeout(function () { func.call(tmpthis, jProto[passfunc].call(tmpthis, fullbool)); }); return this; } // Check if matrix and run calculations. if (this.length > 1) { if (passfunc !== 'sumrow') tmpthis = fullbool === true ? this : this.transpose(); for (; i < tmpthis.length; i++) arr[i] = jStat[passfunc](tmpthis[i]); return fullbool === true ? jStat[passfunc](jStat.utils.toVector(arr)) : arr; } // Pass fullbool if only vector, not a matrix. for variance and stdev. return jStat[passfunc](this[0], fullbool); }; })(funcs[i]); })(('sum sumsqrd sumsqerr sumrow product min max unique mean meansqerr ' + 'geomean median diff rank mode range variance deviation stdev meandev ' + 'meddev coeffvar quartiles histogram skewness kurtosis').split(' ')); // Extend jProto with functions that take arguments. Operations on matrices are // done on columns. (function (funcs) { for (var i = 0; i < funcs.length; i++) (function (passfunc) { jProto[passfunc] = function () { var arr = []; var i = 0; var tmpthis = this; var args = Array.prototype.slice.call(arguments); var callbackFunction; // If the last argument is a function, we assume it's a callback; we // strip the callback out and call the function again. if (isFunction(args[args.length - 1])) { callbackFunction = args[args.length - 1]; var argsToPass = args.slice(0, args.length - 1); setTimeout(function () { callbackFunction.call(tmpthis, jProto[passfunc].apply(tmpthis, argsToPass)); }); return this; // Otherwise we curry the function args and call normally. } else { callbackFunction = undefined; var curriedFunction = function curriedFunction(vector) { return jStat[passfunc].apply(tmpthis, [vector].concat(args)); }; } // If this is a matrix, run column-by-column. if (this.length > 1) { tmpthis = tmpthis.transpose(); for (; i < tmpthis.length; i++) arr[i] = curriedFunction(tmpthis[i]); return arr; } // Otherwise run on the vector. return curriedFunction(this[0]); }; })(funcs[i]); })('quantiles percentileOfScore'.split(' ')); })(jStat, Math); // Special functions // (function (jStat, Math) { // Log-gamma function jStat.gammaln = function gammaln(x) { var j = 0; var cof = [76.18009172947146, -86.50532032941678, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -5395239384953e-18]; var ser = 1.000000000190015; var xx, y, tmp; tmp = (y = xx = x) + 5.5; tmp -= (xx + 0.5) * Math.log(tmp); for (; j < 6; j++) ser += cof[j] / ++y; return Math.log(2.5066282746310005 * ser / xx) - tmp; }; /* * log-gamma function to support poisson distribution sampling. The * algorithm comes from SPECFUN by Shanjie Zhang and Jianming Jin and their * book "Computation of Special Functions", 1996, John Wiley & Sons, Inc. */ jStat.loggam = function loggam(x) { var x0, x2, xp, gl, gl0; var k, n; var a = [8.333333333333333e-02, -0.002777777777777778, 7.936507936507937e-04, -5952380952380952e-19, 8.417508417508418e-04, -0.001917526917526918, 6.410256410256410e-03, -0.02955065359477124, 1.796443723688307e-01, -1.3924322169059]; x0 = x; n = 0; if (x == 1.0 || x == 2.0) { return 0.0; } if (x <= 7.0) { n = Math.floor(7 - x); x0 = x + n; } x2 = 1.0 / (x0 * x0); xp = 2 * Math.PI; gl0 = a[9]; for (k = 8; k >= 0; k--) { gl0 *= x2; gl0 += a[k]; } gl = gl0 / x0 + 0.5 * Math.log(xp) + (x0 - 0.5) * Math.log(x0) - x0; if (x <= 7.0) { for (k = 1; k <= n; k++) { gl -= Math.log(x0 - 1.0); x0 -= 1.0; } } return gl; }; // gamma of x jStat.gammafn = function gammafn(x) { var p = [-1.716185138865495, 24.76565080557592, -379.80425647094563, 629.3311553128184, 866.9662027904133, -31451.272968848367, -36144.413418691176, 66456.14382024054]; var q = [-30.8402300119739, 315.35062697960416, -1015.1563674902192, -3107.771671572311, 22538.118420980151, 4755.8462775278811, -134659.9598649693, -115132.2596755535]; var fact = false; var n = 0; var xden = 0; var xnum = 0; var y = x; var i, z, yi, res; if (x > 171.6243769536076) { return Infinity; } if (y <= 0) { res = y % 1 + 3.6e-16; if (res) { fact = (!(y & 1) ? 1 : -1) * Math.PI / Math.sin(Math.PI * res); y = 1 - y; } else { return Infinity; } } yi = y; if (y < 1) { z = y++; } else { z = (y -= n = (y | 0) - 1) - 1; } for (i = 0; i < 8; ++i) { xnum = (xnum + p[i]) * z; xden = xden * z + q[i]; } res = xnum / xden + 1; if (yi < y) { res /= yi; } else if (yi > y) { for (i = 0; i < n; ++i) { res *= y; y++; } } if (fact) { res = fact / res; } return res; }; // lower incomplete gamma function, which is usually typeset with a // lower-case greek gamma as the function symbol jStat.gammap = function gammap(a, x) { return jStat.lowRegGamma(a, x) * jStat.gammafn(a); }; // The lower regularized incomplete gamma function, usually written P(a,x) jStat.lowRegGamma = function lowRegGamma(a, x) { var aln = jStat.gammaln(a); var ap = a; var sum = 1 / a; var del = sum; var b = x + 1 - a; var c = 1 / 1.0e-30; var d = 1 / b; var h = d; var i = 1; // calculate maximum number of itterations required for a var ITMAX = -~(Math.log(a >= 1 ? a : 1 / a) * 8.5 + a * 0.4 + 17); var an; if (x < 0 || a <= 0) { return NaN; } else if (x < a + 1) { for (; i <= ITMAX; i++) { sum += del *= x / ++ap; } return sum * Math.exp(-x + a * Math.log(x) - aln); } for (; i <= ITMAX; i++) { an = -i * (i - a); b += 2; d = an * d + b; c = b + an / c; d = 1 / d; h *= d * c; } return 1 - h * Math.exp(-x + a * Math.log(x) - aln); }; // natural log factorial of n jStat.factorialln = function factorialln(n) { return n < 0 ? NaN : jStat.gammaln(n + 1); }; // factorial of n jStat.factorial = function factorial(n) { return n < 0 ? NaN : jStat.gammafn(n + 1); }; // combinations of n, m jStat.combination = function combination(n, m) { // make sure n or m don't exceed the upper limit of usable values return n > 170 || m > 170 ? Math.exp(jStat.combinationln(n, m)) : jStat.factorial(n) / jStat.factorial(m) / jStat.factorial(n - m); }; jStat.combinationln = function combinationln(n, m) { return jStat.factorialln(n) - jStat.factorialln(m) - jStat.factorialln(n - m); }; // permutations of n, m jStat.permutation = function permutation(n, m) { return jStat.factorial(n) / jStat.factorial(n - m); }; // beta function jStat.betafn = function betafn(x, y) { // ensure arguments are positive if (x <= 0 || y <= 0) return undefined; // make sure x + y doesn't exceed the upper limit of usable values return x + y > 170 ? Math.exp(jStat.betaln(x, y)) : jStat.gammafn(x) * jStat.gammafn(y) / jStat.gammafn(x + y); }; // natural logarithm of beta function jStat.betaln = function betaln(x, y) { return jStat.gammaln(x) + jStat.gammaln(y) - jStat.gammaln(x + y); }; // Evaluates the continued fraction for incomplete beta function by modified // Lentz's method. jStat.betacf = function betacf(x, a, b) { var fpmin = 1e-30; var m = 1; var qab = a + b; var qap = a + 1; var qam = a - 1; var c = 1; var d = 1 - qab * x / qap; var m2, aa, del, h; // These q's will be used in factors that occur in the coefficients if (Math.abs(d) < fpmin) d = fpmin; d = 1 / d; h = d; for (; m <= 100; m++) { m2 = 2 * m; aa = m * (b - m) * x / ((qam + m2) * (a + m2)); // One step (the even one) of the recurrence d = 1 + aa * d; if (Math.abs(d) < fpmin) d = fpmin; c = 1 + aa / c; if (Math.abs(c) < fpmin) c = fpmin; d = 1 / d; h *= d * c; aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2)); // Next step of the recurrence (the odd one) d = 1 + aa * d; if (Math.abs(d) < fpmin) d = fpmin; c = 1 + aa / c; if (Math.abs(c) < fpmin) c = fpmin; d = 1 / d; del = d * c; h *= del; if (Math.abs(del - 1.0) < 3e-7) break; } return h; }; // Returns the inverse of the lower regularized inomplete gamma function jStat.gammapinv = function gammapinv(p, a) { var j = 0; var a1 = a - 1; var EPS = 1e-8; var gln = jStat.gammaln(a); var x, err, t, u, pp, lna1, afac; if (p >= 1) return Math.max(100, a + 100 * Math.sqrt(a)); if (p <= 0) return 0; if (a > 1) { lna1 = Math.log(a1); afac = Math.exp(a1 * (lna1 - 1) - gln); pp = p < 0.5 ? p : 1 - p; t = Math.sqrt(-2 * Math.log(pp)); x = (2.30753 + t * 0.27061) / (1 + t * (0.99229 + t * 0.04481)) - t; if (p < 0.5) x = -x; x = Math.max(1e-3, a * Math.pow(1 - 1 / (9 * a) - x / (3 * Math.sqrt(a)), 3)); } else { t = 1 - a * (0.253 + a * 0.12); if (p < t) x = Math.pow(p / t, 1 / a);else x = 1 - Math.log(1 - (p - t) / (1 - t)); } for (; j < 12; j++) { if (x <= 0) return 0; err = jStat.lowRegGamma(a, x) - p; if (a > 1) t = afac * Math.exp(-(x - a1) + a1 * (Math.log(x) - lna1));else t = Math.exp(-x + a1 * Math.log(x) - gln); u = err / t; x -= t = u / (1 - 0.5 * Math.min(1, u * ((a - 1) / x - 1))); if (x <= 0) x = 0.5 * (x + t); if (Math.abs(t) < EPS * x) break; } return x; }; // Returns the error function erf(x) jStat.erf = function erf(x) { var cof = [-1.3026537197817094, 6.4196979235649026e-1, 1.9476473204185836e-2, -0.00956151478680863, -946595344482036e-18, 3.66839497852761e-4, 4.2523324806907e-5, -20278578112534e-18, -1624290004647e-18, 1.303655835580e-6, 1.5626441722e-8, -8.5238095915e-8, 6.529054439e-9, 5.059343495e-9, -991364156e-18, -227365122e-18, 9.6467911e-11, 2.394038e-12, -6886027e-18, 8.94487e-13, 3.13092e-13, -112708e-18, 3.81e-16, 7.106e-15, -1523e-18, -94e-18, 1.21e-16, -28e-18]; var j = cof.length - 1; var isneg = false; var d = 0; var dd = 0; var t, ty, tmp, res; if (x < 0) { x = -x; isneg = true; } t = 2 / (2 + x); ty = 4 * t - 2; for (; j > 0; j--) { tmp = d; d = ty * d - dd + cof[j]; dd = tmp; } res = t * Math.exp(-x * x + 0.5 * (cof[0] + ty * d) - dd); return isneg ? res - 1 : 1 - res; }; // Returns the complmentary error function erfc(x) jStat.erfc = function erfc(x) { return 1 - jStat.erf(x); }; // Returns the inverse of the complementary error function jStat.erfcinv = function erfcinv(p) { var j = 0; var x, err, t, pp; if (p >= 2) return -100; if (p <= 0) return 100; pp = p < 1 ? p : 2 - p; t = Math.sqrt(-2 * Math.log(pp / 2)); x = -0.70711 * ((2.30753 + t * 0.27061) / (1 + t * (0.99229 + t * 0.04481)) - t); for (; j < 2; j++) { err = jStat.erfc(x) - pp; x += err / (1.12837916709551257 * Math.exp(-x * x) - x * err); } return p < 1 ? x : -x; }; // Returns the inverse of the incomplete beta function jStat.ibetainv = function ibetainv(p, a, b) { var EPS = 1e-8; var a1 = a - 1; var b1 = b - 1; var j = 0; var lna, lnb, pp, t, u, err, x, al, h, w, afac; if (p <= 0) return 0; if (p >= 1) return 1; if (a >= 1 && b >= 1) { pp = p < 0.5 ? p : 1 - p; t = Math.sqrt(-2 * Math.log(pp)); x = (2.30753 + t * 0.27061) / (1 + t * (0.99229 + t * 0.04481)) - t; if (p < 0.5) x = -x; al = (x * x - 3) / 6; h = 2 / (1 / (2 * a - 1) + 1 / (2 * b - 1)); w = x * Math.sqrt(al + h) / h - (1 / (2 * b - 1) - 1 / (2 * a - 1)) * (al + 5 / 6 - 2 / (3 * h)); x = a / (a + b * Math.exp(2 * w)); } else { lna = Math.log(a / (a + b)); lnb = Math.log(b / (a + b)); t = Math.exp(a * lna) / a; u = Math.exp(b * lnb) / b; w = t + u; if (p < t / w) x = Math.pow(a * w * p, 1 / a);else x = 1 - Math.pow(b * w * (1 - p), 1 / b); } afac = -jStat.gammaln(a) - jStat.gammaln(b) + jStat.gammaln(a + b); for (; j < 10; j++) {