@decidables/detectable-math
Version:
detectable-math: Equations for calculating Signal Detection Theory
1,400 lines (1,282 loc) • 161 kB
JavaScript
(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++) {