From ca4795e74267bff9ef2cb138fae4be5262a78e3a Mon Sep 17 00:00:00 2001 From: Ian Holmes Date: Mon, 6 Mar 2017 14:37:38 -0800 Subject: [PATCH] When there's only one organism (in web client), auto-load it --- web/assets/js/wtfgenes.js | 10104 +++++++++++++++++------------------- web/client.js | 10 +- web/datasets.json | 134 +- 3 files changed, 4875 insertions(+), 5373 deletions(-) diff --git a/web/assets/js/wtfgenes.js b/web/assets/js/wtfgenes.js index e712ed9..02332c7 100644 --- a/web/assets/js/wtfgenes.js +++ b/web/assets/js/wtfgenes.js @@ -1,6171 +1,5669 @@ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o m ? n : m; + return Math.pow(10, + 17 - ~~(Math.log(((val > 0) ? val : -val)) * Math.LOG10E)); +} - 'genesByTerm': [], - 'termsByGene': [], - 'geneHasTerm': {}, - 'genes': function() { return this.geneName.length }, - 'terms': function() { return this.ontology.terms() }, +var isArray = Array.isArray || function isArray(arg) { + return toString.call(arg) === '[object Array]'; +}; - 'relevantTerms': function() { - var assocs = this - return util.iota(assocs.terms()).filter (function(term) { - return assocs.genesByTerm[term].length > 0 - && assocs.termIsExemplar(term) - && !ontology.doNotAnnotate[term] - }) - }, - 'relevantTermsForGeneSet': function (geneSet) { - var assocs = this - return util.removeDups (geneSet.reduce (function(termList,g) { - return termList.concat (assocs.termsByGene[g]) - }, [])).filter (function (term) { - return assocs.termIsExemplar(term) - && !ontology.doNotAnnotate[term] - }).sort(util.numCmp) - }, - 'equivClassByTerm': [], - 'termsInEquivClass': [], - 'getExemplar': function(termIndex) { - return this.termsInEquivClass[this.equivClassByTerm[termIndex]][0] - }, - 'termIsExemplar': function(termIndex) { - return this.getExemplar(termIndex) == termIndex - }, - 'termEquivalents': function() { - var assocs = this - return util.keyValListToObj (assocs.termsInEquivClass.filter (function(l) { - return l.length > 1 - }).map (function(l) { - var n = l.map (function(ti) { return assocs.ontology.termName[ti] }) - return [n[0], n.slice(1)] - })) - }, - - 'nAssocs': 0, - 'hypergeometricPValues': hypergeometricPValues, - 'validateGeneNames': validateGeneNames, - 'toJSON': toJSON - }) +function isFunction(arg) { + return toString.call(arg) === '[object Function]'; +} - var closure - if (conf.closure) - closure = ontology.transitiveClosure() - else { - closure = [] - for (var t = 0; t < ontology.terms(); ++t) - closure.push ([t]) - } - var gtCount = [], missing = {} - idAliasTerm.forEach (function(iat) { - var gene = iat[0] - var aliases = iat[1] - var terms = iat[2] +function isNumber(arg) { + return typeof arg === 'number' && arg === arg; +} - if (!(gene in assocs.geneIndex)) { - var gi = assocs.genes() - assocs.geneName.push (gene) - gtCount.push ({}) - assocs.geneIndex[gene] = gi - assocs.geneIndex[gene.toUpperCase()] = gi - assocs.geneIndex[gene.toLowerCase()] = gi - aliases.forEach (function (alias) { - assocs.geneIndex[alias] = gi - assocs.geneIndex[alias.toUpperCase()] = gi - assocs.geneIndex[alias.toLowerCase()] = gi - }) - } - terms.forEach (function (term) { - if (!(term in ontology.termIndex)) - missing[term] = (missing[term] || 0) + 1 - else { - var g = assocs.geneIndex[gene] - var t = ontology.termIndex[term] - closure[t].forEach (function(c) { - ++gtCount[g][c] - }) - } - }) - }) +// Converts the jStat matrix to vector. +function toVector(arr) { + return concat.apply([], arr); +} - var missingTerms = Object.keys(missing) - if (missingTerms.length > 0 && !conf.ignoreMissingTerms) - console.warn ("Warning: the following terms were not found in the ontology: " + missingTerms) - assocs.genesByTerm = assocs.ontology.termName.map (function() { return [] }) - assocs.termsByGene = assocs.geneName.map (function() { return [] }) - assocs.geneHasTerm = assocs.geneName.map (function() { return {} }) +// The one and only jStat constructor. +function jStat() { + return new jStat._init(arguments); +} - for (var g = 0; g < assocs.genes(); ++g) { - Object.keys(gtCount[g]).forEach (function(tStr) { - var t = parseInt (tStr) - assocs.termsByGene[g].push (t) - assocs.genesByTerm[t].push (g) - assocs.geneHasTerm[g][t] = 1 - ++assocs.nAssocs - }) - } - assocs.termsByGene = assocs.termsByGene.map (util.sortAscending) - assocs.genesByTerm = assocs.genesByTerm.map (util.sortAscending) +// TODO: Remove after all references in src files have been removed. +jStat.fn = jStat.prototype; - var termClass = {} - var reverseToposort = ontology.toposortTermIndex().slice(0).reverse() - assocs.equivClassByTerm = ontology.termName.map (function() { return null }) - reverseToposort.forEach (function (term) { - var genesStr = "#" + assocs.genesByTerm[term].join(",") - if (!(genesStr in termClass)) { - termClass[genesStr] = assocs.termsInEquivClass.length - assocs.termsInEquivClass.push ([]) - } - var c = termClass[genesStr] - assocs.equivClassByTerm[term] = c - assocs.termsInEquivClass[c].push (term) - }) - } - module.exports = Assocs -}) () +// 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) { + var i; -},{"./util":7,"assert":12,"jstat":9}],2:[function(require,module,exports){ -(function() { - var util = require('./util'), - extend = util.extend, - assert = require('assert'), - jStat = require('jStat').jStat + // 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 (i = 0; i < args[0].length; i++) + this[i] = args[0][i]; + this.length = args[0].length; - function update (bp, param) { - var val = bp._params[param] - bp._logYes[param] = Math.log (val) - bp._logNo[param] = Math.log (1 - val) + // Otherwise must be a vector. + } else { + this[0] = isFunction(args[1]) ? jStat.map(args[0], args[1]) : args[0]; + this.length = 1; } - function logLikelihood (params, counts) { - var ll = 0 - for (var param in counts.succ) - if (counts.succ.hasOwnProperty (param)) - ll += params._logYes[param] * counts.succ[param] - for (var param in counts.fail) - if (counts.fail.hasOwnProperty (param)) - ll += params._logNo[param] * counts.fail[param] - return ll - } + // If first argument is number, assume creation of sequence. + } else if (isNumber(args[0])) { + this[0] = jStat.seq.apply(null, args); + this.length = 1; - function logPrior (params, priorCounts) { - var lp = 0 - for (var param in params._params) - lp += Math.log (jStat.beta.pdf (params._params[param], - priorCounts.succ[param] + 1, - priorCounts.fail[param] + 1)) - return lp - } + // 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()); - function logBetaBernoulliLikelihood (priorCounts) { - var counts = this - var l = 0 - var allCounts = [priorCounts.succ, priorCounts.fail, counts.succ, counts.fail].reduce (util.extend, {}) - Object.keys(allCounts).forEach (function (param) { - l += util.logBetaBernoulli ((priorCounts.succ[param] || 0) + 1, - (priorCounts.fail[param] || 0) + 1, - counts.succ[param] || 0, - counts.fail[param] || 0) - }) - return l - } + // 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; + } - function deltaLogBetaBernoulliLikelihood (deltaCounts) { - var counts = this - var d = 0 - var allCounts = [deltaCounts.succ, deltaCounts.fail, this.succ, this.fail].reduce (util.extend, {}) - Object.keys(allCounts).forEach (function (param) { - var oldSucc = counts.succ[param] || 0 - var oldFail = counts.fail[param] || 0 - var newSucc = oldSucc + (deltaCounts.succ[param] || 0) - var newFail = oldFail + (deltaCounts.fail[param] || 0) + return this; +}; +jStat._init.prototype = jStat.prototype; +jStat._init.constructor = jStat; - d += jStat.betaln(newSucc+1,newFail+1) - jStat.betaln(oldSucc+1,oldFail+1) - }) - return d - } - function copyCounts() { - return new BernoulliCounts (this) - } +// Utility functions. +// TODO: for internal use only? +jStat.utils = { + calcRdx: calcRdx, + isArray: isArray, + isFunction: isFunction, + isNumber: isNumber, + toVector: toVector +}; - function add (counts) { - return this.copy().accum(counts) - } - function accWithDelete (c, c2, param) { - var newCount = c2[param] + (c[param] || 0) - if (newCount) - c[param] = newCount - else - delete c[param] - } +// Easily extend the jStat object. +// TODO: is this seriously necessary? +jStat.extend = function extend(obj) { + var i, j; - function accum (counts) { - assert.equal (this.paramSet, counts.paramSet) - for (var param in counts.succ) - accWithDelete (this.succ, counts.succ, param) - for (var param in counts.fail) - accWithDelete (this.fail, counts.fail, param) - return this - } + if (arguments.length === 1) { + for (j in obj) + jStat[j] = obj[j]; + return this; + } - function scale (factor) { - for (var param in this.succ) - this.succ[param] *= factor - for (var param in this.fail) - this.fail[param] *= factor - return this - } + for (i = 1; i < arguments.length; i++) { + for (j in arguments[i]) + obj[j] = arguments[i][j]; + } - function subtract (counts) { - return this.copy().accum (counts.copy().scale(-1)) - } + return obj; +}; - function modalParams(params) { - var counts = this - params = params || counts.paramSet.newParams() - params.paramNames().forEach (function(p) { - params.setParam (p, counts.succ[p] / (counts.succ[p] + counts.fail[p])) - }) - return params - } - function meanParams(params) { - var counts = this - params = params || counts.paramSet.newParams() - params.paramNames().forEach (function(p) { - params.setParam (p, (counts.succ[p] + 1) / (counts.succ[p] + counts.fail[p] + 2)) - }) - return params - } +// Returns the number of rows in the matrix. +jStat.rows = function rows(arr) { + return arr.length || 1; +}; - function sampleParams(generator,params) { - params = params || this.paramSet.newParams() - // quick & dirty hack to bypass jStat's hardwired use of Math.random()... - var oldRandom = Math.random - if (generator) - Math.random = generator.random.bind(generator) - // sample... - for (var param in params._params) - params.setParam (param, jStat.beta.sample (this.succ[param] + 1, this.fail[param] + 1)) - // restore... - Math.random = oldRandom - // return - return params - } - function BernoulliParamSet (params) { - var bp = this - extend (bp, { - _params: params || {}, - paramNames: function() { return Object.keys(this._params).sort() }, - addParam: function(param) { this._params[param] = 1 }, - toJSON: function() { return this.paramNames() }, - newParams: function(p) { return new BernoulliParamAssignment (this._params) }, - newCounts: function(c) { return new BernoulliCounts (extend ({ paramSet: this }, c)) }, - laplacePrior: function() { - var c = this.newCounts() - this.paramNames().forEach (function(p) { c.succ[p] = c.fail[p] = 1 }) - return c - } - }) - } +// Returns the number of columns in the matrix. +jStat.cols = function cols(arr) { + return arr[0].length || 1; +}; - function BernoulliCounts (counts, paramSet) { - var bc = this - extend (bc, { - paramSet: paramSet - || counts.paramSet - || new BernoulliParamSet (extend (extend ({}, counts.succ), counts.fail)), - succ: extend ({}, counts.succ), - fail: extend ({}, counts.fail), - logLikelihood: function(params) { return logLikelihood(params,this) }, - logPrior: function(params) { return logPrior(params,this) }, - logLikeWithPrior: function(prior,params) { - return prior.logPrior(params) + logLikelihood(params,this) - }, - logBetaBernoulliLikelihood: logBetaBernoulliLikelihood, - deltaLogBetaBernoulliLikelihood: deltaLogBetaBernoulliLikelihood, - copy: copyCounts, - add: add, - accum: accum, - scale: scale, - subtract: subtract, - sampleParams: sampleParams, - modalParams: modalParams, - meanParams: meanParams, - toJSON: function() { return { succ: this.succ, fail: this.fail } } - }) - } - function BernoulliParamAssignment (params) { - var bp = this - extend (bp, { - _params: params || {}, - _logYes: {}, - _logNo: {}, - paramNames: function() { return Object.keys(this._params).sort() }, - getParam: function(param) { return this._params[param] }, - setParam: function(param,val) { this._params[param] = val; update (bp, param) }, - setParams: function(params) { - var bp = this - Object.keys(params).map (function(p) { bp.setParam (p, params[p]) }) - }, - logLikelihood: function(count) { return logLikelihood(this,count) }, - logPrior: function(prior) { return logPrior(this,prior) }, - logLikeWithPrior: function(prior,count) { return logPrior(this,prior) + logLikelihood(this,count) }, - toJSON: function() { return extend ({}, this._params) }, - newCounts: function(c) { return new BernoulliCounts (extend ({ paramSet: this }, c)) }, - laplacePrior: function() { - var c = this.newCounts() - this.paramNames().forEach (function(p) { c.succ[p] = c.fail[p] = 1 }) - return c - } - }) - Object.keys(params).forEach (function(param) { - update (bp, param) - }) - } +// Returns the dimensions of the object { rows: i, cols: j } +jStat.dimensions = function dimensions(arr) { + return { + rows: jStat.rows(arr), + cols: jStat.cols(arr) + }; +}; - module.exports.BernoulliParamSet = BernoulliParamSet - module.exports.BernoulliParamAssignment = BernoulliParamAssignment - module.exports.BernoulliCounts = BernoulliCounts -}) () -},{"./util":7,"assert":12,"jStat":8}],3:[function(require,module,exports){ -(function() { - var assert = require('assert'), - jStat = require('jStat').jStat, - MersenneTwister = require('mersennetwister'), - Model = require('./model'), - Parameterization = require('./parameterization'), - BernoulliCounts = require('./bernoulli').BernoulliCounts, - util = require('./util'), - extend = util.extend - - function logMove(text) { - var mcmc = this - console.warn ("Move #" + mcmc.samplesIncludingBurn + ": " + text) - } +// Returns a specified row as a vector +jStat.row = function row(arr, index) { + return arr[index]; +}; - function logTermMove(move) { - var mcmc = this - logMove.bind(mcmc)("(" + Object.keys(move.termStates).map (function(t) { - return mcmc.assocs.ontology.termName[t] + "=>" + move.termStates[t] - }) + ") " + JSON.stringify(move.delta) + " HastingsRatio=" + move.hastingsRatio + " " - + (move.accepted ? "Accept" : "Reject")) - } - function logMoves() { - var mcmc = this - mcmc.postMoveCallback.push (function (mcmc, move) { - logTermMove.bind(mcmc) (move) - }) - } +// Returns the specified column as a vector +jStat.col = function cols(arr, index) { + var column = new Array(arr.length); + for (var i = 0; i < arr.length; i++) + column[i] = [arr[i][index]]; + return column; +}; - function logState() { - this.postMoveCallback.push (function (mcmc, move) { - console.warn ("Sample #" + move.sample - + ": log-likelihood " + mcmc.quickCollapsedLogLikelihood() - + " (" + mcmc.collapsedLogLikelihood() + ")" - + ", state " + mcmc.models.map (function (model) { - return JSON.stringify (model.toJSON()) - })) - }) - } - - function logProgress() { - var progressLogger = util.progressLogger ("Sampled", "states") - this.postMoveCallback.push (function (mcmc, move) { - progressLogger (move.sample + 1, move.totalSamples) - }) - } - function logActiveTerms() { - var mcmc = this - if (!mcmc.activeTermTrace) { - mcmc.activeTermTrace = mcmc.models.map (function() { return [] }) - mcmc.postMoveCallback.push (function (mcmc, move) { - if (mcmc.finishedBurn()) - mcmc.models.forEach (function (model, m) { - mcmc.activeTermTrace[m].push (model.activeTerms()) - }) - }) - } - } +// 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; +}; - function logLogLikelihood (includeBurn) { - var mcmc = this - if (!mcmc.logLikelihoodTrace) { - mcmc.logLikelihoodTrace = [] - mcmc.postMoveCallback.push (function (mcmc, move) { - if (includeBurn || mcmc.finishedBurn()) - mcmc.logLikelihoodTrace.push (mcmc.quickCollapsedLogLikelihood()) - }) - } - } - function logTermPairs() { - var mcmc = this - mcmc.termPairOccupancy = mcmc.models.map (function (model) { - return util.keyValListToObj (model.relevantTerms.map (function(ti,i) { - return [ti, util.keyValListToObj (model.relevantTerms.slice(i+1).map (function(tj) { - return [tj, 0] - }))] - }))}) - mcmc.termPairOccupancyNorm = mcmc.models.map (function (model) { - return util.keyValListToObj (model.relevantTerms.map (function(ti,i) { - return [ti, 0] - })) - }) - mcmc.termPairSamples = 0 - mcmc.logTermPairFunc = function (mcmc, move) { - if (mcmc.finishedBurn()) { - mcmc.models.forEach (function (model, m) { - var active = model.activeTerms() - for (var i = 0; i < active.length; ++i) { - for (var j = i + 1; j < active.length; ++j) - ++mcmc.termPairOccupancy[m][active[i]][active[j]] - ++mcmc.termPairOccupancyNorm[m][active[i]] - } - }) - ++mcmc.termPairSamples - } - } - mcmc.postMoveCallback.push (mcmc.logTermPairFunc) - } +// 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; +}; - function stopLoggingTermPairs() { - var mcmc = this - mcmc.postMoveCallback = mcmc.postMoveCallback.filter (function (func) { - return func !== mcmc.logTermPairFunc - }) - delete mcmc.logTermPairFunc - delete mcmc.termPairOccupancy - delete mcmc.termPairOccupancyNorm - delete mcmc.termPairSamples - } +// Transpose a matrix or array. +jStat.transpose = function transpose(arr) { + var obj = []; + var objArr, rows, cols, j, i; - function logMixing() { - var mcmc = this - var startTime = Date.now(), moveStartMillisecs - mcmc.traceStats = { logLikelihood: [], - moveElapsedMillisecs: {}, - nProposedMoves: {}, - nAcceptedMoves: {} } - Object.keys(mcmc.moveRate).forEach (function(type) { - mcmc.traceStats.moveElapsedMillisecs[type] = 0 - mcmc.traceStats.nAcceptedMoves[type] = 0 - mcmc.traceStats.nProposedMoves[type] = 0 - }) - mcmc.logActiveTerms() - mcmc.logLogLikelihood (false) - mcmc.preMoveCallback.push (function (mcmc) { - moveStartMillisecs = (new Date).getTime() - }) - mcmc.postMoveCallback.push (function (mcmc, move) { - if (mcmc.finishedBurn()) { - mcmc.traceStats.moveElapsedMillisecs[move.type] += (new Date).getTime() - moveStartMillisecs - ++mcmc.traceStats.nProposedMoves[move.type] - if (move.accepted) - ++mcmc.traceStats.nAcceptedMoves[move.type] - } - }) - mcmc.summaryCallback.push (function (mcmc, summ) { - var endTime = Date.now() - summ.mcmc.samplesPerSecond = mcmc.samples / (endTime - startTime) - var points = util.iota (Math.ceil(Math.log2(mcmc.samples))).map (function(x) { return Math.pow(2,x) }) - console.warn ("Computing log-likelihood autocorrelations") - summ.mcmc.logLikeAutoCorrelation = util.autocorrelation (mcmc.logLikelihoodTrace, points) - console.warn ("Computing term autocorrelations") - summ.mcmc.termAutoCorrelation = [] - mcmc.models.forEach (function (model, m) { - var activeTermTrace = mcmc.activeTermTrace[m] - assert.equal (activeTermTrace.length, mcmc.samples) - var termProb = mcmc.termStateOccupancy[m].map (function (occ) { - return occ / mcmc.samples - }) - var termPrecision = termProb.map (function (p) { return 1 / (p - p*p) }) - var dynamicTerms = model.relevantTerms.filter (function (term) { - var p = termProb[term] - return p > 0 && p < 1 - }) - var isDynamic = util.objPredicate (util.listToCounts (dynamicTerms)) - var nTermsHit = dynamicTerms.length + // Make sure arr is in matrix format. + if (!isArray(arr[0])) + arr = [arr]; - // t = time, T = term, tmax = max time, Tmax = number of terms - // X^T_t = term T's state at time t - // Mean term autocorrelation = < <(x^T_t - ) (x^T_{t+tau} - ) / <(x^T_t - )^2> >_t >_T - // = 1/tmax 1/Tmax sum_t^tmax sum_T^Tmax (x^T_t - ) (x^T_{t+tau} - ) / ( - ^2) - // = 1/Tmax sum_T^Tmax ((1/tmax sum_t^tmax x^T_t x^T_{t+tau}) - ^2) / ( - ^2) + rows = arr.length; + cols = arr[0].length; - var baseline = util.sumList (dynamicTerms.map (function (term) { - return termProb[term] * termProb[term] * termPrecision[term] - })) - var termAuto = {} - var progressLogger = util.progressLogger ("Computed autocorrelation at", "lag times") - points.forEach (function(tau,n) { - progressLogger (n + 1, points.length) - var R_tau = [] - for (var i = 0; i + tau < mcmc.samples; ++i) { - var commonTerms = util.commonElements (activeTermTrace[i], activeTermTrace[i+tau]) - .filter (isDynamic) - var sum = util.sumList (commonTerms.map (function (term) { return termPrecision[term] })) - R_tau.push (sum) - } - termAuto[tau] = (jStat.mean(R_tau) - baseline) / nTermsHit - }) - summ.mcmc.termAutoCorrelation.push (termAuto) - }) - summ.mcmc.proposedMovesPerSecond = {} - summ.mcmc.moveAcceptRate = {} - Object.keys(mcmc.moveRate).forEach(function(type) { - summ.mcmc.moveAcceptRate[type] = mcmc.traceStats.nAcceptedMoves[type] / mcmc.traceStats.nProposedMoves[type] - summ.mcmc.proposedMovesPerSecond[type] = 1000 * mcmc.traceStats.nProposedMoves[type] / mcmc.traceStats.moveElapsedMillisecs[type] - }) - }) - } + for (i = 0; i < cols; i++) { + objArr = new Array(rows); + for (j = 0; j < rows; j++) + objArr[j] = arr[j][i]; + obj.push(objArr); + } - function getCounts(models,prior) { - return models.reduce (function(c,m) { - return c.accum (m.getCounts()) - }, prior.copy()) - } + // If obj is vector, return only single array. + return obj.length === 1 ? obj[0] : obj; +}; - function run(samples) { - var mcmc = this - if (util.sumList(mcmc.modelWeight) == 0) { - console.warn ("Refusing to run MCMC on a model with no variables") - return - } +// 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; - var moveTypes = ['flip', 'step', 'jump', 'randomize'] - var moveProposalFuncs = { flip: 'proposeFlipMove', - step: 'proposeStepMove', - jump: 'proposeJumpMove', - randomize: 'proposeRandomizeMove' } - var moveRates = moveTypes.map (function(t) { return mcmc.moveRate[t] }) - - for (var sample = 0; sample < samples; ++sample) { + if (!isArray(arr[0])) + arr = [arr]; - mcmc.preMoveCallback.forEach (function(callback) { - callback (mcmc) - }) + nrow = arr.length; + ncol = arr[0].length; + res = toAlter ? arr : new Array(nrow); - var move = { sample: sample, - totalSamples: samples, - type: moveTypes [util.randomIndex (moveRates, mcmc.generator)], - model: mcmc.models [util.randomIndex (mcmc.modelWeight, mcmc.generator)], - logLikelihoodRatio: 0, - accepted: false } + 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); + } - extend (move, move.model[moveProposalFuncs[move.type]].bind(move.model) ()) - move.model.sampleMoveCollapsed (move, mcmc.countsWithPrior) + return res.length === 1 ? res[0] : res; +}; - ++mcmc.samplesIncludingBurn - if (mcmc.finishedBurn()) { - ++mcmc.samples +// 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; - mcmc.models.forEach (function(model,n) { - var termStateOccupancy = mcmc.termStateOccupancy[n] - model.activeTerms().forEach (function(term) { - ++termStateOccupancy[term] - }) - var geneFalseOccupancy = mcmc.geneFalseOccupancy[n] - model.falseGenes().forEach (function(gene) { - ++geneFalseOccupancy[gene] - }) - }) - } + if (!isArray(arr[0])) + arr = [arr]; - mcmc.postMoveCallback.forEach (function(callback) { - callback (mcmc, move) - }) - } - } + nrow = arr.length; + ncol = arr[0].length; + res = toAlter ? arr : new Array(nrow); - function termSummary (modelIndex, threshold) { - var mcmc = this - threshold = threshold || .01 - return util.keyValListToObj (mcmc.termStateOccupancy[modelIndex].map (function (occ, term) { - return [mcmc.assocs.ontology.termName[term], occ / mcmc.samples] - }).filter (function (keyVal) { return keyVal[1] >= threshold })) - } + 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; +}; - function termPairSummary (modelIndex, terms) { - var mcmc = this - var termIndices = terms.map (function(n) { return mcmc.assocs.ontology.termIndex[n] }) - var termName = mcmc.assocs.ontology.termName - var pairProb = util.keyValListToObj (termIndices.map (function(t1) { - return [termName[t1], - util.keyValListToObj (termIndices - .filter (function(t2) { return t2 != t1 }) - .map (function(t2) { - var ti, tj - if (t1 < t2) { ti = t1; tj = t2 } - else { ti = t2; tj = t1 } - return [termName[t2], - mcmc.termPairOccupancy[modelIndex][ti][tj] / mcmc.termPairSamples] - }))] - })) - var singleProb = util.keyValListToObj (termIndices.map (function(t) { - return [termName[t], mcmc.termPairOccupancyNorm[modelIndex][t] / mcmc.termPairSamples] - })) - return { pair: pairProb, - single: singleProb } - } - function geneFalsePosSummary (modelIndex, threshold) { - return geneSummary (this, modelIndex, true, threshold) - } +// Destructively alter an array. +jStat.alter = function alter(arr, func) { + return jStat.map(arr, func, true); +}; - function geneFalseNegSummary (modelIndex, threshold) { - return geneSummary (this, modelIndex, false, threshold) - } - function geneSummary (mcmc, modelIndex, wantGeneSet, threshold) { - var model = mcmc.models[modelIndex] - threshold = threshold || .01 - return util.keyValListToObj (mcmc.geneFalseOccupancy[modelIndex].map (function (occ, gene) { - return [gene, occ / mcmc.samples] - }).filter (function (keyVal) { - var inGeneSet = model.inGeneSet[keyVal[0]] - return keyVal[1] >= threshold && (wantGeneSet ? inGeneSet : !inGeneSet) - }).map (function (keyVal) { - return [mcmc.assocs.geneName[keyVal[0]], keyVal[1]] - })) - } +// 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; - function hypergeometricSummary (modelIndex, maxPValue) { - var mcmc = this - maxPValue = maxPValue || .05 // default 95% significance - var multiMaxPValue = maxPValue / mcmc.assocs.terms() // Bonferroni correction - return { maxThreshold: maxPValue, - bonferroniMaxThreshold: multiMaxPValue, - term: util.keyValListToObj (mcmc.hypergeometric[modelIndex].map (function (pvalue, term) { - return [mcmc.assocs.ontology.termName[term], pvalue] - }).filter (function (keyVal) { return keyVal[1] <= multiMaxPValue })) - } - } + if (isFunction(cols)) { + func = cols; + cols = rows; + } - function summary (threshold) { - var mcmc = this - threshold = threshold || .01 - var summ = { model: { prior: mcmc.prior.toJSON() }, - termEquivalents: {}, - mcmc: { - samples: mcmc.samples, - burn: mcmc.burn, - moveRate: mcmc.moveRate - }, - summary: mcmc.models.map (function (model, modelIndex) { - return { - hypergeometricPValue: hypergeometricSummary.call (mcmc, modelIndex), - posteriorMarginal: { - minThreshold: threshold, - term: termSummary.bind(mcmc) (modelIndex, threshold), - gene: { - falsePos: geneFalsePosSummary.bind(mcmc) (modelIndex, threshold), - falseNeg: geneFalseNegSummary.bind(mcmc) (modelIndex, threshold) - } - } - } - }) - } - mcmc.summaryCallback.forEach (function(callback) { - callback (mcmc, summ) - }) - var equiv = mcmc.assocs.termEquivalents() - summ.summary.forEach (function (s) { - Object.keys(s.posteriorMarginal.term).forEach (function (t) { - summ.termEquivalents[t] = equiv[t] - }) - }) - return summ - } + for (i = 0; i < rows; i++) { + res[i] = new Array(cols); + for (j = 0; j < cols; j++) + res[i][j] = func(i, j); + } - function nVariables() { - return util.sumList (this.models.map (function (model) { - return model.relevantTerms.length - })) - } - - function MCMC (conf) { - var mcmc = this + return res; +}; - var assocs = conf.assocs - var parameterization = conf.parameterization || new Parameterization (conf) - var prior = conf.prior - ? new BernoulliCounts(conf.prior,parameterization.paramSet) - : parameterization.paramSet.laplacePrior() - var generator = conf.generator || new MersenneTwister (conf.seed) - var initTerms = conf.initTerms || [] - var models = conf.models - || (conf.geneSets || [conf.geneSet]).map (function(geneSet,n) { - return new Model ({ assocs: assocs, - geneSet: geneSet, - parameterization: parameterization, - prior: prior, - initTerms: initTerms[n], - generator: generator }) - }) - var geneSets = models.map (function(model) { return model.geneSet }) - - var moveRate = conf.moveRate - ? extend ( { flip: 0, step: 0, jump: 0, randomize: 0 }, conf.moveRate) - : { flip: 1, step: 1, jump: 0, randomize: 0 } - extend (mcmc, - { - assocs: assocs, - paramSet: parameterization.paramSet, - prior: prior, - models: models, - nVariables: nVariables, +function retZero() { return 0; } - geneSets: geneSets, - hypergeometric: geneSets.map (function (geneSet) { - return assocs.hypergeometricPValues (geneSet) - }), - countsWithPrior: getCounts(models,prior), - computeCounts: function() { - return getCounts (this.models, this.paramSet.newCounts()) - }, - computeCountsWithPrior: function() { - return getCounts (this.models, this.prior) - }, - collapsedLogLikelihood: function() { - return this.computeCounts().logBetaBernoulliLikelihood (this.prior) - }, - quickCollapsedLogLikelihood: function() { - return this.countsWithPrior.subtract(this.prior).logBetaBernoulliLikelihood (this.prior) - }, - - generator: generator, - - moveRate: moveRate, - modelWeight: models.map (function(model) { - return model.relevantTerms.length - }), - - samples: 0, - samplesIncludingBurn: 0, - burn: 0, - finishedBurn: function() { return this.samplesIncludingBurn > this.burn }, +// 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); +}; - termStateOccupancy: models.map (function(model) { - return model.termName.map (function() { return 0 }) - }), - geneFalseOccupancy: models.map (function(model) { - return model.geneName.map (function() { return 0 }) - }), - - preMoveCallback: [], - postMoveCallback: [], - summaryCallback: [], - logMoves: logMoves, - logState: logState, - logProgress: logProgress, - logActiveTerms: logActiveTerms, - logMixing: logMixing, - logLogLikelihood: logLogLikelihood, - logRandomNumbers: function() { util.logRandomNumbers(this.generator) }, - - logTermPairs: logTermPairs, - stopLoggingTermPairs: stopLoggingTermPairs, +function retOne() { return 1; } - run: run, - hypergeometricSummary: hypergeometricSummary, - termSummary: termSummary, - geneFalsePosSummary: geneFalsePosSummary, - geneFalseNegSummary: geneFalseNegSummary, - termPairSummary: termPairSummary, - summary: summary - }) - } - module.exports = MCMC -}) () +// 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); +}; -},{"./bernoulli":2,"./model":4,"./parameterization":6,"./util":7,"assert":12,"jStat":8,"mersennetwister":10}],4:[function(require,module,exports){ -(function() { - var assert = require('assert'), - MersenneTwister = require('mersennetwister'), - Parameterization = require('./parameterization'), - util = require('./util'), - extend = util.extend - function getTermState(t) { return this._termState[t] } +// 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, Math.random); +}; - function setTermState(t,val) { - var model = this - assert (model.isRelevant[t]) - if (model._termState[t] != val) { - var delta = val ? +1 : -1 - model.assocs.genesByTerm[t].forEach (function(g) { - var newCount = model._nActiveTermsByGene[g] + delta - model._nActiveTermsByGene[g] = newCount - var inGeneSet = model.inGeneSet[g] - var isFalse = newCount > 0 ? !inGeneSet : inGeneSet - if (isFalse) - model._isFalseGene[g] = 1 - else - delete model._isFalseGene[g] - }) - if (val) - model._isActiveTerm[t] = true - else - delete model._isActiveTerm[t] - model._termState[t] = val - } - } - function setTermStates(termStateAssignment) { - for (var t in termStateAssignment) - if (termStateAssignment.hasOwnProperty(t)) - this.setTermState (t, termStateAssignment[t]) - } +function retIdent(i, j) { return i === j ? 1 : 0; } - function countTerm(model,counts,inc,t,state) { - var countObj = state ? counts.succ : counts.fail - var countParam = model.parameterization.names.termPrior[t] - var newCount = inc + (countObj[countParam] || 0) - if (newCount) - countObj[countParam] = newCount - else - delete countObj[countParam] - } - function countObs(model,counts,inc,isActive,g) { - var inGeneSet = model.inGeneSet[g] - // isActive inGeneSet param - // 0 0 !falsePos - // 0 1 falsePos - // 1 0 falseNeg - // 1 1 !falseNeg - var isFalse = isActive ? !inGeneSet : inGeneSet - var countObj = isFalse ? counts.succ : counts.fail - var countParam = (isActive ? model.parameterization.names.geneFalseNeg : model.parameterization.names.geneFalsePos)[g] - var newCount = inc + (countObj[countParam] || 0) - if (newCount) - countObj[countParam] = newCount - else - delete countObj[countParam] - } - - function getCounts() { - var model = this - var counts = model.paramSet.newCounts() - var param = model.param - model.relevantTerms.forEach (function (t) { - countTerm (model, counts, +1, t, model._termState[t]) - }) - model._nActiveTermsByGene.forEach (function (active, g) { - countObs (model, counts, +1, active > 0, g) - }) - return counts - } +// 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); +}; - function getCountDelta(termStateAssignment) { - var model = this - var param = model.param - var cd = model.paramSet.newCounts() - var nActiveTermsByGene = { - _val: {}, - val: function(g) { return g in this._val ? this._val[g] : model._nActiveTermsByGene[g] }, - add: function(g,delta) { var oldval = this.val(g); this._val[g] = oldval + delta; return oldval } - } - for (var t in termStateAssignment) - if (termStateAssignment.hasOwnProperty(t)) { - assert (model.isRelevant[t]) - var val = termStateAssignment[t] - if (model._termState[t] != val) { - countTerm (model, cd, -1, t, model._termState[t]) - countTerm (model, cd, +1, t, val) - var delta = val ? +1 : -1 - model.assocs.genesByTerm[t].forEach (function(g) { - var oldActive = nActiveTermsByGene.add(g,delta) - var newActive = nActiveTermsByGene.val(g) - if (oldActive != newActive) { - countObs (model, cd, -1, oldActive, g) - countObs (model, cd, +1, newActive, g) - } - }) - } - } - return cd - } - function invert(termStateAssignment) { - var inv = extend ({}, termStateAssignment) - for (var t in inv) - inv[t] = this._termState[t] - return inv - } +// Tests whether a matrix is symmetric +jStat.symmetric = function symmetric(arr) { + var issymmetric = true; + var size = arr.length; + var row, col; - function proposeFlipMove() { - var model = this - var term = util.randomElement (model.relevantTerms, model.generator) - var tsa = {} + if (arr.length !== arr[0].length) + return false; - tsa[term] = !model._termState[term] - return { termStates: tsa, - proposalHastingsRatio: 1 } - } + for (row = 0; row < size; row++) { + for (col = 0; col < size; col++) + if (arr[col][row] !== arr[row][col]) + return false; + } - function proposeStepMove() { - return proposeExchangeMove.bind(this) (getNeighbors) - } + return true; +}; - function proposeJumpMove() { - return proposeExchangeMove.bind(this) (getActives) - } - function getNeighbors(model,term) { return model.relevantNeighbors[term] } - function getActives(model,term) { return model.relevantTerms } +// Set all values to zero. +jStat.clear = function clear(arr) { + return jStat.alter(arr, retZero); +}; - function proposeExchangeMove (getExchangePartners) { - var model = this - var move = { termStates: {}, - proposalHastingsRatio: 1 } - var activeTerms = model.activeTerms() - if (activeTerms.length > 0) { - var term = util.randomElement (activeTerms, model.generator) - var nbrs = getExchangePartners(model,term) - if (nbrs.length > 0) { - var nbr = util.randomElement (nbrs, model.generator) - if (!this._termState[nbr]) { - move.termStates[term] = false - move.termStates[nbr] = true - move.proposalHastingsRatio = nbrs.length / getExchangePartners(model,nbr).length - } - } - } - return move - } - function proposeRandomizeMove() { - var model = this - var tsa = {} - model.relevantTerms.forEach (function (term) { - tsa[term] = (model.generator.random() > 0.5) - }) - return { termStates: tsa, - proposalHastingsRatio: 1 } - } +// Generate sequence. +jStat.seq = function seq(min, max, length, func) { + if (!isFunction(func)) + func = false; - function sampleMoveCollapsed(move,counts) { - move.delta = this.getCountDelta (move.termStates) - move.logLikelihoodRatio = counts.deltaLogBetaBernoulliLikelihood (move.delta) - move.hastingsRatio = move.proposalHastingsRatio * Math.exp(move.logLikelihoodRatio) - if (move.hastingsRatio >= 1 || this.generator.random() < move.hastingsRatio) { - this.setTermStates (move.termStates) - move.accepted = true - counts.accum (move.delta) - } else - move.accepted = false - return move.accepted - } - - function Model (conf) { - var model = this + var arr = []; + var hival = calcRdx(min, max); + var step = (max * hival - min * hival) / ((length - 1) * hival); + var current = min; + var cnt; - var assocs = conf.assocs - var termName = assocs.ontology.termName - var geneName = assocs.geneName + // Current is assigned using a technique to compensate for IEEE error. + // TODO: Needs better implementation. + for (cnt = 0; + current <= max; + cnt++, current = (min * hival + step * hival * cnt) / hival) { + arr.push((func ? func(current, cnt) : current)); + } - var validation = assocs.validateGeneNames (conf.geneSet) - if (validation.missingGeneNames.length > 0) - console.warn ("Warning: the following genes were not found in the associations list: " + validation.missingGeneNames) - var geneSet = validation.resolvedGeneIndices - - // "relevant" terms are ones which have at least one associated gene in the geneSet, - // excluding those which are indistinguishable from other terms in the ontology - var relevantTerms = assocs.relevantTermsForGeneSet (geneSet) - var isRelevant = util.listToCounts (relevantTerms) - function relevantFilter(termList) { - return termList.filter (util.objPredicate(isRelevant)) - } - var relevantParents = assocs.ontology.parents.map (relevantFilter) - var relevantChildren = assocs.ontology.children.map (relevantFilter) + return arr; +}; - // initial state - var termState = termName.map (function() { return false }) - if (conf.initTerms) - conf.initTerms.forEach (function (initTermName) { - if (initTermName in assocs.ontology.termIndex) { - var initTerm = assocs.ontology.termIndex[initTermName] - if (isRelevant[initTerm]) - termState[initTerm] = true - } - }) - // parameterization - var parameterization = conf.parameterization || new Parameterization (conf) +// 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. - // this object encapsulates both the graphical model itself, - // and an assignment of state to the model variables - extend (model, - { - // the graphical model - assocs: assocs, - geneSet: geneSet, - termName: termName, - geneName: geneName, +// Quick reference. +var jProto = jStat.prototype; - inGeneSet: geneName.map (function() { return false }), +// Default length. +jProto.length = 0; - isRelevant: isRelevant, - relevantTerms: relevantTerms, - relevantNeighbors: relevantParents.map (function (parents, term) { - return util.removeDups - ([parents, - relevantChildren[term]] - .concat (assocs.ontology.parents[term].map (function (parent) { - return relevantChildren[parent] - })) - .reduce (function(r,l) { return r.concat(l) }, [])) - .filter (function(t) { return t != term }) - .map(util.parseDecInt) - .sort(util.numCmp) - }), - - parameterization: parameterization, - paramSet: conf.paramSet || parameterization.paramSet, - prior: conf.prior || parameterization.paramSet.laplacePrior(), +// 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; - genes: function() { return this.assocs.genes() }, - terms: function() { return this.assocs.terms() }, - // current state of the model - _termState: termState, - _isActiveTerm: {}, +// Return a clean array. +jProto.toArray = function toArray() { + return this.length > 1 ? slice.call(this) : slice.call(this)[0]; +}; - _nActiveTermsByGene: assocs.termsByGene.map (function(terms) { - return terms.reduce (function(accum,t) { - return accum + (termState[t] ? 1 : 0) - }, 0) - }), - _isFalseGene: {}, - - hasActiveTerms: function() { return Object.keys(this._isActiveTerm).length > 0 }, - activeTerms: function() { - return Object.keys(this._isActiveTerm).sort(util.numCmp) - }, - falseGenes: function() { - return Object.keys(this._isFalseGene).sort(util.numCmp) - }, - - getTermState: getTermState, - setTermState: setTermState, - setTermStates: setTermStates, - invert: invert, - - getCounts: getCounts, - getCountDelta: getCountDelta, - - toJSON: function() { - var model = this - return model.activeTerms() - .map (function(t) { return model.termName[t] }) - }, +// Map a function to a matrix or vector. +jProto.map = function map(func, toAlter) { + return jStat(jStat.map(this, func, toAlter)); +}; - // MCMC methods - generator: conf.generator || new MersenneTwister (conf.seed), - - proposeFlipMove: proposeFlipMove, - proposeStepMove: proposeStepMove, - proposeJumpMove: proposeJumpMove, - proposeRandomizeMove: proposeRandomizeMove, - sampleMoveCollapsed: sampleMoveCollapsed - }) +// 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)); +}; - geneSet.forEach (function(g) { model.inGeneSet[g] = true }) - termState.forEach (function(s,t) { if (s) model._isActiveTerm[t] = true }) - model._nActiveTermsByGene.map (function(terms,gene) { - if (terms > 0 ? !model.inGeneSet[gene] : model.inGeneSet[gene]) - model._isFalseGene[gene] = true - }) - } +// Destructively alter an array. +jProto.alter = function alter(func) { + jStat.alter(this, func); + return this; +}; - module.exports = Model -}) () -},{"./parameterization":6,"./util":7,"assert":12,"mersennetwister":10}],5:[function(require,module,exports){ -(function() { - var util = require('./util'), - extend = util.extend, - assert = require('assert') +// 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(' ')); - function toJSON (conf) { - var onto = this - var json = [] - conf = extend ({ compress: false, - includeTermInfo: true }, - conf) - var parentLookup = conf.compress - ? function(j) { return j } - : function(j) { return onto.termName[j] } - for (var i = 0; i < onto.terms(); ++i) { - json.push ([onto.termName[i]].concat (onto.parents[i].map (parentLookup))) - } - var result = { "termParents" : json } - if (conf.includeTermInfo && onto.termInfo) - result.termInfo = onto.termInfo - var doNotAnnotate = util.iota(onto.terms()).filter (util.objPredicate (onto.doNotAnnotate)) - if (doNotAnnotate.length) - result.doNotAnnotate = doNotAnnotate.map (onto.getTermName.bind(onto)) - return result - } - function toposortTermIndex (onto) { - // Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558–562, doi:10.1145/368996.369025 - // https://en.wikipedia.org/wiki/Topological_sorting - var S = [], L = [] - var nParents = [], edges = 0 - for (var c = 0; c < onto.terms(); ++c) { - nParents[c] = onto.parents[c].length - edges += nParents[c] - if (nParents[c] == 0) - S.push (c) - } - while (S.length > 0) { - var n = S.shift() - L.push (n) - onto.children[n].forEach (function(m) { - --edges - if (--nParents[m] == 0) - S.push (m) - }) - } - if (edges > 0) - return undefined +// 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(' ')); - return L - } - function isCyclic() { - var L = toposortTermIndex(this) - return typeof(L) === 'undefined' - } +// Extend prototype with simple shortcut methods. +(function(funcs) { + for (var i = 0; i < funcs.length; i++) (function(passfunc) { + jProto[passfunc] = new Function( + 'return jStat(jStat.' + passfunc + '.apply(null, arguments));'); + })(funcs[i]); +})('create zeros ones rand identity'.split(' ')); - function toposortTermIndexOrDie (onto) { - var L = toposortTermIndex(onto) - if (typeof(L) === 'undefined') - throw new Error ("Ontology graph is not a DAG") - - return L - } - function toposort() { - var onto = this +// Exposing jStat. +return jStat; - if (onto.isToposorted()) - return onto - - var L = toposortTermIndexOrDie (onto) +}(Math)); +(function(jStat, Math) { - var json = onto.toJSON() - var toposortedJson = { termParents: util.permuteList (json.termParents, L) } - if (json.termInfo) - toposortedJson.termInfo = util.permuteList (json.termInfo, L) - - return new Ontology (toposortedJson) - } +var isFunction = jStat.utils.isFunction; - function isToposorted() { - for (var i = 0; i < this.terms(); ++i) - if (this.parents[i].some (function(p) { return p >= i })) - return false; - return true; - } +// Ascending functions for sort +function ascNum(a, b) { return a - b; } - function toposortTermOrder() { - var onto = this - var L = toposortTermIndexOrDie (onto) - var order = onto.termName.map (function() { return null }) - L.forEach (function (term, index) { order[term] = index }) - return order - } +function clip(arg, min, max) { + return Math.max(min, Math.min(arg, max)); +} - function buildChildren (onto) { - onto.children = onto.parents.map (function() { return [] }) - for (var c = 0; c < onto.terms(); ++c) - onto.parents[c].forEach (function(p) { - onto.children[p].push (c) - }) - } - function equals (onto) { - return JSON.stringify (this.toJSON({'compress':true})) == JSON.stringify (onto.toJSON({'compress':true})); - } +// sum of an array +jStat.sum = function sum(arr) { + var sum = 0; + var i = arr.length; + while (--i >= 0) + sum += arr[i]; + return sum; +}; - function transitiveClosure() { - var onto = this - if (!('_closure' in onto)) { - var clos = [] - var L = toposortTermIndexOrDie (onto) - L.forEach (function(n) { - var closIndex = {} - onto.parents[n].forEach (function(p) { - clos[p].forEach (function(c) { - closIndex[c] = 1 - }) - }) - closIndex[n] = 1 - clos[n] = Object.keys(closIndex) - .sort (util.numCmp) - }) - onto._closure = clos - } - return onto._closure - } - function getTermInfo (name) { - var onto = this - if (name in onto.termIndex && onto.termInfo) - return onto.termInfo[onto.termIndex[name]] - return undefined - } +// sum squared +jStat.sumsqrd = function sumsqrd(arr) { + var sum = 0; + var i = arr.length; + while (--i >= 0) + sum += arr[i] * arr[i]; + return sum; +}; - function subgraphRootedAt (rootTermList) { - var onto = this - var inSubgraph = {} - var inSubFunc = util.objPredicate(inSubgraph) - rootTermList.forEach (function (tn) { - if (tn in onto.termIndex) - inSubgraph[onto.termIndex[tn]] = true - }) - onto.toposortTermIndex().forEach (function (ti) { - var parents = onto.parents[ti] - inSubgraph[ti] = inSubgraph[ti] || (parents.length ? parents.every(inSubFunc) : false) - }) - return onto.subgraph (inSubFunc) - } - function subgraphWithAncestors (termList) { - var onto = this - var inSubgraph = {} - var closure = onto.transitiveClosure() - termList.forEach (function (tn) { - if (tn in onto.termIndex) - closure[onto.termIndex[tn]].forEach (function(c) { - inSubgraph[c] = true - }) - }) - return onto.subgraph (util.objPredicate (inSubgraph)) - } +// 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; +}; - function subgraph (termIndexPredicate) { - var onto = this - var termParents = [], - termInfo = onto.termInfo ? [] : undefined - onto.termName.forEach (function (tn, ti) { - if (termIndexPredicate(ti)) { - termParents.push ([tn].concat (onto.parents[ti].filter(termIndexPredicate).map (onto.getTermName.bind(onto)))) - if (termInfo) - termInfo.push (onto.termInfo[ti]) - } - }) - - return new Ontology ({ termParents: termParents, termInfo: termInfo }) - } - - function Ontology (conf) { - var onto = this - extend (onto, - { 'termName': [], // this is actually the term ID. ahem - 'termIndex': {}, // mapping from term ID to term index - 'termInfo': null, // the array of term names (in the OBO sense) goes here, if available - 'doNotAnnotate': {}, - 'parents': [], - 'children': [], - 'terms': function() { return this.termName.length }, - 'toJSON': toJSON, - 'isCyclic': isCyclic, - 'isToposorted': isToposorted, - 'toposort': toposort, - 'toposortTermOrder': toposortTermOrder, - 'toposortTermIndex': function() { return toposortTermIndexOrDie(this) }, - 'equals': equals, - 'transitiveClosure': transitiveClosure, - 'getTermInfo': getTermInfo, - 'getTermName': function(ti) { return this.termName[ti] }, - 'subgraphRootedAt': subgraphRootedAt, - 'subgraphWithAncestors': subgraphWithAncestors, - 'subgraph': subgraph - }) +// 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; +}; - if (Object.prototype.toString.call(conf) === '[object Array]') - conf = { 'termParents': conf } - - if ('termParents' in conf) { - var extTermParents = [] - conf.termParents.forEach (function (tp) { - extTermParents.push (tp) - var term = tp[0] - onto.termIndex[term] = onto.terms() - onto.termName.push (term) - }) - conf.termParents.forEach (function (tp) { - for (var n = 1; n < tp.length; ++n) { - if (typeof(tp[n]) === 'string' && !(tp[n] in onto.termIndex)) { - onto.termIndex[tp[n]] = onto.terms() - onto.termName.push (tp[n]) - extTermParents.push ([tp[n]]) - } - } - }) - - extTermParents.forEach (function (tp,term) { - onto.parents[term] = tp.slice([1]) - .map (function(n) { - return typeof(n) === 'number' ? n : onto.termIndex[n] - }) - }) - } else - throw new Error ("Can't parse Ontology config") +// product of an array +jStat.product = function product(arr) { + var prod = 1; + var i = arr.length; + while (--i >= 0) + prod *= arr[i]; + return prod; +}; - if ('termInfo' in conf) - onto.termInfo = conf.termInfo - if ('doNotAnnotate' in conf) - conf.doNotAnnotate.forEach (function (tn) { - if (tn in onto.termIndex) - onto.doNotAnnotate[onto.termIndex[tn]] = true - }) - - buildChildren (onto) - } +// 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; +}; - module.exports = Ontology -}) () -},{"./util":7,"assert":12}],6:[function(require,module,exports){ -(function() { - var assert = require('assert'), - BernoulliParamSet = require('./bernoulli').BernoulliParamSet, - util = require('./util'), - extend = util.extend +// 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; +}; - function Parameterization (conf) { - var parameterization = this - conf = extend ({ termPrior: function(term) { return 't' }, - geneFalsePos: function(gene) { return 'fp' }, - geneFalseNeg: function(gene) { return 'fn' }, - }, - conf) +// 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; +}; - var assocs = conf.assocs - var termName = assocs.ontology.termName - var geneName = assocs.geneName - var params = {} - function init(f) { - return function(x) { - var name = f(x) - params[name] = 1 - return name - } - } - - parameterization.names = { - termPrior: termName.map (init (conf.termPrior)), - geneFalsePos: geneName.map (init (conf.geneFalsePos)), - geneFalseNeg: geneName.map (init (conf.geneFalseNeg)) - } - - parameterization.paramSet = new BernoulliParamSet (params) - } +// mean value of an array +jStat.mean = function mean(arr) { + return jStat.sum(arr) / arr.length; +}; - module.exports = Parameterization -}) () -},{"./bernoulli":2,"./util":7,"assert":12}],7:[function(require,module,exports){ -(function() { - var extend = require('util')._extend, - assert = require('assert'), - jStat = require('jStat').jStat +// mean squared error (MSE) +jStat.meansqerr = function meansqerr(arr) { + return jStat.sumsqerr(arr) / arr.length; +}; - function numCmp (a, b) { return a-b } - function reverseCmp (comparisonFunc) { - return function(a,b) { - return comparisonFunc(b,a) - } - } +// geometric mean of an array +jStat.geomean = function geomean(arr) { + return Math.pow(jStat.product(arr), 1 / arr.length); +}; - function sortAscending (list) { - return list.sort (numCmp) - } - function listToCounts (list) { - var c = {} - list.forEach (function(x) { - c[x] = (c[x] || 0) + 1 - }) - return c - } - - function removeDups (list) { - return Object.keys (listToCounts (list)) - } +// 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 ]; +}; - function parseDecInt (x) { - return parseInt (x) - } - function objPredicate (obj) { - return function(x) { - return obj[x] ? true : false - } - } +// cumulative sum of an array +jStat.cumsum = function cumsum(arr) { + return jStat.cumreduce(arr, function (a, b) { return a + b; }); +}; - function negate (predicateFunc) { - return function () { - return !predicateFunc.apply(this, arguments) - } - } - function sumList (list) { - return list.reduce (function(tot,x) { return tot+x }, 0) - } - - function randomElement (list, generator) { - return list.length > 0 ? list [Math.floor (generator.random() * list.length)] : undefined - } +// cumulative product of an array +jStat.cumprod = function cumprod(arr) { + return jStat.cumreduce(arr, function (a, b) { return a * b; }); +}; - function randomIndex (distrib, generator) { - var sum = sumList (distrib) - var rnd = generator.random() * sum - for (var idx = 0; idx < distrib.length; ++idx) - if ((rnd -= distrib[idx]) <= 0) - return idx - return undefined - } - function randomKey (obj, generator) { - var keys = Object.keys (obj) - var distrib = keys.map (function(k) { return obj[k] }) - return keys [randomIndex (distrib, generator)] - } +// 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; +}; - function iota(n) { - var list = [] - for (var i = 0; i < n; ++i) - list.push(i) - return list - } - function sortKeys (obj, sortFunc, keys) { - sortFunc = sortFunc || numCmp - return (keys || Object.keys(obj)).sort (function(a,b) { - return sortFunc (obj[a], obj[b]) - }) +// ranks of an array +jStat.rank = function (arr) { + var arrlen = arr.length; + var sorted = arr.slice().sort(ascNum); + var ranks = new Array(arrlen); + for (var i = 0; i < arrlen; i++) { + var first = sorted.indexOf(arr[i]); + var last = sorted.lastIndexOf(arr[i]); + if (first === last) { + var val = first; + } else { + var val = (first + last) / 2; } + ranks[i] = val + 1; + } + return ranks; +}; - function sortIndices (order, indexList, sortFunc) { - sortFunc = sortFunc || numCmp - return (indexList || iota(order.length)).sort (function(a,b) { - return sortFunc (order[a], order[b]) - }) - } - function permuteList (list, order) { - return order.map (function(idx) { return list[idx] }) - } +// 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; - function keyValListToObj (keyValList) { - var obj = {} - keyValList.forEach (function (keyVal) { - obj[keyVal[0]] = keyVal[1] - }) - return obj + 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; } + } - function values (obj) { - return Object.keys(obj).map (function(k) { return obj[k] }) - } + return numMaxCount === 0 ? mode_arr[0] : mode_arr; +}; - function commonKeys (obj1, obj2) { - return Object.keys(obj1).filter (function(k) { return obj2.hasOwnProperty(k) }) - } - function commonElements (list1, list2) { - return commonKeys (listToCounts(list1), listToCounts(list2)) - } +// range of an array +jStat.range = function range(arr) { + return jStat.max(arr) - jStat.min(arr); +}; - function logBinomialCoefficient (n, k) { - return jStat.gammaln(n+1) - jStat.gammaln(k+1) - jStat.gammaln(n-k+1) - } - - function logBetaBinomial (alpha, beta, n, k) { - return logBinomialCoefficient(n,k) + logBetaBernoulli(alpha,beta,k,n-k) - } - - function logBetaBernoulli (alpha, beta, succ, fail) { - return jStat.betaln(alpha+succ,beta+fail) - jStat.betaln(alpha,beta) - } +// 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)); +}; - function autocorrelation (list, points) { - points = points || iota(list.length-1) - var mean = jStat.mean(list), variance = jStat.variance(list) - var list_minus_mean = list.map (function(x) { return x - mean }) - var R = {} - points.forEach (function(tau) { - var R_tau = [] - for (var i = 0; i + tau < list.length; ++i) - R_tau.push (list_minus_mean[i] * list_minus_mean[i + tau]) - R[tau] = jStat.mean(R_tau) / variance - }) - return R - } +// 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; +}; - function arraysEqual (a, b) { - if (a === b) return true; - if (a == null || b == null) return false - if (a.length != b.length) return false - for (var i = 0; i < a.length; ++i) - if (a[i] !== b[i]) return false - return true - } +// 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)); +}; - function approxEqual (a, b, epsilon) { - epsilon = epsilon || .0001 - if (Math.max (Math.abs(a), Math.abs(b)) > 0) - return Math.abs(a-b) / Math.max (Math.abs(a), Math.abs(b)) < epsilon - else - return Math.abs(a-b) < epsilon - } - - function assertApproxEqual (a, b, epsilon, message) { - assert (approxEqual(a,b,epsilon), message || ("Difference between a ("+a+") and b ("+b+") is too large")) - } - - function plural (count, singular, plural) { - plural = plural || (singular + 's') - return count + ' ' + (count == 1 ? singular : plural) - } - - function toHHMMSS (milliseconds) { - var sec_num = Math.floor (milliseconds / 1000) - var hours = Math.floor (sec_num / 3600) - var minutes = Math.floor ((sec_num - (hours * 3600)) / 60) - var seconds = sec_num - (hours * 3600) - (minutes * 60) - if (hours < 10) - hours = "0" + hours - if (minutes < 10) - minutes = "0" + minutes - if (seconds < 10) - seconds = "0" + seconds +// mean deviation (mean absolute deviation) of an array +jStat.meandev = function meandev(arr) { + var devSum = 0; + var mean = jStat.mean(arr); + var i; + for (i = arr.length - 1; i >= 0; i--) + devSum += Math.abs(arr[i] - mean); + return devSum / arr.length; +}; - return hours+':'+minutes+':'+seconds - } - function progressLogger (pastTenseVerb, pluralNoun) { - var startTime = Date.now(), lastTime = startTime, delay = 1000 - return function (stepsCompleted, totalSteps) { - var nowTime = Date.now() - if (nowTime - lastTime > delay) { - lastTime = nowTime - delay = Math.min (30000, delay*2) - var progress = stepsCompleted / totalSteps - console.warn (pastTenseVerb + " " + stepsCompleted + "/" + totalSteps + " " + pluralNoun + " (" + Math.round(100*progress) + "%), estimated time left " + toHHMMSS ((1/progress - 1) * (nowTime - startTime))) - } - } - } - - function logRandomNumbers(generator,nMax) { - var rnd = generator.int, nRnd = 0 - generator.int = function() { - var r = rnd.apply (this, arguments) - if (typeof(nMax) === 'undefined' || nRnd < nMax) - console.warn ("Random number #" + (++nRnd) + ": " + r) - return r - } - } +// median deviation (median absolute deviation) of an array +jStat.meddev = function meddev(arr) { + var devSum = 0; + var median = jStat.median(arr); + var i; + for (i = arr.length - 1; i >= 0; i--) + devSum += Math.abs(arr[i] - median); + return devSum / arr.length; +}; - function HSVtoRGB(h, s, v) { - var r, g, b, i, f, p, q, t - i = Math.floor(h * 6) - f = h * 6 - i - p = v * (1 - s) - q = v * (1 - f * s) - t = v * (1 - (1 - f) * s) - switch (i % 6) { - case 0: r = v, g = t, b = p; break - case 1: r = q, g = v, b = p; break - case 2: r = p, g = v, b = t; break - case 3: r = p, g = q, b = v; break - case 4: r = t, g = p, b = v; break - case 5: r = v, g = p, b = q; break - } - return { - r: Math.round(r * 255), - g: Math.round(g * 255), - b: Math.round(b * 255) - } - } - module.exports.numCmp = numCmp - module.exports.reverseCmp = reverseCmp - module.exports.sortAscending = sortAscending - module.exports.listToCounts = listToCounts - module.exports.removeDups = removeDups - module.exports.parseDecInt = parseDecInt - module.exports.objPredicate = objPredicate - module.exports.negate = negate - module.exports.sumList = sumList - module.exports.randomElement = randomElement - module.exports.randomIndex = randomIndex - module.exports.randomKey = randomKey - module.exports.iota = iota - module.exports.sortKeys = sortKeys - module.exports.sortIndices = sortIndices - module.exports.permuteList = permuteList - module.exports.keyValListToObj = keyValListToObj - module.exports.values = values - module.exports.commonKeys = commonKeys - module.exports.commonElements = commonElements - module.exports.logBinomialCoefficient = logBinomialCoefficient - module.exports.logBetaBinomial = logBetaBinomial - module.exports.logBetaBernoulli = logBetaBernoulli - module.exports.autocorrelation = autocorrelation - module.exports.arraysEqual = arraysEqual - module.exports.approxEqual = approxEqual - module.exports.assertApproxEqual = assertApproxEqual - module.exports.toHHMMSS = toHHMMSS - module.exports.plural = plural - module.exports.progressLogger = progressLogger - module.exports.logRandomNumbers = logRandomNumbers - module.exports.HSVtoRGB = HSVtoRGB - module.exports.extend = extend -}) () +// coefficient of variation +jStat.coeffvar = function coeffvar(arr) { + return jStat.stdev(arr) / jStat.mean(arr); +}; -},{"assert":12,"jStat":8,"util":16}],8:[function(require,module,exports){ -this.j$ = this.jStat = (function(Math, undefined) { -// For quick reference. -var concat = Array.prototype.concat; -var slice = Array.prototype.slice; -var toString = Object.prototype.toString; +// 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 ] + ]; +}; -// 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)); -} +// 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; -var isArray = Array.isArray || function isArray(arg) { - return toString.call(arg) === '[object Array]'; -}; + 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]; + } -function isFunction(arg) { - return toString.call(arg) === '[object Function]'; -} + return quantileVals; +}; +// Returns the k-th percentile of values in a range, where k is in the +// range 0..1, exclusive. +jStat.percentile = function percentile(arr, k) { + var _arr = arr.slice().sort(ascNum); + var realIndex = k * (_arr.length - 1); + var index = parseInt(realIndex); + var frac = realIndex - index; -function isNumber(arg) { - return typeof arg === 'number' && arg === arg; + if (index + 1 < _arr.length) { + return _arr[index] * (1 - frac) + _arr[index + 1] * frac; + } else { + return _arr[index]; + } } -// Converts the jStat matrix to vector. -function toVector(arr) { - return concat.apply([], arr); -} - +// 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; -// The one and only jStat constructor. -function jStat() { - return new jStat._init(arguments); -} + if (kind === 'strict') + strict = true; + for (i = 0; i < len; i++) { + value = arr[i]; + if ((strict && value < score) || + (!strict && value <= score)) { + counter++; + } + } -// TODO: Remove after all references in src files have been removed. -jStat.fn = jStat.prototype; + return counter / len; +}; -// 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) { +// Histogram (bin count) data +jStat.histogram = function histogram(arr, bins) { + var first = jStat.min(arr); + var binCnt = bins || 4; + var binWidth = (jStat.max(arr) - first) / binCnt; + var len = arr.length; + var bins = []; var i; - // 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; + 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; - // Otherwise must be a vector. - } else { - this[0] = isFunction(args[1]) ? jStat.map(args[0], args[1]) : args[0]; - this.length = 1; - } + return bins; +}; - // 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()); +// 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; - // 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; - } + for (i = 0; i < arr1Len; i++) + sq_dev[i] = (arr1[i] - u) * (arr2[i] - v); - return this; + return jStat.sum(sq_dev) / (arr1Len - 1); }; -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 +// (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); }; - -// 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 (var 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) - }; -}; + // (spearman's) rank correlation coefficient, sp +jStat.spearmancoeff = function (arr1, arr2) { + arr1 = jStat.rank(arr1); + arr2 = jStat.rank(arr2); + var arr1dev = jStat.deviation(arr1); + var arr2dev = jStat.deviation(arr2); + return jStat.sum(arr1dev.map(function (x, i) { + return x * arr2dev[i]; + })) / + Math.sqrt(jStat.sum(arr1dev.map(function (x) { + return Math.pow(x, 2); + })) * jStat.sum(arr2dev.map(function (x) { + return Math.pow(x, 2); + })) + ); +} -// 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]; -}; +// 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 (i = 0; i < len; i++) + skewSum += Math.pow((arr[i] - mu) / sigma, n); -// return row as array -// rowa([[1,2],[3,4]],0) -> [1,2] -jStat.rowa = function rowa(arr, i) { - return jStat.row(arr, i); + return skewSum / arr.length; }; - -// 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(i) { - 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; +// (pearson's) moment coefficient of skewness +jStat.skewness = function skewness(arr) { + return jStat.stanMoment(arr, 3); }; - -// 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] }); +// (pearson's) (excess) kurtosis +jStat.kurtosis = function kurtosis(arr) { + return jStat.stanMoment(arr, 4) - 3; }; -// 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; -}; +var jProto = jStat.prototype; -// 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; -}; +// 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(' ')); -// 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]; +// 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(' ')); - rows = arr.length; - cols = arr[0].length; - for (var i = 0; i < cols; i++) { - objArr = new Array(rows); - for (j = 0; j < rows; j++) - objArr[j] = arr[j][i]; - obj.push(objArr); - } +// 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); - // If obj is vector, return only single array. - return obj.length === 1 ? obj[0] : obj; -}; + // 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])) { + var 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; -// 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; + // Otherwise we curry the function args and call normally. + } else { + var callbackFunction = undefined; + var curriedFunction = function curriedFunction(vector) { + return jStat[passfunc].apply(tmpthis, [vector].concat(args)); + } + } - if (!isArray(arr[0])) - arr = [arr]; + // 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; + } - nrow = arr.length; - ncol = arr[0].length; - res = toAlter ? arr : new Array(nrow); + // Otherwise run on the vector. + return curriedFunction(this[0]); + }; + })(funcs[i]); +})('quantiles percentileOfScore'.split(' ')); - 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); - } +}(this.jStat, Math)); +// Special functions // +(function(jStat, Math) { - return res.length === 1 ? res[0] : res; +// Log-gamma function +jStat.gammaln = function gammaln(x) { + var j = 0; + var cof = [ + 76.18009172947146, -86.50532032941677, 24.01409824083091, + -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5 + ]; + 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; }; -// 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]); +// 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, sum, ysq; + 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; + } } - return res.length === 1 ? res[0] : res; + 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; }; -// Destructively alter an array. -jStat.alter = function alter(arr, func) { - return jStat.map(arr, func, true); +// 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); }; -// 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; +// 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, endval; - if (isFunction(cols)) { - func = cols; - cols = rows; + 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 (var i = 0; i < rows; i++) { - res[i] = new Array(cols); - for (j = 0; j < cols; j++) - res[i][j] = func(i, j); + 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 res; + return (1 - h * Math.exp(-x + a * Math.log(x) - (aln))); }; - -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); +// natural log factorial of n +jStat.factorialln = function factorialln(n) { + return n < 0 ? NaN : jStat.gammaln(n + 1); }; - -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); +// factorial of n +jStat.factorial = function factorial(n) { + return n < 0 ? NaN : jStat.gammafn(n + 1); }; - -// 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, Math.random); +// 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); }; -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); +jStat.combinationln = function combinationln(n, m){ + return jStat.factorialln(n) - jStat.factorialln(m) - jStat.factorialln(n - m); }; -// Tests whether a matrix is symmetric -jStat.symmetric = function symmetric(arr) { - var issymmetric = true; - var size = arr.length; - var row, col; - - if (arr.length !== arr[0].length) - return false; +// permutations of n, m +jStat.permutation = function permutation(n, m) { + return jStat.factorial(n) / jStat.factorial(n - m); +}; - for (row = 0; row < size; row++) { - for (col = 0; col < size; col++) - if (arr[col][row] !== arr[row][col]) - return false; - } - return true; +// 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); }; -// Set all values to zero. -jStat.clear = function clear(arr) { - return jStat.alter(arr, retZero); +// natural logarithm of beta function +jStat.betaln = function betaln(x, y) { + return jStat.gammaln(x) + jStat.gammaln(y) - jStat.gammaln(x + y); }; -// Generate sequence. -jStat.seq = function seq(min, max, length, func) { - if (!isFunction(func)) - func = false; +// 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; - var arr = []; - var hival = calcRdx(min, max); - var step = (max * hival - min * hival) / ((length - 1) * hival); - var current = min; - var cnt; + // 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; - // Current is assigned using a technique to compensate for IEEE error. - // TODO: Needs better implementation. - for (cnt = 0; - current <= max; - cnt++, current = (min * hival + step * hival * cnt) / hival) { - arr.push((func ? func(current, cnt) : current)); + 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 arr; + return h; }; -// 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 = []; - step = step || 1; - if (end === undefined) { - 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; -}; - +// 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; -// 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 && end === undefined && step === undefined) { - return jStat.copy(list); - } + 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)); + } - 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; + 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; } - function slice(list, rcSlice) { - rcSlice = rcSlice || {}; - if (isNumber(rcSlice.row)) { - if (isNumber(rcSlice.col)) - return list[rcSlice.row][rcSlice.col]; - var row = jStat.rowa(list, rcSlice.row); - var colSlice = rcSlice.col || {}; - return _slice(row, colSlice.start, colSlice.end, colSlice.step); - } + return x; +}; - if (isNumber(rcSlice.col)) { - var col = jStat.cola(list, rcSlice.col); - var rowSlice = rcSlice.row || {}; - return _slice(col, rowSlice.start, rowSlice.end, rowSlice.step); - } - var rowSlice = rcSlice.row || {}; - var 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); - }); +// Returns the error function erf(x) +jStat.erf = function erf(x) { + var cof = [-1.3026537197817094, 6.4196979235649026e-1, 1.9476473204185836e-2, + -9.561514786808631e-3, -9.46595344482036e-4, 3.66839497852761e-4, + 4.2523324806907e-5, -2.0278578112534e-5, -1.624290004647e-6, + 1.303655835580e-6, 1.5626441722e-8, -8.5238095915e-8, + 6.529054439e-9, 5.059343495e-9, -9.91364156e-10, + -2.27365122e-10, 9.6467911e-11, 2.394038e-12, + -6.886027e-12, 8.94487e-13, 3.13092e-13, + -1.12708e-13, 3.81e-16, 7.106e-15, + -1.523e-15, -9.4e-17, 1.21e-16, + -2.8e-17]; + 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; } - 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) { - 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; - var 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; - var 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) { - 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; - var ml = jStat.arange(rcSlice.row.start, - Math.min(A.length, rcSlice.row.end), - rcSlice.row.step); - var 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; -}; + t = 2 / (2 + x); + ty = 4 * t - 2; + for(; j > 0; j--) { + tmp = d; + d = ty * d - dd + cof[j]; + dd = tmp; + } -// [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; + res = t * Math.exp(-x * x + 0.5 * (cof[0] + ty * d) - dd); + return isneg ? res - 1 : 1 - res; }; -// 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; - }); - }); +// Returns the complmentary error function erfc(x) +jStat.erfc = function erfc(x) { + return 1 - jStat.erf(x); }; -// 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; +// 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; +}; -// 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; +// 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++) { + if (x === 0 || x === 1) + return x; + err = jStat.ibeta(x, a, b) - p; + t = Math.exp(a1 * Math.log(x) + b1 * Math.log(1 - x) + afac); + u = err / t; + x -= (t = u / (1 - 0.5 * Math.min(1, u * (a1 / x - b1 / (1 - x))))); + if (x <= 0) + x = 0.5 * (x + t); + if (x >= 1) + x = 0.5 * (x + t + 1); + if (Math.abs(t) < EPS * x && j > 0) + break; + } + return x; +}; -// Return a clean array. -jProto.toArray = function toArray() { - return this.length > 1 ? slice.call(this) : slice.call(this)[0]; +// Returns the incomplete beta function I_x(a,b) +jStat.ibeta = function ibeta(x, a, b) { + // Factors in front of the continued fraction. + var bt = (x === 0 || x === 1) ? 0 : + Math.exp(jStat.gammaln(a + b) - jStat.gammaln(a) - + jStat.gammaln(b) + a * Math.log(x) + b * + Math.log(1 - x)); + if (x < 0 || x > 1) + return false; + if (x < (a + 1) / (a + b + 2)) + // Use continued fraction directly. + return bt * jStat.betacf(x, a, b) / a; + // else use continued fraction after making the symmetry transformation. + return 1 - bt * jStat.betacf(1 - x, b, a) / b; }; -// Map a function to a matrix or vector. -jProto.map = function map(func, toAlter) { - return jStat(jStat.map(this, func, toAlter)); +// Returns a normal deviate (mu=0, sigma=1). +// If n and m are specified it returns a object of normal deviates. +jStat.randn = function randn(n, m) { + var u, v, x, y, q, mat; + if (!m) + m = n; + if (n) + return jStat.create(n, m, function() { return jStat.randn(); }); + do { + u = Math.random(); + v = 1.7156 * (Math.random() - 0.5); + x = u - 0.449871; + y = Math.abs(v) + 0.386595; + q = x * x + y * (0.19600 * y - 0.25472 * x); + } while (q > 0.27597 && (q > 0.27846 || v * v > -4 * Math.log(u) * u * u)); + return v / u; }; -// 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)); +// Returns a gamma deviate by the method of Marsaglia and Tsang. +jStat.randg = function randg(shape, n, m) { + var oalph = shape; + var a1, a2, u, v, x, mat; + if (!m) + m = n; + if (!shape) + shape = 1; + if (n) { + mat = jStat.zeros(n,m); + mat.alter(function() { return jStat.randg(shape); }); + return mat; + } + if (shape < 1) + shape += 1; + a1 = shape - 1 / 3; + a2 = 1 / Math.sqrt(9 * a1); + do { + do { + x = jStat.randn(); + v = 1 + a2 * x; + } while(v <= 0); + v = v * v * v; + u = Math.random(); + } while(u > 1 - 0.331 * Math.pow(x, 4) && + Math.log(u) > 0.5 * x*x + a1 * (1 - v + Math.log(v))); + // alpha > 1 + if (shape == oalph) + return a1 * v; + // alpha < 1 + do { + u = Math.random(); + } while(u === 0); + return Math.pow(u, 1 / oalph) * a1 * v; }; -// Destructively alter an array. -jProto.alter = function alter(func) { - jStat.alter(this, func); - return this; -}; - - -// Extend prototype with methods that have no argument. +// making use of static methods on the instance (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; - }; + jStat.fn[passfunc] = function() { + return jStat( + jStat.map(this, function(value) { return jStat[passfunc](value); })); + } })(funcs[i]); -})('transpose clear symmetric rows cols dimensions diag antidiag'.split(' ')); +})('gammaln gammafn factorial factorialln'.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)); + jStat.fn[passfunc] = function() { + return jStat(jStat[passfunc].apply(null, arguments)); }; })(funcs[i]); -})('row col'.split(' ')); - +})('randn'.split(' ')); -// Extend prototype with simple shortcut methods. -(function(funcs) { - for (var i = 0; i < funcs.length; i++) (function(passfunc) { - jProto[passfunc] = new Function( - 'return jStat(jStat.' + passfunc + '.apply(null, arguments));'); - })(funcs[i]); -})('create zeros ones rand identity'.split(' ')); +}(this.jStat, Math)); +(function(jStat, Math) { +// generate all distribution instance methods +(function(list) { + for (var i = 0; i < list.length; i++) (function(func) { + // distribution instance method + jStat[func] = function(a, b, c) { + if (!(this instanceof arguments.callee)) + return new arguments.callee(a, b, c); + this._a = a; + this._b = b; + this._c = c; + return this; + }; + // distribution method to be used on a jStat instance + jStat.fn[func] = function(a, b, c) { + var newthis = jStat[func](a, b, c); + newthis.data = this; + return newthis; + }; + // sample instance method + jStat[func].prototype.sample = function(arr) { + var a = this._a; + var b = this._b; + var c = this._c; + if (arr) + return jStat.alter(arr, function() { + return jStat[func].sample(a, b, c); + }); + else + return jStat[func].sample(a, b, c); + }; + // generate the pdf, cdf and inv instance methods + (function(vals) { + for (var i = 0; i < vals.length; i++) (function(fnfunc) { + jStat[func].prototype[fnfunc] = function(x) { + var a = this._a; + var b = this._b; + var c = this._c; + if (!x && x !== 0) + x = this.data; + if (typeof x !== 'number') { + return jStat.fn.map.call(x, function(x) { + return jStat[func][fnfunc](x, a, b, c); + }); + } + return jStat[func][fnfunc](x, a, b, c); + }; + })(vals[i]); + })('pdf cdf inv'.split(' ')); + // generate the mean, median, mode and variance instance methods + (function(vals) { + for (var i = 0; i < vals.length; i++) (function(fnfunc) { + jStat[func].prototype[fnfunc] = function() { + return jStat[func][fnfunc](this._a, this._b, this._c); + }; + })(vals[i]); + })('mean median mode variance'.split(' ')); + })(list[i]); +})(( + 'beta centralF cauchy chisquare exponential gamma invgamma kumaraswamy ' + + 'laplace lognormal noncentralt normal pareto studentt weibull uniform ' + + 'binomial negbin hypgeom poisson triangular' +).split(' ')); -// Exposing jStat. -return jStat; -}(Math)); -(function(jStat, Math) { -var isFunction = jStat.utils.isFunction; +// extend beta function with static methods +jStat.extend(jStat.beta, { + pdf: function pdf(x, alpha, beta) { + // PDF is zero outside the support + if (x > 1 || x < 0) + return 0; + // PDF is one for the uniform case + if (alpha == 1 && beta == 1) + return 1; -// Ascending functions for sort -function ascNum(a, b) { return a - b; } + if (alpha < 512 && beta < 512) { + return (Math.pow(x, alpha - 1) * Math.pow(1 - x, beta - 1)) / + jStat.betafn(alpha, beta); + } else { + return Math.exp((alpha - 1) * Math.log(x) + + (beta - 1) * Math.log(1 - x) - + jStat.betaln(alpha, beta)); + } + }, -function clip(arg, min, max) { - return Math.max(min, Math.min(arg, max)); -} + cdf: function cdf(x, alpha, beta) { + return (x > 1 || x < 0) ? (x > 1) * 1 : jStat.ibeta(x, alpha, beta); + }, + inv: function inv(x, alpha, beta) { + return jStat.ibetainv(x, alpha, beta); + }, -// sum of an array -jStat.sum = function sum(arr) { - var sum = 0; - var i = arr.length; - while (--i >= 0) - sum += arr[i]; - return sum; -}; + mean: function mean(alpha, beta) { + return alpha / (alpha + beta); + }, + median: function median(alpha, beta) { + return jStat.ibetainv(0.5, alpha, beta); + }, -// sum squared -jStat.sumsqrd = function sumsqrd(arr) { - var sum = 0; - var i = arr.length; - while (--i >= 0) - sum += arr[i] * arr[i]; - return sum; -}; + mode: function mode(alpha, beta) { + return (alpha - 1 ) / ( alpha + beta - 2); + }, + // return a random sample + sample: function sample(alpha, beta) { + var u = jStat.randg(alpha); + return u / (u + jStat.randg(beta)); + }, -// 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; + variance: function variance(alpha, beta) { + return (alpha * beta) / (Math.pow(alpha + beta, 2) * (alpha + beta + 1)); } - 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; -}; +// extend F function with static methods +jStat.extend(jStat.centralF, { + // This implementation of the pdf function avoids float overflow + // See the way that R calculates this value: + // https://svn.r-project.org/R/trunk/src/nmath/df.c + pdf: function pdf(x, df1, df2) { + var p, q, f; -// product of an array -jStat.product = function product(arr) { - var prod = 1; - var i = arr.length; - while (--i >= 0) - prod *= arr[i]; - return prod; -}; + if (x < 0) + return 0; + if (df1 <= 2) { + if (x === 0 && df1 < 2) { + return Infinity; + } + if (x === 0 && df1 === 2) { + return 1; + } + return Math.sqrt((Math.pow(df1 * x, df1) * Math.pow(df2, df2)) / + (Math.pow(df1 * x + df2, df1 + df2))) / + (x * jStat.betafn(df1/2, df2/2)); + } -// 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; -}; + p = (df1 * x) / (df2 + x * df1); + q = df2 / (df2 + x * df1); + f = df1 * q / 2.0; + return f * jStat.binomial.pdf((df1 - 2) / 2, (df1 + df2 - 2) / 2, p); + }, + cdf: function cdf(x, df1, df2) { + if (x < 0) + return 0; + return jStat.ibeta((df1 * x) / (df1 * x + df2), df1 / 2, df2 / 2); + }, -// 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; -}; + inv: function inv(x, df1, df2) { + return df2 / (df1 * (1 / jStat.ibetainv(x, df1 / 2, df2 / 2) - 1)); + }, + mean: function mean(df1, df2) { + return (df2 > 2) ? df2 / (df2 - 2) : undefined; + }, -// 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; -}; + mode: function mode(df1, df2) { + return (df1 > 2) ? (df2 * (df1 - 2)) / (df1 * (df2 + 2)) : undefined; + }, + // return a random sample + sample: function sample(df1, df2) { + var x1 = jStat.randg(df1 / 2) * 2; + var x2 = jStat.randg(df2 / 2) * 2; + return (x1 / df1) / (x2 / df2); + }, -// mean value of an array -jStat.mean = function mean(arr) { - return jStat.sum(arr) / arr.length; -}; + variance: function variance(df1, df2) { + if (df2 <= 4) + return undefined; + return 2 * df2 * df2 * (df1 + df2 - 2) / + (df1 * (df2 - 2) * (df2 - 2) * (df2 - 4)); + } +}); -// mean squared error (MSE) -jStat.meansqerr = function meansqerr(arr) { - return jStat.sumsqerr(arr) / arr.length; -}; +// extend cauchy function with static methods +jStat.extend(jStat.cauchy, { + pdf: function pdf(x, local, scale) { + if (scale < 0) { return 0; } + return (scale / (Math.pow(x - local, 2) + Math.pow(scale, 2))) / Math.PI; + }, -// geometric mean of an array -jStat.geomean = function geomean(arr) { - return Math.pow(jStat.product(arr), 1 / arr.length); -}; + cdf: function cdf(x, local, scale) { + return Math.atan((x - local) / scale) / Math.PI + 0.5; + }, + inv: function(p, local, scale) { + return local + scale * Math.tan(Math.PI * (p - 0.5)); + }, -// 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 ]; -}; + median: function median(local, scale) { + return local; + }, + mode: function mode(local, scale) { + return local; + }, -// cumulative sum of an array -jStat.cumsum = function cumsum(arr) { - return jStat.cumreduce(arr, function (a, b) { return a + b; }); -}; + sample: function sample(local, scale) { + return jStat.randn() * + Math.sqrt(1 / (2 * jStat.randg(0.5))) * scale + local; + } +}); -// cumulative product of an array -jStat.cumprod = function cumprod(arr) { - return jStat.cumreduce(arr, function (a, b) { return a * b; }); -}; +// extend chisquare function with static methods +jStat.extend(jStat.chisquare, { + pdf: function pdf(x, dof) { + if (x < 0) + return 0; + return (x === 0 && dof === 2) ? 0.5 : + Math.exp((dof / 2 - 1) * Math.log(x) - x / 2 - (dof / 2) * + Math.log(2) - jStat.gammaln(dof / 2)); + }, -// successive differences of a sequence -jStat.diff = function diff(arr) { - var diffs = []; - var arrLen = arr.length; - var i; - for (var i = 1; i < arrLen; i++) - diffs.push(arr[i] - arr[i - 1]); - return diffs; -}; + cdf: function cdf(x, dof) { + if (x < 0) + return 0; + return jStat.lowRegGamma(dof / 2, x / 2); + }, + inv: function(p, dof) { + return 2 * jStat.gammapinv(p, 0.5 * dof); + }, -// ranks of an array -jStat.rank = function (arr) { - var arrlen = arr.length; - var sorted = arr.slice().sort(ascNum); - var ranks = new Array(arrlen); - for (var i = 0; i < arrlen; i++) { - var first = sorted.indexOf(arr[i]); - var last = sorted.lastIndexOf(arr[i]); - if (first === last) { - var val = first; - } else { - var val = (first + last) / 2; - } - ranks[i] = val + 1; - } - return ranks; -}; + mean : function(dof) { + return dof; + }, + // TODO: this is an approximation (is there a better way?) + median: function median(dof) { + return dof * Math.pow(1 - (2 / (9 * dof)), 3); + }, -// 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; + mode: function mode(dof) { + return (dof - 2 > 0) ? dof - 2 : 0; + }, - for (var 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; - } + sample: function sample(dof) { + return jStat.randg(dof / 2) * 2; + }, + + variance: function variance(dof) { + return 2 * dof; } +}); - return numMaxCount === 0 ? mode_arr[0] : mode_arr; -}; -// range of an array -jStat.range = function range(arr) { - return jStat.max(arr) - jStat.min(arr); -}; +// extend exponential function with static methods +jStat.extend(jStat.exponential, { + pdf: function pdf(x, rate) { + return x < 0 ? 0 : rate * Math.exp(-rate * x); + }, -// 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)); -}; + cdf: function cdf(x, rate) { + return x < 0 ? 0 : 1 - Math.exp(-rate * x); + }, -// 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; -}; + inv: function(p, rate) { + return -Math.log(1 - p) / rate; + }, -// 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)); -}; + mean : function(rate) { + return 1 / rate; + }, + median: function (rate) { + return (1 / rate) * Math.log(2); + }, -// mean deviation (mean absolute deviation) of an array -jStat.meandev = function meandev(arr) { - var devSum = 0; - var mean = jStat.mean(arr); - var i; - for (var i = arr.length - 1; i >= 0; i--) - devSum += Math.abs(arr[i] - mean); - return devSum / arr.length; -}; + mode: function mode(rate) { + return 0; + }, + sample: function sample(rate) { + return -1 / rate * Math.log(Math.random()); + }, -// median deviation (median absolute deviation) of an array -jStat.meddev = function meddev(arr) { - var devSum = 0; - var median = jStat.median(arr); - var i; - for (var i = arr.length - 1; i >= 0; i--) - devSum += Math.abs(arr[i] - median); - return devSum / arr.length; -}; + variance : function(rate) { + return Math.pow(rate, -2); + } +}); -// coefficient of variation -jStat.coeffvar = function coeffvar(arr) { - return jStat.stdev(arr) / jStat.mean(arr); -}; +// extend gamma function with static methods +jStat.extend(jStat.gamma, { + pdf: function pdf(x, shape, scale) { + if (x < 0) + return 0; + return (x === 0 && shape === 1) ? 1 / scale : + Math.exp((shape - 1) * Math.log(x) - x / scale - + jStat.gammaln(shape) - shape * Math.log(scale)); + }, -// 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 ] - ]; -}; + cdf: function cdf(x, shape, scale) { + if (x < 0) + return 0; + return jStat.lowRegGamma(shape, x / scale); + }, + inv: function(p, shape, scale) { + return jStat.gammapinv(p, shape) * scale; + }, -// 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; + mean : function(shape, scale) { + return shape * scale; + }, - if (typeof alphap === 'undefined') - alphap = 3 / 8; - if (typeof betap === 'undefined') - betap = 3 / 8; + mode: function mode(shape, scale) { + if(shape > 1) return (shape - 1) * scale; + return undefined; + }, - for (var 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]; + sample: function sample(shape, scale) { + return jStat.randg(shape) * scale; + }, + + variance: function variance(shape, scale) { + return shape * scale * scale; } +}); - return quantileVals; -}; +// extend inverse gamma function with static methods +jStat.extend(jStat.invgamma, { + pdf: function pdf(x, shape, scale) { + if (x <= 0) + return 0; + return Math.exp(-(shape + 1) * Math.log(x) - scale / x - + jStat.gammaln(shape) + shape * Math.log(scale)); + }, -// Returns the k-th percentile of values in a range, where k is in the -// range 0..1, exclusive. -jStat.percentile = function percentile(arr, k) { - var _arr = arr.slice().sort(ascNum); - var realIndex = k * (_arr.length - 1); - var index = parseInt(realIndex); - var frac = realIndex - index; + cdf: function cdf(x, shape, scale) { + if (x <= 0) + return 0; + return 1 - jStat.lowRegGamma(shape, scale / x); + }, - if (index + 1 < _arr.length) { - return _arr[index] * (1 - frac) + _arr[index + 1] * frac; - } else { - return _arr[index]; - } -} + inv: function(p, shape, scale) { + return scale / jStat.gammapinv(1 - p, shape); + }, + mean : function(shape, scale) { + return (shape > 1) ? scale / (shape - 1) : undefined; + }, -// 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; + mode: function mode(shape, scale) { + return scale / (shape + 1); + }, - if (kind === 'strict') - strict = true; + sample: function sample(shape, scale) { + return scale / jStat.randg(shape); + }, - for (var i = 0; i < len; i++) { - value = arr[i]; - if ((strict && value < score) || - (!strict && value <= score)) { - counter++; - } + variance: function variance(shape, scale) { + if (shape <= 2) + return undefined; + return scale * scale / ((shape - 1) * (shape - 1) * (shape - 2)); } +}); - return counter / len; -}; +// extend kumaraswamy function with static methods +jStat.extend(jStat.kumaraswamy, { + pdf: function pdf(x, alpha, beta) { + if (x === 0 && alpha === 1) + return beta; + else if (x === 1 && beta === 1) + return alpha; + return Math.exp(Math.log(alpha) + Math.log(beta) + (alpha - 1) * + Math.log(x) + (beta - 1) * + Math.log(1 - Math.pow(x, alpha))); + }, -// Histogram (bin count) data -jStat.histogram = function histogram(arr, bins) { - var first = jStat.min(arr); - var binCnt = bins || 4; - var binWidth = (jStat.max(arr) - first) / binCnt; - var len = arr.length; - var bins = []; - var i; + cdf: function cdf(x, alpha, beta) { + if (x < 0) + return 0; + else if (x > 1) + return 1; + return (1 - Math.pow(1 - Math.pow(x, alpha), beta)); + }, - for (var i = 0; i < binCnt; i++) - bins[i] = 0; - for (var i = 0; i < len; i++) - bins[Math.min(Math.floor(((arr[i] - first) / binWidth)), binCnt - 1)] += 1; + inv: function inv(p, alpha, beta) { + return Math.pow(1 - Math.pow(1 - p, 1 / beta), 1 / alpha); + }, - return bins; -}; + mean : function(alpha, beta) { + return (beta * jStat.gammafn(1 + 1 / alpha) * + jStat.gammafn(beta)) / (jStat.gammafn(1 + 1 / alpha + beta)); + }, + median: function median(alpha, beta) { + return Math.pow(1 - Math.pow(2, -1 / beta), 1 / alpha); + }, -// 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; + mode: function mode(alpha, beta) { + if (!(alpha >= 1 && beta >= 1 && (alpha !== 1 && beta !== 1))) + return undefined; + return Math.pow((alpha - 1) / (alpha * beta - 1), 1 / alpha); + }, - for (var i = 0; i < arr1Len; i++) - sq_dev[i] = (arr1[i] - u) * (arr2[i] - v); + variance: function variance(alpha, beta) { + throw new Error('variance not yet implemented'); + // TODO: complete this + } +}); - 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); -}; +// extend lognormal function with static methods +jStat.extend(jStat.lognormal, { + pdf: function pdf(x, mu, sigma) { + if (x <= 0) + return 0; + return Math.exp(-Math.log(x) - 0.5 * Math.log(2 * Math.PI) - + Math.log(sigma) - Math.pow(Math.log(x) - mu, 2) / + (2 * sigma * sigma)); + }, - // (spearman's) rank correlation coefficient, sp -jStat.spearmancoeff = function (arr1, arr2) { - arr1 = jStat.rank(arr1); - arr2 = jStat.rank(arr2); - var arr1dev = jStat.deviation(arr1); - var arr2dev = jStat.deviation(arr2); - return jStat.sum(arr1dev.map(function (x, i) { - return x * arr2dev[i]; - })) / - Math.sqrt(jStat.sum(arr1dev.map(function (x) { - return Math.pow(x, 2); - })) * jStat.sum(arr2dev.map(function (x) { - return Math.pow(x, 2); - })) - ); -} + cdf: function cdf(x, mu, sigma) { + if (x < 0) + return 0; + return 0.5 + + (0.5 * jStat.erf((Math.log(x) - mu) / Math.sqrt(2 * sigma * sigma))); + }, + inv: function(p, mu, sigma) { + return Math.exp(-1.41421356237309505 * sigma * jStat.erfcinv(2 * p) + mu); + }, -// 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; + mean: function mean(mu, sigma) { + return Math.exp(mu + sigma * sigma / 2); + }, - for (var i = 0; i < len; i++) - skewSum += Math.pow((arr[i] - mu) / sigma, n); + median: function median(mu, sigma) { + return Math.exp(mu); + }, - return skewSum / arr.length; -}; + mode: function mode(mu, sigma) { + return Math.exp(mu - sigma * sigma); + }, -// (pearson's) moment coefficient of skewness -jStat.skewness = function skewness(arr) { - return jStat.stanMoment(arr, 3); -}; + sample: function sample(mu, sigma) { + return Math.exp(jStat.randn() * sigma + mu); + }, -// (pearson's) (excess) kurtosis -jStat.kurtosis = function kurtosis(arr) { - return jStat.stanMoment(arr, 4) - 3; -}; + variance: function variance(mu, sigma) { + return (Math.exp(sigma * sigma) - 1) * Math.exp(2 * mu + sigma * sigma); + } +}); -var jProto = jStat.prototype; +// extend noncentralt function with static methods +jStat.extend(jStat.noncentralt, { + pdf: function pdf(x, dof, ncp) { + var tol = 1e-14; + if (Math.abs(ncp) < tol) // ncp approx 0; use student-t + return jStat.studentt.pdf(x, dof) -// 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(' ')); + if (Math.abs(x) < tol) { // different formula for x == 0 + return Math.exp(jStat.gammaln((dof + 1) / 2) - ncp * ncp / 2 - + 0.5 * Math.log(Math.PI * dof) - jStat.gammaln(dof / 2)); + } + // formula for x != 0 + return dof / x * + (jStat.noncentralt.cdf(x * Math.sqrt(1 + 2 / dof), dof+2, ncp) - + jStat.noncentralt.cdf(x, dof, ncp)); + }, -// 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(' ')); + cdf: function cdf(x, dof, ncp) { + var tol = 1e-14; + var min_iterations = 200; + if (Math.abs(ncp) < tol) // ncp approx 0; use student-t + return jStat.studentt.cdf(x, dof); -// 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); + // turn negative x into positive and flip result afterwards + var flip = false; + if (x < 0) { + flip = true; + ncp = -ncp; + } - // 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])) { - var callbackFunction = args[args.length - 1]; - var argsToPass = args.slice(0, args.length - 1); + var prob = jStat.normal.cdf(-ncp, 0, 1); + var value = tol + 1; + // use value at last two steps to determine convergence + var lastvalue = value; + var y = x * x / (x * x + dof); + var j = 0; + var p = Math.exp(-ncp * ncp / 2); + var q = Math.exp(-ncp * ncp / 2 - 0.5 * Math.log(2) - + jStat.gammaln(3 / 2)) * ncp; + while (j < min_iterations || lastvalue > tol || value > tol) { + lastvalue = value; + if (j > 0) { + p *= (ncp * ncp) / (2 * j); + q *= (ncp * ncp) / (2 * (j + 1 / 2)); + } + value = p * jStat.beta.cdf(y, j + 0.5, dof / 2) + + q * jStat.beta.cdf(y, j+1, dof/2); + prob += 0.5 * value; + j++; + } - setTimeout(function() { - callbackFunction.call(tmpthis, - jProto[passfunc].apply(tmpthis, argsToPass)); - }); - return this; + return flip ? (1 - prob) : prob; + } +}); - // Otherwise we curry the function args and call normally. - } else { - var 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; - } +// extend normal function with static methods +jStat.extend(jStat.normal, { + pdf: function pdf(x, mean, std) { + return Math.exp(-0.5 * Math.log(2 * Math.PI) - + Math.log(std) - Math.pow(x - mean, 2) / (2 * std * std)); + }, - // Otherwise run on the vector. - return curriedFunction(this[0]); - }; - })(funcs[i]); -})('quantiles percentileOfScore'.split(' ')); + cdf: function cdf(x, mean, std) { + return 0.5 * (1 + jStat.erf((x - mean) / Math.sqrt(2 * std * std))); + }, -}(this.jStat, Math)); -// Special functions // -(function(jStat, Math) { + inv: function(p, mean, std) { + return -1.41421356237309505 * std * jStat.erfcinv(2 * p) + mean; + }, -// Log-gamma function -jStat.gammaln = function gammaln(x) { - var j = 0; - var cof = [ - 76.18009172947146, -86.50532032941677, 24.01409824083091, - -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5 - ]; - 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; -}; + mean : function(mean, std) { + return mean; + }, + median: function median(mean, std) { + return mean; + }, -// 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, sum, ysq; - 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 (var 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 (var i = 0; i < n; ++i) { - res *= y; - y++; - } - } - if (fact) { - res = fact / res; + mode: function (mean, std) { + return mean; + }, + + sample: function sample(mean, std) { + return jStat.randn() * std + mean; + }, + + variance : function(mean, std) { + return std * std; } - 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); -}; +// extend pareto function with static methods +jStat.extend(jStat.pareto, { + pdf: function pdf(x, scale, shape) { + if (x < scale) + return 0; + return (shape * Math.pow(scale, shape)) / Math.pow(x, shape + 1); + }, -// 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, endval; + cdf: function cdf(x, scale, shape) { + if (x < scale) + return 0; + return 1 - Math.pow(scale / x, shape); + }, - 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))); - } + inv: function inv(p, scale, shape) { + return scale / Math.pow(1 - p, 1 / shape); + }, - for (; i <= ITMAX; i++) { - an = -i * (i - a); - b += 2; - d = an * d + b; - c = b + an / c; - d = 1 / d; - h *= d * c; - } + mean: function mean(scale, shape) { + if (shape <= 1) + return undefined; + return (shape * Math.pow(scale, shape)) / (shape - 1); + }, - return (1 - h * Math.exp(-x + a * Math.log(x) - (aln))); -}; + median: function median(scale, shape) { + return scale * (shape * Math.SQRT2); + }, -// natural log factorial of n -jStat.factorialln = function factorialln(n) { - return n < 0 ? NaN : jStat.gammaln(n + 1); -}; + mode: function mode(scale, shape) { + return scale; + }, -// factorial of n -jStat.factorial = function factorial(n) { - return n < 0 ? NaN : jStat.gammafn(n + 1); -}; + variance : function(scale, shape) { + if (shape <= 2) + return undefined; + return (scale*scale * shape) / (Math.pow(shape - 1, 2) * (shape - 2)); + } +}); -// 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); -}; +// extend studentt function with static methods +jStat.extend(jStat.studentt, { + pdf: function pdf(x, dof) { + dof = dof > 1e100 ? 1e100 : dof; + return (1/(Math.sqrt(dof) * jStat.betafn(0.5, dof/2))) * + Math.pow(1 + ((x * x) / dof), -((dof + 1) / 2)); + }, + cdf: function cdf(x, dof) { + var dof2 = dof / 2; + return jStat.ibeta((x + Math.sqrt(x * x + dof)) / + (2 * Math.sqrt(x * x + dof)), dof2, dof2); + }, -// permutations of n, m -jStat.permutation = function permutation(n, m) { - return jStat.factorial(n) / jStat.factorial(n - m); -}; + inv: function(p, dof) { + var x = jStat.ibetainv(2 * Math.min(p, 1 - p), 0.5 * dof, 0.5); + x = Math.sqrt(dof * (1 - x) / x); + return (p > 0.5) ? x : -x; + }, + mean: function mean(dof) { + return (dof > 1) ? 0 : undefined; + }, -// 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); -}; + median: function median(dof) { + return 0; + }, + mode: function mode(dof) { + return 0; + }, -// natural logarithm of beta function -jStat.betaln = function betaln(x, y) { - return jStat.gammaln(x) + jStat.gammaln(y) - jStat.gammaln(x + y); -}; + sample: function sample(dof) { + return jStat.randn() * Math.sqrt(dof / (2 * jStat.randg(dof / 2))); + }, + variance: function variance(dof) { + return (dof > 2) ? dof / (dof - 2) : (dof > 1) ? Infinity : undefined; + } +}); -// 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; - } +// extend weibull function with static methods +jStat.extend(jStat.weibull, { + pdf: function pdf(x, scale, shape) { + if (x < 0 || scale < 0 || shape < 0) + return 0; + return (shape / scale) * Math.pow((x / scale), (shape - 1)) * + Math.exp(-(Math.pow((x / scale), shape))); + }, - return h; -}; + cdf: function cdf(x, scale, shape) { + return x < 0 ? 0 : 1 - Math.exp(-Math.pow((x / scale), shape)); + }, + inv: function(p, scale, shape) { + return scale * Math.pow(-Math.log(1 - p), 1 / shape); + }, -// 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; + mean : function(scale, shape) { + return scale * jStat.gammafn(1 + 1 / shape); + }, - 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)); - } + median: function median(scale, shape) { + return scale * Math.pow(Math.log(2), 1 / shape); + }, - for(; j < 12; j++) { - if (x <= 0) + mode: function mode(scale, shape) { + if (shape <= 1) 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 scale * Math.pow((shape - 1) / shape, 1 / shape); + }, + + sample: function sample(scale, shape) { + return scale * Math.pow(-Math.log(Math.random()), 1 / shape); + }, + + variance: function variance(scale, shape) { + return scale * scale * jStat.gammafn(1 + 2 / shape) - + Math.pow(jStat.weibull.mean(scale, shape), 2); } +}); - return x; -}; -// Returns the error function erf(x) -jStat.erf = function erf(x) { - var cof = [-1.3026537197817094, 6.4196979235649026e-1, 1.9476473204185836e-2, - -9.561514786808631e-3, -9.46595344482036e-4, 3.66839497852761e-4, - 4.2523324806907e-5, -2.0278578112534e-5, -1.624290004647e-6, - 1.303655835580e-6, 1.5626441722e-8, -8.5238095915e-8, - 6.529054439e-9, 5.059343495e-9, -9.91364156e-10, - -2.27365122e-10, 9.6467911e-11, 2.394038e-12, - -6.886027e-12, 8.94487e-13, 3.13092e-13, - -1.12708e-13, 3.81e-16, 7.106e-15, - -1.523e-15, -9.4e-17, 1.21e-16, - -2.8e-17]; - 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; - } +// extend uniform function with static methods +jStat.extend(jStat.uniform, { + pdf: function pdf(x, a, b) { + return (x < a || x > b) ? 0 : 1 / (b - a); + }, - t = 2 / (2 + x); - ty = 4 * t - 2; + cdf: function cdf(x, a, b) { + if (x < a) + return 0; + else if (x < b) + return (x - a) / (b - a); + return 1; + }, - for(; j > 0; j--) { - tmp = d; - d = ty * d - dd + cof[j]; - dd = tmp; - } + inv: function(p, a, b) { + return a + (p * (b - a)); + }, - res = t * Math.exp(-x * x + 0.5 * (cof[0] + ty * d) - dd); - return isneg ? res - 1 : 1 - res; -}; + mean: function mean(a, b) { + return 0.5 * (a + b); + }, + median: function median(a, b) { + return jStat.mean(a, b); + }, -// Returns the complmentary error function erfc(x) -jStat.erfc = function erfc(x) { - return 1 - jStat.erf(x); -}; + mode: function mode(a, b) { + throw new Error('mode is not yet implemented'); + }, + sample: function sample(a, b) { + return (a / 2 + b / 2) + (b / 2 - a / 2) * (2 * Math.random() - 1); + }, -// 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); + variance: function variance(a, b) { + return Math.pow(b - a, 2) / 12; } - 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++) { - if (x === 0 || x === 1) - return x; - err = jStat.ibeta(x, a, b) - p; - t = Math.exp(a1 * Math.log(x) + b1 * Math.log(1 - x) + afac); - u = err / t; - x -= (t = u / (1 - 0.5 * Math.min(1, u * (a1 / x - b1 / (1 - x))))); - if (x <= 0) - x = 0.5 * (x + t); - if (x >= 1) - x = 0.5 * (x + t + 1); - if (Math.abs(t) < EPS * x && j > 0) - break; - } - return x; -}; +// extend uniform function with static methods +jStat.extend(jStat.binomial, { + pdf: function pdf(k, n, p) { + return (p === 0 || p === 1) ? + ((n * p) === k ? 1 : 0) : + jStat.combination(n, k) * Math.pow(p, k) * Math.pow(1 - p, n - k); + }, -// Returns the incomplete beta function I_x(a,b) -jStat.ibeta = function ibeta(x, a, b) { - // Factors in front of the continued fraction. - var bt = (x === 0 || x === 1) ? 0 : - Math.exp(jStat.gammaln(a + b) - jStat.gammaln(a) - - jStat.gammaln(b) + a * Math.log(x) + b * - Math.log(1 - x)); - if (x < 0 || x > 1) - return false; - if (x < (a + 1) / (a + b + 2)) - // Use continued fraction directly. - return bt * jStat.betacf(x, a, b) / a; - // else use continued fraction after making the symmetry transformation. - return 1 - bt * jStat.betacf(1 - x, b, a) / b; -}; + cdf: function cdf(x, n, p) { + var binomarr = [], + k = 0; + if (x < 0) { + return 0; + } + if (x < n) { + for (; k <= x; k++) { + binomarr[ k ] = jStat.binomial.pdf(k, n, p); + } + return jStat.sum(binomarr); + } + return 1; + } +}); -// Returns a normal deviate (mu=0, sigma=1). -// If n and m are specified it returns a object of normal deviates. -jStat.randn = function randn(n, m) { - var u, v, x, y, q, mat; - if (!m) - m = n; - if (n) - return jStat.create(n, m, function() { return jStat.randn(); }); - do { - u = Math.random(); - v = 1.7156 * (Math.random() - 0.5); - x = u - 0.449871; - y = Math.abs(v) + 0.386595; - q = x * x + y * (0.19600 * y - 0.25472 * x); - } while (q > 0.27597 && (q > 0.27846 || v * v > -4 * Math.log(u) * u * u)); - return v / u; -}; +// extend uniform function with static methods +jStat.extend(jStat.negbin, { + pdf: function pdf(k, r, p) { + if (k !== k >>> 0) + return false; + if (k < 0) + return 0; + return jStat.combination(k + r - 1, r - 1) * + Math.pow(1 - p, k) * Math.pow(p, r); + }, -// Returns a gamma deviate by the method of Marsaglia and Tsang. -jStat.randg = function randg(shape, n, m) { - var oalph = shape; - var a1, a2, u, v, x, mat; - if (!m) - m = n; - if (!shape) - shape = 1; - if (n) { - mat = jStat.zeros(n,m); - mat.alter(function() { return jStat.randg(shape); }); - return mat; + cdf: function cdf(x, r, p) { + var sum = 0, + k = 0; + if (x < 0) return 0; + for (; k <= x; k++) { + sum += jStat.negbin.pdf(k, r, p); + } + return sum; } - if (shape < 1) - shape += 1; - a1 = shape - 1 / 3; - a2 = 1 / Math.sqrt(9 * a1); - do { - do { - x = jStat.randn(); - v = 1 + a2 * x; - } while(v <= 0); - v = v * v * v; - u = Math.random(); - } while(u > 1 - 0.331 * Math.pow(x, 4) && - Math.log(u) > 0.5 * x*x + a1 * (1 - v + Math.log(v))); - // alpha > 1 - if (shape == oalph) - return a1 * v; - // alpha < 1 - do { - u = Math.random(); - } while(u === 0); - return Math.pow(u, 1 / oalph) * a1 * v; -}; +}); -// making use of static methods on the instance -(function(funcs) { - for (var i = 0; i < funcs.length; i++) (function(passfunc) { - jStat.fn[passfunc] = function() { - return jStat( - jStat.map(this, function(value) { return jStat[passfunc](value); })); - } - })(funcs[i]); -})('gammaln gammafn factorial factorialln'.split(' ')); +// extend uniform function with static methods +jStat.extend(jStat.hypgeom, { + pdf: function pdf(k, N, m, n) { + // Hypergeometric PDF. -(function(funcs) { - for (var i = 0; i < funcs.length; i++) (function(passfunc) { - jStat.fn[passfunc] = function() { - return jStat(jStat[passfunc].apply(null, arguments)); - }; - })(funcs[i]); -})('randn'.split(' ')); + // A simplification of the CDF algorithm below. -}(this.jStat, Math)); -(function(jStat, Math) { + // k = number of successes drawn + // N = population size + // m = number of successes in population + // n = number of items drawn from population -// generate all distribution instance methods -(function(list) { - for (var i = 0; i < list.length; i++) (function(func) { - // distribution instance method - jStat[func] = function(a, b, c) { - if (!(this instanceof arguments.callee)) - return new arguments.callee(a, b, c); - this._a = a; - this._b = b; - this._c = c; - return this; - }; - // distribution method to be used on a jStat instance - jStat.fn[func] = function(a, b, c) { - var newthis = jStat[func](a, b, c); - newthis.data = this; - return newthis; - }; - // sample instance method - jStat[func].prototype.sample = function(arr) { - var a = this._a; - var b = this._b; - var c = this._c; - if (arr) - return jStat.alter(arr, function() { - return jStat[func].sample(a, b, c); - }); - else - return jStat[func].sample(a, b, c); - }; - // generate the pdf, cdf and inv instance methods - (function(vals) { - for (var i = 0; i < vals.length; i++) (function(fnfunc) { - jStat[func].prototype[fnfunc] = function(x) { - var a = this._a; - var b = this._b; - var c = this._c; - if (!x && x !== 0) - x = this.data; - if (typeof x !== 'number') { - return jStat.fn.map.call(x, function(x) { - return jStat[func][fnfunc](x, a, b, c); - }); - } - return jStat[func][fnfunc](x, a, b, c); - }; - })(vals[i]); - })('pdf cdf inv'.split(' ')); - // generate the mean, median, mode and variance instance methods - (function(vals) { - for (var i = 0; i < vals.length; i++) (function(fnfunc) { - jStat[func].prototype[fnfunc] = function() { - return jStat[func][fnfunc](this._a, this._b, this._c); - }; - })(vals[i]); - })('mean median mode variance'.split(' ')); - })(list[i]); -})(( - 'beta centralF cauchy chisquare exponential gamma invgamma kumaraswamy ' + - 'laplace lognormal noncentralt normal pareto studentt weibull uniform ' + - 'binomial negbin hypgeom poisson triangular' -).split(' ')); + if(k !== k | 0) { + return false; + } else if(k < 0 || k < m - (N - n)) { + // It's impossible to have this few successes drawn. + return 0; + } else if(k > n || k > m) { + // It's impossible to have this many successes drawn. + return 0; + } else if (m * 2 > N) { + // More than half the population is successes. + if(n * 2 > N) { + // More than half the population is sampled. + return jStat.hypgeom.pdf(N - m - n + k, N, N - m, N - n) + } else { + // Half or less of the population is sampled. -// extend beta function with static methods -jStat.extend(jStat.beta, { - pdf: function pdf(x, alpha, beta) { - // PDF is zero outside the support - if (x > 1 || x < 0) - return 0; - // PDF is one for the uniform case - if (alpha == 1 && beta == 1) - return 1; + return jStat.hypgeom.pdf(n - k, N, N - m, n); + } - if (alpha < 512 && beta < 512) { - return (Math.pow(x, alpha - 1) * Math.pow(1 - x, beta - 1)) / - jStat.betafn(alpha, beta); - } else { - return Math.exp((alpha - 1) * Math.log(x) + - (beta - 1) * Math.log(1 - x) - - jStat.betaln(alpha, beta)); - } - }, + } else if(n * 2 > N) { + // Half or less is successes. - cdf: function cdf(x, alpha, beta) { - return (x > 1 || x < 0) ? (x > 1) * 1 : jStat.ibeta(x, alpha, beta); - }, + return jStat.hypgeom.pdf(m - k, N, m, N - n); - inv: function inv(x, alpha, beta) { - return jStat.ibetainv(x, alpha, beta); - }, + } else if(m < n) { + // We want to have the number of things sampled to be less than the + // successes available. So swap the definitions of successful and sampled. + return jStat.hypgeom.pdf(k, N, n, m); + } else { + // If we get here, half or less of the population was sampled, half or + // less of it was successes, and we had fewer sampled things than + // successes. Now we can do this complicated iterative algorithm in an + // efficient way. - mean: function mean(alpha, beta) { - return alpha / (alpha + beta); - }, + // The basic premise of the algorithm is that we partially normalize our + // intermediate product to keep it in a numerically good region, and then + // finish the normalization at the end. - median: function median(alpha, beta) { - return jStat.ibetainv(0.5, alpha, beta); - }, + // This variable holds the scaled probability of the current number of + // successes. + var scaledPDF = 1; - mode: function mode(alpha, beta) { - return (alpha - 1 ) / ( alpha + beta - 2); - }, + // This keeps track of how much we have normalized. + var samplesDone = 0; - // return a random sample - sample: function sample(alpha, beta) { - var u = jStat.randg(alpha); - return u / (u + jStat.randg(beta)); - }, + for(var i = 0; i < k; i++) { + // For every possible number of successes up to that observed... - variance: function variance(alpha, beta) { - return (alpha * beta) / (Math.pow(alpha + beta, 2) * (alpha + beta + 1)); - } -}); + while(scaledPDF > 1 && samplesDone < n) { + // Intermediate result is growing too big. Apply some of the + // normalization to shrink everything. -// extend F function with static methods -jStat.extend(jStat.centralF, { - // This implementation of the pdf function avoids float overflow - // See the way that R calculates this value: - // https://svn.r-project.org/R/trunk/src/nmath/df.c - pdf: function pdf(x, df1, df2) { - var p, q, f; + scaledPDF *= 1 - (m / (N - samplesDone)); - if (x < 0) - return 0; + // Say we've normalized by this sample already. + samplesDone++; + } - if (df1 <= 2) { - if (x === 0 && df1 < 2) { - return Infinity; - } - if (x === 0 && df1 === 2) { - return 1; + // Work out the partially-normalized hypergeometric PDF for the next + // number of successes + scaledPDF *= (n - i) * (m - i) / ((i + 1) * (N - m - n + i + 1)); } - return Math.sqrt((Math.pow(df1 * x, df1) * Math.pow(df2, df2)) / - (Math.pow(df1 * x + df2, df1 + df2))) / - (x * jStat.betafn(df1/2, df2/2)); - } - p = (df1 * x) / (df2 + x * df1); - q = df2 / (df2 + x * df1); - f = df1 * q / 2.0; - return f * jStat.binomial.pdf((df1 - 2) / 2, (df1 + df2 - 2) / 2, p); - }, + for(; samplesDone < n; samplesDone++) { + // Apply all the rest of the normalization + scaledPDF *= 1 - (m / (N - samplesDone)); + } - cdf: function cdf(x, df1, df2) { - if (x < 0) - return 0; - return jStat.ibeta((df1 * x) / (df1 * x + df2), df1 / 2, df2 / 2); + // Bound answer sanely before returning. + return Math.min(1, Math.max(0, scaledPDF)); + } }, - inv: function inv(x, df1, df2) { - return df2 / (df1 * (1 / jStat.ibetainv(x, df1 / 2, df2 / 2) - 1)); - }, + cdf: function cdf(x, N, m, n) { + // Hypergeometric CDF. - mean: function mean(df1, df2) { - return (df2 > 2) ? df2 / (df2 - 2) : undefined; - }, + // This algorithm is due to Prof. Thomas S. Ferguson, , + // and comes from his hypergeometric test calculator at + // . - mode: function mode(df1, df2) { - return (df1 > 2) ? (df2 * (df1 - 2)) / (df1 * (df2 + 2)) : undefined; - }, + // x = number of successes drawn + // N = population size + // m = number of successes in population + // n = number of items drawn from population - // return a random sample - sample: function sample(df1, df2) { - var x1 = jStat.randg(df1 / 2) * 2; - var x2 = jStat.randg(df2 / 2) * 2; - return (x1 / df1) / (x2 / df2); - }, + if(x < 0 || x < m - (N - n)) { + // It's impossible to have this few successes drawn or fewer. + return 0; + } else if(x >= n || x >= m) { + // We will always have this many successes or fewer. + return 1; + } else if (m * 2 > N) { + // More than half the population is successes. - variance: function variance(df1, df2) { - if (df2 <= 4) - return undefined; - return 2 * df2 * df2 * (df1 + df2 - 2) / - (df1 * (df2 - 2) * (df2 - 2) * (df2 - 4)); - } -}); + if(n * 2 > N) { + // More than half the population is sampled. + return jStat.hypgeom.cdf(N - m - n + x, N, N - m, N - n) + } else { + // Half or less of the population is sampled. -// extend cauchy function with static methods -jStat.extend(jStat.cauchy, { - pdf: function pdf(x, local, scale) { - if (scale < 0) { return 0; } + return 1 - jStat.hypgeom.cdf(n - x - 1, N, N - m, n); + } - return (scale / (Math.pow(x - local, 2) + Math.pow(scale, 2))) / Math.PI; - }, + } else if(n * 2 > N) { + // Half or less is successes. - cdf: function cdf(x, local, scale) { - return Math.atan((x - local) / scale) / Math.PI + 0.5; - }, + return 1 - jStat.hypgeom.cdf(m - x - 1, N, m, N - n); - inv: function(p, local, scale) { - return local + scale * Math.tan(Math.PI * (p - 0.5)); - }, + } else if(m < n) { + // We want to have the number of things sampled to be less than the + // successes available. So swap the definitions of successful and sampled. + return jStat.hypgeom.cdf(x, N, n, m); + } else { + // If we get here, half or less of the population was sampled, half or + // less of it was successes, and we had fewer sampled things than + // successes. Now we can do this complicated iterative algorithm in an + // efficient way. - median: function median(local, scale) { - return local; - }, + // The basic premise of the algorithm is that we partially normalize our + // intermediate sum to keep it in a numerically good region, and then + // finish the normalization at the end. - mode: function mode(local, scale) { - return local; - }, + // Holds the intermediate, scaled total CDF. + var scaledCDF = 1; - sample: function sample(local, scale) { - return jStat.randn() * - Math.sqrt(1 / (2 * jStat.randg(0.5))) * scale + local; - } -}); + // This variable holds the scaled probability of the current number of + // successes. + var scaledPDF = 1; + // This keeps track of how much we have normalized. + var samplesDone = 0; + for(var i = 0; i < x; i++) { + // For every possible number of successes up to that observed... -// extend chisquare function with static methods -jStat.extend(jStat.chisquare, { - pdf: function pdf(x, dof) { - if (x < 0) - return 0; - return (x === 0 && dof === 2) ? 0.5 : - Math.exp((dof / 2 - 1) * Math.log(x) - x / 2 - (dof / 2) * - Math.log(2) - jStat.gammaln(dof / 2)); - }, + while(scaledCDF > 1 && samplesDone < n) { + // Intermediate result is growing too big. Apply some of the + // normalization to shrink everything. - cdf: function cdf(x, dof) { - if (x < 0) - return 0; - return jStat.lowRegGamma(dof / 2, x / 2); - }, + var factor = 1 - (m / (N - samplesDone)); - inv: function(p, dof) { - return 2 * jStat.gammapinv(p, 0.5 * dof); - }, + scaledPDF *= factor; + scaledCDF *= factor; - mean : function(dof) { - return dof; - }, + // Say we've normalized by this sample already. + samplesDone++; + } - // TODO: this is an approximation (is there a better way?) - median: function median(dof) { - return dof * Math.pow(1 - (2 / (9 * dof)), 3); - }, + // Work out the partially-normalized hypergeometric PDF for the next + // number of successes + scaledPDF *= (n - i) * (m - i) / ((i + 1) * (N - m - n + i + 1)); - mode: function mode(dof) { - return (dof - 2 > 0) ? dof - 2 : 0; - }, + // Add to the CDF answer. + scaledCDF += scaledPDF; + } - sample: function sample(dof) { - return jStat.randg(dof / 2) * 2; - }, + for(; samplesDone < n; samplesDone++) { + // Apply all the rest of the normalization + scaledCDF *= 1 - (m / (N - samplesDone)); + } - variance: function variance(dof) { - return 2 * dof; + // Bound answer sanely before returning. + return Math.min(1, Math.max(0, scaledCDF)); + } } }); -// extend exponential function with static methods -jStat.extend(jStat.exponential, { - pdf: function pdf(x, rate) { - return x < 0 ? 0 : rate * Math.exp(-rate * x); - }, - - cdf: function cdf(x, rate) { - return x < 0 ? 0 : 1 - Math.exp(-rate * x); - }, - - inv: function(p, rate) { - return -Math.log(1 - p) / rate; - }, +// extend uniform function with static methods +jStat.extend(jStat.poisson, { + pdf: function pdf(k, l) { + if (l < 0 || (k % 1) !== 0 || k < 0) { + return 0; + } - mean : function(rate) { - return 1 / rate; + return Math.pow(l, k) * Math.exp(-l) / jStat.factorial(k); }, - median: function (rate) { - return (1 / rate) * Math.log(2); + cdf: function cdf(x, l) { + var sumarr = [], + k = 0; + if (x < 0) return 0; + for (; k <= x; k++) { + sumarr.push(jStat.poisson.pdf(k, l)); + } + return jStat.sum(sumarr); }, - mode: function mode(rate) { - return 0; + mean : function(l) { + return l; }, - sample: function sample(rate) { - return -1 / rate * Math.log(Math.random()); + variance : function(l) { + return l; }, - variance : function(rate) { - return Math.pow(rate, -2); + sample: function sample(l) { + var p = 1, k = 0, L = Math.exp(-l); + do { + k++; + p *= Math.random(); + } while (p > L); + return k - 1; } }); +// extend triangular function with static methods +jStat.extend(jStat.triangular, { + pdf: function pdf(x, a, b, c) { + if (b <= a || c < a || c > b) { + return NaN; + } else { + if (x < a || x > b) { + return 0; + } else if (x < c) { + return (2 * (x - a)) / ((b - a) * (c - a)); + } else if (x === c) { + return (2 / (b - a)); + } else { // x > c + return (2 * (b - x)) / ((b - a) * (b - c)); + } + } + }, - -// extend gamma function with static methods -jStat.extend(jStat.gamma, { - pdf: function pdf(x, shape, scale) { - if (x < 0) + cdf: function cdf(x, a, b, c) { + if (b <= a || c < a || c > b) + return NaN; + if (x <= a) return 0; - return (x === 0 && shape === 1) ? 1 / scale : - Math.exp((shape - 1) * Math.log(x) - x / scale - - jStat.gammaln(shape) - shape * Math.log(scale)); + else if (x >= b) + return 1; + if (x <= c) + return Math.pow(x - a, 2) / ((b - a) * (c - a)); + else // x > c + return 1 - Math.pow(b - x, 2) / ((b - a) * (b - c)); }, - cdf: function cdf(x, shape, scale) { - if (x < 0) - return 0; - return jStat.lowRegGamma(shape, x / scale); + inv: function inv(p, a, b, c) { + if (b <= a || c < a || c > b) { + return NaN; + } else { + if (p <= ((c - a) / (b - a))) { + return a + (b - a) * Math.sqrt(p * ((c - a) / (b - a))); + } else { // p > ((c - a) / (b - a)) + return a + (b - a) * (1 - Math.sqrt((1 - p) * (1 - ((c - a) / (b - a))))); + } + } }, - inv: function(p, shape, scale) { - return jStat.gammapinv(p, shape) * scale; + mean: function mean(a, b, c) { + return (a + b + c) / 3; }, - mean : function(shape, scale) { - return shape * scale; + median: function median(a, b, c) { + if (c <= (a + b) / 2) { + return b - Math.sqrt((b - a) * (b - c)) / Math.sqrt(2); + } else if (c > (a + b) / 2) { + return a + Math.sqrt((b - a) * (c - a)) / Math.sqrt(2); + } }, - mode: function mode(shape, scale) { - if(shape > 1) return (shape - 1) * scale; - return undefined; + mode: function mode(a, b, c) { + return c; }, - sample: function sample(shape, scale) { - return jStat.randg(shape) * scale; + sample: function sample(a, b, c) { + var u = Math.random(); + if (u < ((c - a) / (b - a))) + return a + Math.sqrt(u * (b - a) * (c - a)) + return b - Math.sqrt((1 - u) * (b - a) * (b - c)); }, - variance: function variance(shape, scale) { - return shape * scale * scale; + variance: function variance(a, b, c) { + return (a * a + b * b + c * c - a * b - a * c - b * c) / 18; } }); -// extend inverse gamma function with static methods -jStat.extend(jStat.invgamma, { - pdf: function pdf(x, shape, scale) { - if (x <= 0) - return 0; - return Math.exp(-(shape + 1) * Math.log(x) - scale / x - - jStat.gammaln(shape) + shape * Math.log(scale)); +function laplaceSign(x) { return x / Math.abs(x); } + +jStat.extend(jStat.laplace, { + pdf: function pdf(x, mu, b) { + return (b <= 0) ? 0 : (Math.exp(-Math.abs(x - mu) / b)) / (2 * b); }, - cdf: function cdf(x, shape, scale) { - if (x <= 0) - return 0; - return 1 - jStat.lowRegGamma(shape, scale / x); + cdf: function cdf(x, mu, b) { + if (b <= 0) { return 0; } + + if(x < mu) { + return 0.5 * Math.exp((x - mu) / b); + } else { + return 1 - 0.5 * Math.exp(- (x - mu) / b); + } }, - inv: function(p, shape, scale) { - return scale / jStat.gammapinv(1 - p, shape); + mean: function(mu, b) { + return mu; }, - mean : function(shape, scale) { - return (shape > 1) ? scale / (shape - 1) : undefined; + median: function(mu, b) { + return mu; }, - mode: function mode(shape, scale) { - return scale / (shape + 1); + mode: function(mu, b) { + return mu; }, - sample: function sample(shape, scale) { - return scale / jStat.randg(shape); + variance: function(mu, b) { + return 2 * b * b; }, - variance: function variance(shape, scale) { - if (shape <= 2) - return undefined; - return scale * scale / ((shape - 1) * (shape - 1) * (shape - 2)); + sample: function sample(mu, b) { + var u = Math.random() - 0.5; + + return mu - (b * laplaceSign(u) * Math.log(1 - (2 * Math.abs(u)))); } }); +}(this.jStat, Math)); +/* Provides functions for the solution of linear system of equations, integration, extrapolation, + * interpolation, eigenvalue problems, differential equations and PCA analysis. */ -// extend kumaraswamy function with static methods -jStat.extend(jStat.kumaraswamy, { - pdf: function pdf(x, alpha, beta) { - if (x === 0 && alpha === 1) - return beta; - else if (x === 1 && beta === 1) - return alpha; - return Math.exp(Math.log(alpha) + Math.log(beta) + (alpha - 1) * - Math.log(x) + (beta - 1) * - Math.log(1 - Math.pow(x, alpha))); - }, +(function(jStat, Math) { - cdf: function cdf(x, alpha, beta) { - if (x < 0) - return 0; - else if (x > 1) - return 1; - return (1 - Math.pow(1 - Math.pow(x, alpha), beta)); - }, +var push = Array.prototype.push; +var isArray = jStat.utils.isArray; - inv: function inv(p, alpha, beta) { - return Math.pow(1 - Math.pow(1 - p, 1 / beta), 1 / alpha); - }, +function isUsable(arg) { + return isArray(arg) || arg instanceof jStat; +} - mean : function(alpha, beta) { - return (beta * jStat.gammafn(1 + 1 / alpha) * - jStat.gammafn(beta)) / (jStat.gammafn(1 + 1 / alpha + beta)); - }, +jStat.extend({ - median: function median(alpha, beta) { - return Math.pow(1 - Math.pow(2, -1 / beta), 1 / alpha); + // add a vector/matrix to a vector/matrix or scalar + add: function add(arr, arg) { + // check if arg is a vector or scalar + if (isUsable(arg)) { + if (!isUsable(arg[0])) arg = [ arg ]; + return jStat.map(arr, function(value, row, col) { + return value + arg[row][col]; + }); + } + return jStat.map(arr, function(value) { return value + arg; }); }, - mode: function mode(alpha, beta) { - if (!(alpha >= 1 && beta >= 1 && (alpha !== 1 && beta !== 1))) - return undefined; - return Math.pow((alpha - 1) / (alpha * beta - 1), 1 / alpha); + // subtract a vector or scalar from the vector + subtract: function subtract(arr, arg) { + // check if arg is a vector or scalar + if (isUsable(arg)) { + if (!isUsable(arg[0])) arg = [ arg ]; + return jStat.map(arr, function(value, row, col) { + return value - arg[row][col] || 0; + }); + } + return jStat.map(arr, function(value) { return value - arg; }); }, - variance: function variance(alpha, beta) { - throw new Error('variance not yet implemented'); - // TODO: complete this - } -}); - - - -// extend lognormal function with static methods -jStat.extend(jStat.lognormal, { - pdf: function pdf(x, mu, sigma) { - if (x <= 0) - return 0; - return Math.exp(-Math.log(x) - 0.5 * Math.log(2 * Math.PI) - - Math.log(sigma) - Math.pow(Math.log(x) - mu, 2) / - (2 * sigma * sigma)); + // matrix division + divide: function divide(arr, arg) { + if (isUsable(arg)) { + if (!isUsable(arg[0])) arg = [ arg ]; + return jStat.multiply(arr, jStat.inv(arg)); + } + return jStat.map(arr, function(value) { return value / arg; }); }, - cdf: function cdf(x, mu, sigma) { - if (x < 0) - return 0; - return 0.5 + - (0.5 * jStat.erf((Math.log(x) - mu) / Math.sqrt(2 * sigma * sigma))); + // matrix multiplication + multiply: function multiply(arr, arg) { + var row, col, nrescols, sum, + nrow = arr.length, + ncol = arr[0].length, + res = jStat.zeros(nrow, nrescols = (isUsable(arg)) ? arg[0].length : ncol), + rescols = 0; + if (isUsable(arg)) { + for (; rescols < nrescols; rescols++) { + for (row = 0; row < nrow; row++) { + sum = 0; + for (col = 0; col < ncol; col++) + sum += arr[row][col] * arg[col][rescols]; + res[row][rescols] = sum; + } + } + return (nrow === 1 && rescols === 1) ? res[0][0] : res; + } + return jStat.map(arr, function(value) { return value * arg; }); }, - inv: function(p, mu, sigma) { - return Math.exp(-1.41421356237309505 * sigma * jStat.erfcinv(2 * p) + mu); + // Returns the dot product of two matricies + dot: function dot(arr, arg) { + if (!isUsable(arr[0])) arr = [ arr ]; + if (!isUsable(arg[0])) arg = [ arg ]; + // convert column to row vector + var left = (arr[0].length === 1 && arr.length !== 1) ? jStat.transpose(arr) : arr, + right = (arg[0].length === 1 && arg.length !== 1) ? jStat.transpose(arg) : arg, + res = [], + row = 0, + nrow = left.length, + ncol = left[0].length, + sum, col; + for (; row < nrow; row++) { + res[row] = []; + sum = 0; + for (col = 0; col < ncol; col++) + sum += left[row][col] * right[row][col]; + res[row] = sum; + } + return (res.length === 1) ? res[0] : res; }, - mean: function mean(mu, sigma) { - return Math.exp(mu + sigma * sigma / 2); + // raise every element by a scalar + pow: function pow(arr, arg) { + return jStat.map(arr, function(value) { return Math.pow(value, arg); }); }, - median: function median(mu, sigma) { - return Math.exp(mu); + // exponentiate every element + exp: function exp(arr) { + return jStat.map(arr, function(value) { return Math.exp(value); }); }, - mode: function mode(mu, sigma) { - return Math.exp(mu - sigma * sigma); + // generate the natural log of every element + log: function exp(arr) { + return jStat.map(arr, function(value) { return Math.log(value); }); }, - sample: function sample(mu, sigma) { - return Math.exp(jStat.randn() * sigma + mu); + // generate the absolute values of the vector + abs: function abs(arr) { + return jStat.map(arr, function(value) { return Math.abs(value); }); }, - variance: function variance(mu, sigma) { - return (Math.exp(sigma * sigma) - 1) * Math.exp(2 * mu + sigma * sigma); - } -}); - - - -// extend noncentralt function with static methods -jStat.extend(jStat.noncentralt, { - pdf: function pdf(x, dof, ncp) { - var tol = 1e-14; - if (Math.abs(ncp) < tol) // ncp approx 0; use student-t - return jStat.studentt.pdf(x, dof) - - if (Math.abs(x) < tol) { // different formula for x == 0 - return Math.exp(jStat.gammaln((dof + 1) / 2) - ncp * ncp / 2 - - 0.5 * Math.log(Math.PI * dof) - jStat.gammaln(dof / 2)); + // computes the p-norm of the vector + // In the case that a matrix is passed, uses the first row as the vector + norm: function norm(arr, p) { + var nnorm = 0, + i = 0; + // check the p-value of the norm, and set for most common case + if (isNaN(p)) p = 2; + // check if multi-dimensional array, and make vector correction + if (isUsable(arr[0])) arr = arr[0]; + // vector norm + for (; i < arr.length; i++) { + nnorm += Math.pow(Math.abs(arr[i]), p); } - - // formula for x != 0 - return dof / x * - (jStat.noncentralt.cdf(x * Math.sqrt(1 + 2 / dof), dof+2, ncp) - - jStat.noncentralt.cdf(x, dof, ncp)); + return Math.pow(nnorm, 1 / p); }, - cdf: function cdf(x, dof, ncp) { - var tol = 1e-14; - var min_iterations = 200; - - if (Math.abs(ncp) < tol) // ncp approx 0; use student-t - return jStat.studentt.cdf(x, dof); + // computes the angle between two vectors in rads + // In case a matrix is passed, this uses the first row as the vector + angle: function angle(arr, arg) { + return Math.acos(jStat.dot(arr, arg) / (jStat.norm(arr) * jStat.norm(arg))); + }, - // turn negative x into positive and flip result afterwards - var flip = false; - if (x < 0) { - flip = true; - ncp = -ncp; + // augment one matrix by another + // Note: this function returns a matrix, not a jStat object + aug: function aug(a, b) { + var newarr = []; + for (var i = 0; i < a.length; i++) { + newarr.push(a[i].slice()); } - - var prob = jStat.normal.cdf(-ncp, 0, 1); - var value = tol + 1; - // use value at last two steps to determine convergence - var lastvalue = value; - var y = x * x / (x * x + dof); - var j = 0; - var p = Math.exp(-ncp * ncp / 2); - var q = Math.exp(-ncp * ncp / 2 - 0.5 * Math.log(2) - - jStat.gammaln(3 / 2)) * ncp; - while (j < min_iterations || lastvalue > tol || value > tol) { - lastvalue = value; - if (j > 0) { - p *= (ncp * ncp) / (2 * j); - q *= (ncp * ncp) / (2 * (j + 1 / 2)); - } - value = p * jStat.beta.cdf(y, j + 0.5, dof / 2) + - q * jStat.beta.cdf(y, j+1, dof/2); - prob += 0.5 * value; - j++; + for (var i = 0; i < newarr.length; i++) { + push.apply(newarr[i], b[i]); } - - return flip ? (1 - prob) : prob; - } -}); - - -// extend normal function with static methods -jStat.extend(jStat.normal, { - pdf: function pdf(x, mean, std) { - return Math.exp(-0.5 * Math.log(2 * Math.PI) - - Math.log(std) - Math.pow(x - mean, 2) / (2 * std * std)); + return newarr; }, - cdf: function cdf(x, mean, std) { - return 0.5 * (1 + jStat.erf((x - mean) / Math.sqrt(2 * std * std))); + // The inv() function calculates the inverse of a matrix + // Create the inverse by augmenting the matrix by the identity matrix of the + // appropriate size, and then use G-J elimination on the augmented matrix. + inv: function inv(a) { + var rows = a.length; + var cols = a[0].length; + var b = jStat.identity(rows, cols); + var c = jStat.gauss_jordan(a, b); + var result = []; + var i = 0; + var j; + + //We need to copy the inverse portion to a new matrix to rid G-J artifacts + for (; i < rows; i++) { + result[i] = []; + for (j = cols; j < c[0].length; j++) + result[i][j - cols] = c[i][j]; + } + return result; }, - inv: function(p, mean, std) { - return -1.41421356237309505 * std * jStat.erfcinv(2 * p) + mean; - }, - - mean : function(mean, std) { - return mean; - }, - - median: function median(mean, std) { - return mean; + // calculate the determinant of a matrix + det: function det(a) { + var alen = a.length, + alend = alen * 2, + vals = new Array(alend), + rowshift = alen - 1, + colshift = alend - 1, + mrow = rowshift - alen + 1, + mcol = colshift, + i = 0, + result = 0, + j; + // check for special 2x2 case + if (alen === 2) { + return a[0][0] * a[1][1] - a[0][1] * a[1][0]; + } + for (; i < alend; i++) { + vals[i] = 1; + } + for (i = 0; i < alen; i++) { + for (j = 0; j < alen; j++) { + vals[(mrow < 0) ? mrow + alen : mrow ] *= a[i][j]; + vals[(mcol < alen) ? mcol + alen : mcol ] *= a[i][j]; + mrow++; + mcol--; + } + mrow = --rowshift - alen + 1; + mcol = --colshift; + } + for (i = 0; i < alen; i++) { + result += vals[i]; + } + for (; i < alend; i++) { + result -= vals[i]; + } + return result; }, - mode: function (mean, std) { - return mean; + gauss_elimination: function gauss_elimination(a, b) { + var i = 0, + j = 0, + n = a.length, + m = a[0].length, + factor = 1, + sum = 0, + x = [], + maug, pivot, temp, k; + a = jStat.aug(a, b); + maug = a[0].length; + for(i = 0; i < n; i++) { + pivot = a[i][i]; + j = i; + for (k = i + 1; k < m; k++) { + if (pivot < Math.abs(a[k][i])) { + pivot = a[k][i]; + j = k; + } + } + if (j != i) { + for(k = 0; k < maug; k++) { + temp = a[i][k]; + a[i][k] = a[j][k]; + a[j][k] = temp; + } + } + for (j = i + 1; j < n; j++) { + factor = a[j][i] / a[i][i]; + for(k = i; k < maug; k++) { + a[j][k] = a[j][k] - factor * a[i][k]; + } + } + } + for (i = n - 1; i >= 0; i--) { + sum = 0; + for (j = i + 1; j<= n - 1; j++) { + sum = sum + x[j] * a[i][j]; + } + x[i] =(a[i][maug - 1] - sum) / a[i][i]; + } + return x; }, - sample: function sample(mean, std) { - return jStat.randn() * std + mean; + gauss_jordan: function gauss_jordan(a, b) { + var m = jStat.aug(a, b), + h = m.length, + w = m[0].length; + // find max pivot + for (var y = 0; y < h; y++) { + var maxrow = y; + for (var y2 = y+1; y2 < h; y2++) { + if (Math.abs(m[y2][y]) > Math.abs(m[maxrow][y])) + maxrow = y2; + } + var tmp = m[y]; + m[y] = m[maxrow]; + m[maxrow] = tmp + for (var y2 = y+1; y2 < h; y2++) { + c = m[y2][y] / m[y][y]; + for (var x = y; x < w; x++) { + m[y2][x] -= m[y][x] * c; + } + } + } + // backsubstitute + for (var y = h-1; y >= 0; y--) { + c = m[y][y]; + for (var y2 = 0; y2 < y; y2++) { + for (var x = w-1; x > y-1; x--) { + m[y2][x] -= m[y][x] * m[y2][y] / c; + } + } + m[y][y] /= c; + for (var x = h; x < w; x++) { + m[y][x] /= c; + } + } + return m; }, - variance : function(mean, std) { - return std * std; - } -}); - - - -// extend pareto function with static methods -jStat.extend(jStat.pareto, { - pdf: function pdf(x, scale, shape) { - if (x < scale) - return 0; - return (shape * Math.pow(scale, shape)) / Math.pow(x, shape + 1); + lu: function lu(a, b) { + throw new Error('lu not yet implemented'); }, - cdf: function cdf(x, scale, shape) { - if (x < scale) - return 0; - return 1 - Math.pow(scale / x, shape); + cholesky: function cholesky(a, b) { + throw new Error('cholesky not yet implemented'); }, - inv: function inv(p, scale, shape) { - return scale / Math.pow(1 - p, 1 / shape); + gauss_jacobi: function gauss_jacobi(a, b, x, r) { + var i = 0; + var j = 0; + var n = a.length; + var l = []; + var u = []; + var d = []; + var xv, c, h, xk; + for (; i < n; i++) { + l[i] = []; + u[i] = []; + d[i] = []; + for (j = 0; j < n; j++) { + if (i > j) { + l[i][j] = a[i][j]; + u[i][j] = d[i][j] = 0; + } else if (i < j) { + u[i][j] = a[i][j]; + l[i][j] = d[i][j] = 0; + } else { + d[i][j] = a[i][j]; + l[i][j] = u[i][j] = 0; + } + } + } + h = jStat.multiply(jStat.multiply(jStat.inv(d), jStat.add(l, u)), -1); + c = jStat.multiply(jStat.inv(d), b); + xv = x; + xk = jStat.add(jStat.multiply(h, x), c); + i = 2; + while (Math.abs(jStat.norm(jStat.subtract(xk,xv))) > r) { + xv = xk; + xk = jStat.add(jStat.multiply(h, xv), c); + i++; + } + return xk; }, - mean: function mean(scale, shape) { - if (shape <= 1) - return undefined; - return (shape * Math.pow(scale, shape)) / (shape - 1); + gauss_seidel: function gauss_seidel(a, b, x, r) { + var i = 0; + var n = a.length; + var l = []; + var u = []; + var d = []; + var j, xv, c, h, xk; + for (; i < n; i++) { + l[i] = []; + u[i] = []; + d[i] = []; + for (j = 0; j < n; j++) { + if (i > j) { + l[i][j] = a[i][j]; + u[i][j] = d[i][j] = 0; + } else if (i < j) { + u[i][j] = a[i][j]; + l[i][j] = d[i][j] = 0; + } else { + d[i][j] = a[i][j]; + l[i][j] = u[i][j] = 0; + } + } + } + h = jStat.multiply(jStat.multiply(jStat.inv(jStat.add(d, l)), u), -1); + c = jStat.multiply(jStat.inv(jStat.add(d, l)), b); + xv = x; + xk = jStat.add(jStat.multiply(h, x), c); + i = 2; + while (Math.abs(jStat.norm(jStat.subtract(xk, xv))) > r) { + xv = xk; + xk = jStat.add(jStat.multiply(h, xv), c); + i = i + 1; + } + return xk; }, - median: function median(scale, shape) { - return scale * (shape * Math.SQRT2); + SOR: function SOR(a, b, x, r, w) { + var i = 0; + var n = a.length; + var l = []; + var u = []; + var d = []; + var j, xv, c, h, xk; + for (; i < n; i++) { + l[i] = []; + u[i] = []; + d[i] = []; + for (j = 0; j < n; j++) { + if (i > j) { + l[i][j] = a[i][j]; + u[i][j] = d[i][j] = 0; + } else if (i < j) { + u[i][j] = a[i][j]; + l[i][j] = d[i][j] = 0; + } else { + d[i][j] = a[i][j]; + l[i][j] = u[i][j] = 0; + } + } + } + h = jStat.multiply(jStat.inv(jStat.add(d, jStat.multiply(l, w))), + jStat.subtract(jStat.multiply(d, 1 - w), + jStat.multiply(u, w))); + c = jStat.multiply(jStat.multiply(jStat.inv(jStat.add(d, + jStat.multiply(l, w))), b), w); + xv = x; + xk = jStat.add(jStat.multiply(h, x), c); + i = 2; + while (Math.abs(jStat.norm(jStat.subtract(xk, xv))) > r) { + xv = xk; + xk = jStat.add(jStat.multiply(h, xv), c); + i++; + } + return xk; }, - mode: function mode(scale, shape) { - return scale; + householder: function householder(a) { + var m = a.length; + var n = a[0].length; + var i = 0; + var w = []; + var p = []; + var alpha, r, k, j, factor; + for (; i < m - 1; i++) { + alpha = 0; + for (j = i + 1; j < n; j++) + alpha += (a[j][i] * a[j][i]); + factor = (a[i + 1][i] > 0) ? -1 : 1; + alpha = factor * Math.sqrt(alpha); + r = Math.sqrt((((alpha * alpha) - a[i + 1][i] * alpha) / 2)); + w = jStat.zeros(m, 1); + w[i + 1][0] = (a[i + 1][i] - alpha) / (2 * r); + for (k = i + 2; k < m; k++) w[k][0] = a[k][i] / (2 * r); + p = jStat.subtract(jStat.identity(m, n), + jStat.multiply(jStat.multiply(w, jStat.transpose(w)), 2)); + a = jStat.multiply(p, jStat.multiply(a, p)); + } + return a; }, - variance : function(scale, shape) { - if (shape <= 2) - return undefined; - return (scale*scale * shape) / (Math.pow(shape - 1, 2) * (shape - 2)); - } -}); - - - -// extend studentt function with static methods -jStat.extend(jStat.studentt, { - pdf: function pdf(x, dof) { - dof = dof > 1e100 ? 1e100 : dof; - return (1/(Math.sqrt(dof) * jStat.betafn(0.5, dof/2))) * - Math.pow(1 + ((x * x) / dof), -((dof + 1) / 2)); + // TODO: not working properly. + QR: function QR(a, b) { + var m = a.length; + var n = a[0].length; + var i = 0; + var w = []; + var p = []; + var x = []; + var j, alpha, r, k, factor, sum; + for (; i < m - 1; i++) { + alpha = 0; + for (j = i + 1; j < n; j++) + alpha += (a[j][i] * a[j][i]); + factor = (a[i + 1][i] > 0) ? -1 : 1; + alpha = factor * Math.sqrt(alpha); + r = Math.sqrt((((alpha * alpha) - a[i + 1][i] * alpha) / 2)); + w = jStat.zeros(m, 1); + w[i + 1][0] = (a[i + 1][i] - alpha) / (2 * r); + for (k = i + 2; k < m; k++) + w[k][0] = a[k][i] / (2 * r); + p = jStat.subtract(jStat.identity(m, n), + jStat.multiply(jStat.multiply(w, jStat.transpose(w)), 2)); + a = jStat.multiply(p, a); + b = jStat.multiply(p, b); + } + for (i = m - 1; i >= 0; i--) { + sum = 0; + for (j = i + 1; j <= n - 1; j++) + sum = x[j] * a[i][j]; + x[i] = b[i][0] / a[i][i]; + } + return x; }, - cdf: function cdf(x, dof) { - var dof2 = dof / 2; - return jStat.ibeta((x + Math.sqrt(x * x + dof)) / - (2 * Math.sqrt(x * x + dof)), dof2, dof2); + jacobi: function jacobi(a) { + var condition = 1; + var count = 0; + var n = a.length; + var e = jStat.identity(n, n); + var ev = []; + var b, i, j, p, q, maxim, theta, s; + // condition === 1 only if tolerance is not reached + while (condition === 1) { + count++; + maxim = a[0][1]; + p = 0; + q = 1; + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + if (i != j) { + if (maxim < Math.abs(a[i][j])) { + maxim = Math.abs(a[i][j]); + p = i; + q = j; + } + } + } + } + if (a[p][p] === a[q][q]) + theta = (a[p][q] > 0) ? Math.PI / 4 : -Math.PI / 4; + else + theta = Math.atan(2 * a[p][q] / (a[p][p] - a[q][q])) / 2; + s = jStat.identity(n, n); + s[p][p] = Math.cos(theta); + s[p][q] = -Math.sin(theta); + s[q][p] = Math.sin(theta); + s[q][q] = Math.cos(theta); + // eigen vector matrix + e = jStat.multiply(e, s); + b = jStat.multiply(jStat.multiply(jStat.inv(s), a), s); + a = b; + condition = 0; + for (i = 1; i < n; i++) { + for (j = 1; j < n; j++) { + if (i != j && Math.abs(a[i][j]) > 0.001) { + condition = 1; + } + } + } + } + for (i = 0; i < n; i++) ev.push(a[i][i]); + //returns both the eigenvalue and eigenmatrix + return [e, ev]; }, - inv: function(p, dof) { - var x = jStat.ibetainv(2 * Math.min(p, 1 - p), 0.5 * dof, 0.5); - x = Math.sqrt(dof * (1 - x) / x); - return (p > 0.5) ? x : -x; + rungekutta: function rungekutta(f, h, p, t_j, u_j, order) { + var k1, k2, u_j1, k3, k4; + if (order === 2) { + while (t_j <= p) { + k1 = h * f(t_j, u_j); + k2 = h * f(t_j + h, u_j + k1); + u_j1 = u_j + (k1 + k2) / 2; + u_j = u_j1; + t_j = t_j + h; + } + } + if (order === 4) { + while (t_j <= p) { + k1 = h * f(t_j, u_j); + k2 = h * f(t_j + h / 2, u_j + k1 / 2); + k3 = h * f(t_j + h / 2, u_j + k2 / 2); + k4 = h * f(t_j +h, u_j + k3); + u_j1 = u_j + (k1 + 2 * k2 + 2 * k3 + k4) / 6; + u_j = u_j1; + t_j = t_j + h; + } + } + return u_j; }, - mean: function mean(dof) { - return (dof > 1) ? 0 : undefined; + romberg: function romberg(f, a, b, order) { + var i = 0; + var h = (b - a) / 2; + var x = []; + var h1 = []; + var g = []; + var m, a1, j, k, I, d; + while (i < order / 2) { + I = f(a); + for (j = a, k = 0; j <= b; j = j + h, k++) x[k] = j; + m = x.length; + for (j = 1; j < m - 1; j++) { + I += (((j % 2) !== 0) ? 4 : 2) * f(x[j]); + } + I = (h / 3) * (I + f(b)); + g[i] = I; + h /= 2; + i++; + } + a1 = g.length; + m = 1; + while (a1 !== 1) { + for (j = 0; j < a1 - 1; j++) + h1[j] = ((Math.pow(4, m)) * g[j + 1] - g[j]) / (Math.pow(4, m) - 1); + a1 = h1.length; + g = h1; + h1 = []; + m++; + } + return g; }, - median: function median(dof) { - return 0; + richardson: function richardson(X, f, x, h) { + function pos(X, x) { + var i = 0; + var n = X.length; + var p; + for (; i < n; i++) + if (X[i] === x) p = i; + return p; + } + var n = X.length, + h_min = Math.abs(x - X[pos(X, x) + 1]), + i = 0, + g = [], + h1 = [], + y1, y2, m, a, j; + while (h >= h_min) { + y1 = pos(X, x + h); + y2 = pos(X, x); + g[i] = (f[y1] - 2 * f[y2] + f[2 * y2 - y1]) / (h * h); + h /= 2; + i++; + } + a = g.length; + m = 1; + while (a != 1) { + for (j = 0; j < a - 1; j++) + h1[j] = ((Math.pow(4, m)) * g[j + 1] - g[j]) / (Math.pow(4, m) - 1); + a = h1.length; + g = h1; + h1 = []; + m++; + } + return g; }, - mode: function mode(dof) { - return 0; + simpson: function simpson(f, a, b, n) { + var h = (b - a) / n; + var I = f(a); + var x = []; + var j = a; + var k = 0; + var i = 1; + var m; + for (; j <= b; j = j + h, k++) + x[k] = j; + m = x.length; + for (; i < m - 1; i++) { + I += ((i % 2 !== 0) ? 4 : 2) * f(x[i]); + } + return (h / 3) * (I + f(b)); }, - sample: function sample(dof) { - return jStat.randn() * Math.sqrt(dof / (2 * jStat.randg(dof / 2))); + hermite: function hermite(X, F, dF, value) { + var n = X.length; + var p = 0; + var i = 0; + var l = []; + var dl = []; + var A = []; + var B = []; + var j; + for (; i < n; i++) { + l[i] = 1; + for (j = 0; j < n; j++) { + if (i != j) l[i] *= (value - X[j]) / (X[i] - X[j]); + } + dl[i] = 0; + for (j = 0; j < n; j++) { + if (i != j) dl[i] += 1 / (X [i] - X[j]); + } + A[i] = (1 - 2 * (value - X[i]) * dl[i]) * (l[i] * l[i]); + B[i] = (value - X[i]) * (l[i] * l[i]); + p += (A[i] * F[i] + B[i] * dF[i]); + } + return p; }, - variance: function variance(dof) { - return (dof > 2) ? dof / (dof - 2) : (dof > 1) ? Infinity : undefined; - } -}); - - - -// extend weibull function with static methods -jStat.extend(jStat.weibull, { - pdf: function pdf(x, scale, shape) { - if (x < 0 || scale < 0 || shape < 0) - return 0; - return (shape / scale) * Math.pow((x / scale), (shape - 1)) * - Math.exp(-(Math.pow((x / scale), shape))); - }, - - cdf: function cdf(x, scale, shape) { - return x < 0 ? 0 : 1 - Math.exp(-Math.pow((x / scale), shape)); - }, - - inv: function(p, scale, shape) { - return scale * Math.pow(-Math.log(1 - p), 1 / shape); - }, - - mean : function(scale, shape) { - return scale * jStat.gammafn(1 + 1 / shape); - }, - - median: function median(scale, shape) { - return scale * Math.pow(Math.log(2), 1 / shape); + lagrange: function lagrange(X, F, value) { + var p = 0; + var i = 0; + var j, l; + var n = X.length; + for (; i < n; i++) { + l = F[i]; + for (j = 0; j < n; j++) { + // calculating the lagrange polynomial L_i + if (i != j) l *= (value - X[j]) / (X[i] - X[j]); + } + // adding the lagrange polynomials found above + p += l; + } + return p; }, - mode: function mode(scale, shape) { - if (shape <= 1) - return 0; - return scale * Math.pow((shape - 1) / shape, 1 / shape); + cubic_spline: function cubic_spline(X, F, value) { + var n = X.length; + var i = 0, j; + var A = []; + var B = []; + var alpha = []; + var c = []; + var h = []; + var b = []; + var d = []; + for (; i < n - 1; i++) + h[i] = X[i + 1] - X[i]; + alpha[0] = 0; + for (i = 1; i < n - 1; i++) { + alpha[i] = (3 / h[i]) * (F[i + 1] - F[i]) - + (3 / h[i-1]) * (F[i] - F[i-1]); + } + for (i = 1; i < n - 1; i++) { + A[i] = []; + B[i] = []; + A[i][i-1] = h[i-1]; + A[i][i] = 2 * (h[i - 1] + h[i]); + A[i][i+1] = h[i]; + B[i][0] = alpha[i]; + } + c = jStat.multiply(jStat.inv(A), B); + for (j = 0; j < n - 1; j++) { + b[j] = (F[j + 1] - F[j]) / h[j] - h[j] * (c[j + 1][0] + 2 * c[j][0]) / 3; + d[j] = (c[j + 1][0] - c[j][0]) / (3 * h[j]); + } + for (j = 0; j < n; j++) { + if (X[j] > value) break; + } + j -= 1; + return F[j] + (value - X[j]) * b[j] + jStat.sq(value-X[j]) * + c[j] + (value - X[j]) * jStat.sq(value - X[j]) * d[j]; }, - sample: function sample(scale, shape) { - return scale * Math.pow(-Math.log(Math.random()), 1 / shape); + gauss_quadrature: function gauss_quadrature() { + throw new Error('gauss_quadrature not yet implemented'); }, - variance: function variance(scale, shape) { - return scale * scale * jStat.gammafn(1 + 2 / shape) - - Math.pow(jStat.weibull.mean(scale, shape), 2); + PCA: function PCA(X) { + var m = X.length; + var n = X[0].length; + var flag = false; + var i = 0; + var j, temp1; + var u = []; + var D = []; + var result = []; + var temp2 = []; + var Y = []; + var Bt = []; + var B = []; + var C = []; + var V = []; + var Vt = []; + for (i = 0; i < m; i++) { + u[i] = jStat.sum(X[i]) / n; + } + for (i = 0; i < n; i++) { + B[i] = []; + for(j = 0; j < m; j++) { + B[i][j] = X[j][i] - u[j]; + } + } + B = jStat.transpose(B); + for (i = 0; i < m; i++) { + C[i] = []; + for (j = 0; j < m; j++) { + C[i][j] = (jStat.dot([B[i]], [B[j]])) / (n - 1); + } + } + result = jStat.jacobi(C); + V = result[0]; + D = result[1]; + Vt = jStat.transpose(V); + for (i = 0; i < D.length; i++) { + for (j = i; j < D.length; j++) { + if(D[i] < D[j]) { + temp1 = D[i]; + D[i] = D[j]; + D[j] = temp1; + temp2 = Vt[i]; + Vt[i] = Vt[j]; + Vt[j] = temp2; + } + } + } + Bt = jStat.transpose(B); + for (i = 0; i < m; i++) { + Y[i] = []; + for (j = 0; j < Bt.length; j++) { + Y[i][j] = jStat.dot([Vt[i]], [Bt[j]]); + } + } + return [X, D, Vt, Y]; } }); +// extend jStat.fn with methods that require one argument +(function(funcs) { + for (var i = 0; i < funcs.length; i++) (function(passfunc) { + jStat.fn[passfunc] = function(arg, func) { + var tmpthis = this; + // check for callback + if (func) { + setTimeout(function() { + func.call(tmpthis, jStat.fn[passfunc].call(tmpthis, arg)); + }, 15); + return this; + } + if (typeof jStat[passfunc](this, arg) === 'number') + return jStat[passfunc](this, arg); + else + return jStat(jStat[passfunc](this, arg)); + }; + }(funcs[i])); +}('add divide multiply subtract dot pow exp log abs norm angle'.split(' '))); +}(this.jStat, Math)); +(function(jStat, Math) { -// extend uniform function with static methods -jStat.extend(jStat.uniform, { - pdf: function pdf(x, a, b) { - return (x < a || x > b) ? 0 : 1 / (b - a); - }, - - cdf: function cdf(x, a, b) { - if (x < a) - return 0; - else if (x < b) - return (x - a) / (b - a); - return 1; - }, - - inv: function(p, a, b) { - return a + (p * (b - a)); - }, - - mean: function mean(a, b) { - return 0.5 * (a + b); - }, - - median: function median(a, b) { - return jStat.mean(a, b); - }, - - mode: function mode(a, b) { - throw new Error('mode is not yet implemented'); - }, +var slice = [].slice; +var isNumber = jStat.utils.isNumber; +var isArray = jStat.utils.isArray; - sample: function sample(a, b) { - return (a / 2 + b / 2) + (b / 2 - a / 2) * (2 * Math.random() - 1); +// flag==true denotes use of sample standard deviation +// Z Statistics +jStat.extend({ + // 2 different parameter lists: + // (value, mean, sd) + // (value, array, flag) + zscore: function zscore() { + var args = slice.call(arguments); + if (isNumber(args[1])) { + return (args[0] - args[1]) / args[2]; + } + return (args[0] - jStat.mean(args[1])) / jStat.stdev(args[1], args[2]); }, - variance: function variance(a, b) { - return Math.pow(b - a, 2) / 12; + // 3 different paramter lists: + // (value, mean, sd, sides) + // (zscore, sides) + // (value, array, sides, flag) + ztest: function ztest() { + var args = slice.call(arguments); + var z; + if (isArray(args[1])) { + // (value, array, sides, flag) + z = jStat.zscore(args[0],args[1],args[3]); + return (args[2] === 1) ? + (jStat.normal.cdf(-Math.abs(z), 0, 1)) : + (jStat.normal.cdf(-Math.abs(z), 0, 1)*2); + } else { + if (args.length > 2) { + // (value, mean, sd, sides) + z = jStat.zscore(args[0],args[1],args[2]); + return (args[3] === 1) ? + (jStat.normal.cdf(-Math.abs(z),0,1)) : + (jStat.normal.cdf(-Math.abs(z),0,1)* 2); + } else { + // (zscore, sides) + z = args[0]; + return (args[1] === 1) ? + (jStat.normal.cdf(-Math.abs(z),0,1)) : + (jStat.normal.cdf(-Math.abs(z),0,1)*2); + } + } } }); - - -// extend uniform function with static methods -jStat.extend(jStat.binomial, { - pdf: function pdf(k, n, p) { - return (p === 0 || p === 1) ? - ((n * p) === k ? 1 : 0) : - jStat.combination(n, k) * Math.pow(p, k) * Math.pow(1 - p, n - k); +jStat.extend(jStat.fn, { + zscore: function zscore(value, flag) { + return (value - this.mean()) / this.stdev(flag); }, - cdf: function cdf(x, n, p) { - var binomarr = [], - k = 0; - if (x < 0) { - return 0; - } - if (x < n) { - for (; k <= x; k++) { - binomarr[ k ] = jStat.binomial.pdf(k, n, p); - } - return jStat.sum(binomarr); - } - return 1; + ztest: function ztest(value, sides, flag) { + var zscore = Math.abs(this.zscore(value, flag)); + return (sides === 1) ? + (jStat.normal.cdf(-zscore, 0, 1)) : + (jStat.normal.cdf(-zscore, 0, 1) * 2); } }); - - -// extend uniform function with static methods -jStat.extend(jStat.negbin, { - pdf: function pdf(k, r, p) { - if (k !== k >>> 0) - return false; - if (k < 0) - return 0; - return jStat.combination(k + r - 1, r - 1) * - Math.pow(1 - p, k) * Math.pow(p, r); +// T Statistics +jStat.extend({ + // 2 parameter lists + // (value, mean, sd, n) + // (value, array) + tscore: function tscore() { + var args = slice.call(arguments); + return (args.length === 4) ? + ((args[0] - args[1]) / (args[2] / Math.sqrt(args[3]))) : + ((args[0] - jStat.mean(args[1])) / + (jStat.stdev(args[1], true) / Math.sqrt(args[1].length))); }, - cdf: function cdf(x, r, p) { - var sum = 0, - k = 0; - if (x < 0) return 0; - for (; k <= x; k++) { - sum += jStat.negbin.pdf(k, r, p); + // 3 different paramter lists: + // (value, mean, sd, n, sides) + // (tscore, n, sides) + // (value, array, sides) + ttest: function ttest() { + var args = slice.call(arguments); + var tscore; + if (args.length === 5) { + tscore = Math.abs(jStat.tscore(args[0], args[1], args[2], args[3])); + return (args[4] === 1) ? + (jStat.studentt.cdf(-tscore, args[3]-1)) : + (jStat.studentt.cdf(-tscore, args[3]-1)*2); } - return sum; + if (isNumber(args[1])) { + tscore = Math.abs(args[0]) + return (args[2] == 1) ? + (jStat.studentt.cdf(-tscore, args[1]-1)) : + (jStat.studentt.cdf(-tscore, args[1]-1) * 2); + } + tscore = Math.abs(jStat.tscore(args[0], args[1])) + return (args[2] == 1) ? + (jStat.studentt.cdf(-tscore, args[1].length-1)) : + (jStat.studentt.cdf(-tscore, args[1].length-1) * 2); } }); +jStat.extend(jStat.fn, { + tscore: function tscore(value) { + return (value - this.mean()) / (this.stdev(true) / Math.sqrt(this.cols())); + }, + ttest: function ttest(value, sides) { + return (sides === 1) ? + (1 - jStat.studentt.cdf(Math.abs(this.tscore(value)), this.cols()-1)) : + (jStat.studentt.cdf(-Math.abs(this.tscore(value)), this.cols()-1)*2); + } +}); -// extend uniform function with static methods -jStat.extend(jStat.hypgeom, { - pdf: function pdf(k, N, m, n) { - // Hypergeometric PDF. - - // A simplification of the CDF algorithm below. - - // k = number of successes drawn - // N = population size - // m = number of successes in population - // n = number of items drawn from population - - if(k !== k | 0) { - return false; - } else if(k < 0 || k < m - (N - n)) { - // It's impossible to have this few successes drawn. - return 0; - } else if(k > n || k > m) { - // It's impossible to have this many successes drawn. - return 0; - } else if (m * 2 > N) { - // More than half the population is successes. +// F Statistics +jStat.extend({ + // Paramter list is as follows: + // (array1, array2, array3, ...) + // or it is an array of arrays + // array of arrays conversion + anovafscore: function anovafscore() { + var args = slice.call(arguments), + expVar, sample, sampMean, sampSampMean, tmpargs, unexpVar, i, j; + if (args.length === 1) { + tmpargs = new Array(args[0].length); + for (i = 0; i < args[0].length; i++) { + tmpargs[i] = args[0][i]; + } + args = tmpargs; + } + // 2 sample case + if (args.length === 2) { + return jStat.variance(args[0]) / jStat.variance(args[1]); + } + // Builds sample array + sample = new Array(); + for (i = 0; i < args.length; i++) { + sample = sample.concat(args[i]); + } + sampMean = jStat.mean(sample); + // Computes the explained variance + expVar = 0; + for (i = 0; i < args.length; i++) { + expVar = expVar + args[i].length * Math.pow(jStat.mean(args[i]) - sampMean, 2); + } + expVar /= (args.length - 1); + // Computes unexplained variance + unexpVar = 0; + for (i = 0; i < args.length; i++) { + sampSampMean = jStat.mean(args[i]); + for (j = 0; j < args[i].length; j++) { + unexpVar += Math.pow(args[i][j] - sampSampMean, 2); + } + } + unexpVar /= (sample.length - args.length); + return expVar / unexpVar; + }, - if(n * 2 > N) { - // More than half the population is sampled. + // 2 different paramter setups + // (array1, array2, array3, ...) + // (anovafscore, df1, df2) + anovaftest: function anovaftest() { + var args = slice.call(arguments), + df1, df2, n, i; + if (isNumber(args[0])) { + return 1 - jStat.centralF.cdf(args[0], args[1], args[2]); + } + anovafscore = jStat.anovafscore(args); + df1 = args.length - 1; + n = 0; + for (i = 0; i < args.length; i++) { + n = n + args[i].length; + } + df2 = n - df1 - 1; + return 1 - jStat.centralF.cdf(anovafscore, df1, df2); + }, - return jStat.hypgeom.pdf(N - m - n + k, N, N - m, N - n) - } else { - // Half or less of the population is sampled. + ftest: function ftest(fscore, df1, df2) { + return 1 - jStat.centralF.cdf(fscore, df1, df2); + } +}); - return jStat.hypgeom.pdf(n - k, N, N - m, n); - } +jStat.extend(jStat.fn, { + anovafscore: function anovafscore() { + return jStat.anovafscore(this.toArray()); + }, - } else if(n * 2 > N) { - // Half or less is successes. + anovaftes: function anovaftes() { + var n = 0; + var i; + for (i = 0; i < this.length; i++) { + n = n + this[i].length; + } + return jStat.ftest(this.anovafscore(), this.length - 1, n - this.length); + } +}); - return jStat.hypgeom.pdf(m - k, N, m, N - n); +// Error Bounds +jStat.extend({ + // 2 different parameter setups + // (value, alpha, sd, n) + // (value, alpha, array) + normalci: function normalci() { + var args = slice.call(arguments), + ans = new Array(2), + change; + if (args.length === 4) { + change = Math.abs(jStat.normal.inv(args[1] / 2, 0, 1) * + args[2] / Math.sqrt(args[3])); + } else { + change = Math.abs(jStat.normal.inv(args[1] / 2, 0, 1) * + jStat.stdev(args[2]) / Math.sqrt(args[2].length)); + } + ans[0] = args[0] - change; + ans[1] = args[0] + change; + return ans; + }, - } else if(m < n) { - // We want to have the number of things sampled to be less than the - // successes available. So swap the definitions of successful and sampled. - return jStat.hypgeom.pdf(k, N, n, m); + // 2 different parameter setups + // (value, alpha, sd, n) + // (value, alpha, array) + tci: function tci() { + var args = slice.call(arguments), + ans = new Array(2), + change; + if (args.length === 4) { + change = Math.abs(jStat.studentt.inv(args[1] / 2, args[3] - 1) * + args[2] / Math.sqrt(args[3])); } else { - // If we get here, half or less of the population was sampled, half or - // less of it was successes, and we had fewer sampled things than - // successes. Now we can do this complicated iterative algorithm in an - // efficient way. + change = Math.abs(jStat.studentt.inv(args[1] / 2, args[2].length - 1) * + jStat.stdev(args[2], true) / Math.sqrt(args[2].length)); + } + ans[0] = args[0] - change; + ans[1] = args[0] + change; + return ans; + }, - // The basic premise of the algorithm is that we partially normalize our - // intermediate product to keep it in a numerically good region, and then - // finish the normalization at the end. + significant: function significant(pvalue, alpha) { + return pvalue < alpha; + } +}); - // This variable holds the scaled probability of the current number of - // successes. - var scaledPDF = 1; +jStat.extend(jStat.fn, { + normalci: function normalci(value, alpha) { + return jStat.normalci(value, alpha, this.toArray()); + }, - // This keeps track of how much we have normalized. - var samplesDone = 0; + tci: function tci(value, alpha) { + return jStat.tci(value, alpha, this.toArray()); + } +}); - for(var i = 0; i < k; i++) { - // For every possible number of successes up to that observed... +// internal method for calculating the z-score for a difference of proportions test +function differenceOfProportions(p1, n1, p2, n2) { + if (p1 > 1 || p2 > 1 || p1 <= 0 || p2 <= 0) { + throw new Error("Proportions should be greater than 0 and less than 1") + } + var pooled = (p1 * n1 + p2 * n2) / (n1 + n2); + var se = Math.sqrt(pooled * (1 - pooled) * ((1/n1) + (1/n2))); + return (p1 - p2) / se; +} - while(scaledPDF > 1 && samplesDone < n) { - // Intermediate result is growing too big. Apply some of the - // normalization to shrink everything. +// Difference of Proportions +jStat.extend(jStat.fn, { + oneSidedDifferenceOfProportions: function oneSidedDifferenceOfProportions(p1, n1, p2, n2) { + var z = differenceOfProportions(p1, n1, p2, n2); + return jStat.ztest(z, 1); + }, - scaledPDF *= 1 - (m / (N - samplesDone)); + twoSidedDifferenceOfProportions: function twoSidedDifferenceOfProportions(p1, n1, p2, n2) { + var z = differenceOfProportions(p1, n1, p2, n2); + return jStat.ztest(z, 2); + } +}); - // Say we've normalized by this sample already. - samplesDone++; - } - - // Work out the partially-normalized hypergeometric PDF for the next - // number of successes - scaledPDF *= (n - i) * (m - i) / ((i + 1) * (N - m - n + i + 1)); - } +}(this.jStat, Math)); - for(; samplesDone < n; samplesDone++) { - // Apply all the rest of the normalization - scaledPDF *= 1 - (m / (N - samplesDone)); - } +},{}],2:[function(require,module,exports){ +(function (root, factory) { + 'use strict'; - // Bound answer sanely before returning. - return Math.min(1, Math.max(0, scaledPDF)); + if (typeof exports === 'object') { + module.exports = factory(); + } else if (typeof define === 'function' && define.amd) { + define(factory); + } else { + root.MersenneTwister = factory(); } - }, +}(this, function () { + /** + * A standalone, pure JavaScript implementation of the Mersenne Twister pseudo random number generator. Compatible + * with Node.js, requirejs and browser environments. Packages are available for npm, Jam and Bower. + * + * @module MersenneTwister + * @author Raphael Pigulla + * @license See the attached LICENSE file. + * @version 0.2.3 + */ - cdf: function cdf(x, N, m, n) { - // Hypergeometric CDF. + /* + * Most comments were stripped from the source. If needed you can still find them in the original C code: + * http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.c + * + * The original port to JavaScript, on which this file is based, was done by Sean McCullough. It can be found at: + * https://gist.github.com/banksean/300494 + */ + 'use strict'; - // This algorithm is due to Prof. Thomas S. Ferguson, , - // and comes from his hypergeometric test calculator at - // . + var MAX_INT = 4294967296.0, + N = 624, + M = 397, + UPPER_MASK = 0x80000000, + LOWER_MASK = 0x7fffffff, + MATRIX_A = 0x9908b0df; - // x = number of successes drawn - // N = population size - // m = number of successes in population - // n = number of items drawn from population + /** + * Instantiates a new Mersenne Twister. + * + * @constructor + * @alias module:MersenneTwister + * @since 0.1.0 + * @param {number=} seed The initial seed value. + */ + var MersenneTwister = function (seed) { + if (typeof seed === 'undefined') { + seed = new Date().getTime(); + } - if(x < 0 || x < m - (N - n)) { - // It's impossible to have this few successes drawn or fewer. - return 0; - } else if(x >= n || x >= m) { - // We will always have this many successes or fewer. - return 1; - } else if (m * 2 > N) { - // More than half the population is successes. + this.mt = new Array(N); + this.mti = N + 1; - if(n * 2 > N) { - // More than half the population is sampled. + this.seed(seed); + }; - return jStat.hypgeom.cdf(N - m - n + x, N, N - m, N - n) - } else { - // Half or less of the population is sampled. + /** + * Initializes the state vector by using one unsigned 32-bit integer "seed", which may be zero. + * + * @since 0.1.0 + * @param {number} seed The seed value. + */ + MersenneTwister.prototype.seed = function (seed) { + var s; - return 1 - jStat.hypgeom.cdf(n - x - 1, N, N - m, n); - } + this.mt[0] = seed >>> 0; - } else if(n * 2 > N) { - // Half or less is successes. + for (this.mti = 1; this.mti < N; this.mti++) { + s = this.mt[this.mti - 1] ^ (this.mt[this.mti - 1] >>> 30); + this.mt[this.mti] = + (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253) + this.mti; + this.mt[this.mti] >>>= 0; + } + }; - return 1 - jStat.hypgeom.cdf(m - x - 1, N, m, N - n); + /** + * Initializes the state vector by using an array key[] of unsigned 32-bit integers of the specified length. If + * length is smaller than 624, then each array of 32-bit integers gives distinct initial state vector. This is + * useful if you want a larger seed space than 32-bit word. + * + * @since 0.1.0 + * @param {array} vector The seed vector. + */ + MersenneTwister.prototype.seedArray = function (vector) { + var i = 1, + j = 0, + k = N > vector.length ? N : vector.length, + s; - } else if(m < n) { - // We want to have the number of things sampled to be less than the - // successes available. So swap the definitions of successful and sampled. - return jStat.hypgeom.cdf(x, N, n, m); - } else { - // If we get here, half or less of the population was sampled, half or - // less of it was successes, and we had fewer sampled things than - // successes. Now we can do this complicated iterative algorithm in an - // efficient way. + this.seed(19650218); - // The basic premise of the algorithm is that we partially normalize our - // intermediate sum to keep it in a numerically good region, and then - // finish the normalization at the end. + for (; k > 0; k--) { + s = this.mt[i-1] ^ (this.mt[i-1] >>> 30); - // Holds the intermediate, scaled total CDF. - var scaledCDF = 1; + this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525))) + + vector[j] + j; + this.mt[i] >>>= 0; + i++; + j++; + if (i >= N) { + this.mt[0] = this.mt[N - 1]; + i = 1; + } + if (j >= vector.length) { + j = 0; + } + } - // This variable holds the scaled probability of the current number of - // successes. - var scaledPDF = 1; + for (k = N - 1; k; k--) { + s = this.mt[i - 1] ^ (this.mt[i - 1] >>> 30); + this.mt[i] = + (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1566083941) << 16) + (s & 0x0000ffff) * 1566083941)) - i; + this.mt[i] >>>= 0; + i++; + if (i >= N) { + this.mt[0] = this.mt[N - 1]; + i = 1; + } + } - // This keeps track of how much we have normalized. - var samplesDone = 0; + this.mt[0] = 0x80000000; + }; - for(var i = 0; i < x; i++) { - // For every possible number of successes up to that observed... + /** + * Generates a random unsigned 32-bit integer. + * + * @since 0.1.0 + * @returns {number} + */ + MersenneTwister.prototype.int = function () { + var y, + kk, + mag01 = new Array(0, MATRIX_A); - while(scaledCDF > 1 && samplesDone < n) { - // Intermediate result is growing too big. Apply some of the - // normalization to shrink everything. + if (this.mti >= N) { + if (this.mti === N + 1) { + this.seed(5489); + } - var factor = 1 - (m / (N - samplesDone)); + for (kk = 0; kk < N - M; kk++) { + y = (this.mt[kk] & UPPER_MASK) | (this.mt[kk + 1] & LOWER_MASK); + this.mt[kk] = this.mt[kk + M] ^ (y >>> 1) ^ mag01[y & 1]; + } - scaledPDF *= factor; - scaledCDF *= factor; + for (; kk < N - 1; kk++) { + y = (this.mt[kk] & UPPER_MASK) | (this.mt[kk + 1] & LOWER_MASK); + this.mt[kk] = this.mt[kk + (M - N)] ^ (y >>> 1) ^ mag01[y & 1]; + } - // Say we've normalized by this sample already. - samplesDone++; + y = (this.mt[N - 1] & UPPER_MASK) | (this.mt[0] & LOWER_MASK); + this.mt[N - 1] = this.mt[M - 1] ^ (y >>> 1) ^ mag01[y & 1]; + this.mti = 0; } - // Work out the partially-normalized hypergeometric PDF for the next - // number of successes - scaledPDF *= (n - i) * (m - i) / ((i + 1) * (N - m - n + i + 1)); + y = this.mt[this.mti++]; - // Add to the CDF answer. - scaledCDF += scaledPDF; - } + y ^= (y >>> 11); + y ^= (y << 7) & 0x9d2c5680; + y ^= (y << 15) & 0xefc60000; + y ^= (y >>> 18); - for(; samplesDone < n; samplesDone++) { - // Apply all the rest of the normalization - scaledCDF *= 1 - (m / (N - samplesDone)); - } + return y >>> 0; + }; - // Bound answer sanely before returning. - return Math.min(1, Math.max(0, scaledCDF)); - } - } -}); + /** + * Generates a random unsigned 31-bit integer. + * + * @since 0.1.0 + * @returns {number} + */ + MersenneTwister.prototype.int31 = function () { + return this.int() >>> 1; + }; + /** + * Generates a random real in the interval [0;1] with 32-bit resolution. + * + * @since 0.1.0 + * @returns {number} + */ + MersenneTwister.prototype.real = function () { + return this.int() * (1.0 / (MAX_INT - 1)); + }; + /** + * Generates a random real in the interval ]0;1[ with 32-bit resolution. + * + * @since 0.1.0 + * @returns {number} + */ + MersenneTwister.prototype.realx = function () { + return (this.int() + 0.5) * (1.0 / MAX_INT); + }; -// extend uniform function with static methods -jStat.extend(jStat.poisson, { - pdf: function pdf(k, l) { - if (l < 0 || (k % 1) !== 0 || k < 0) { - return 0; - } + /** + * Generates a random real in the interval [0;1[ with 32-bit resolution. + * + * @since 0.1.0 + * @returns {number} + */ + MersenneTwister.prototype.rnd = function () { + return this.int() * (1.0 / MAX_INT); + }; - return Math.pow(l, k) * Math.exp(-l) / jStat.factorial(k); - }, + /** + * Generates a random real in the interval [0;1[ with 32-bit resolution. + * + * Same as .rnd() method - for consistency with Math.random() interface. + * + * @since 0.2.0 + * @returns {number} + */ + MersenneTwister.prototype.random = MersenneTwister.prototype.rnd; - cdf: function cdf(x, l) { - var sumarr = [], - k = 0; - if (x < 0) return 0; - for (; k <= x; k++) { - sumarr.push(jStat.poisson.pdf(k, l)); - } - return jStat.sum(sumarr); - }, + /** + * Generates a random real in the interval [0;1[ with 53-bit resolution. + * + * @since 0.1.0 + * @returns {number} + */ + MersenneTwister.prototype.rndHiRes = function () { + var a = this.int() >>> 5, + b = this.int() >>> 6; - mean : function(l) { - return l; - }, + return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0); + }; - variance : function(l) { - return l; - }, + var instance = new MersenneTwister(); - sample: function sample(l) { - var p = 1, k = 0, L = Math.exp(-l); - do { - k++; - p *= Math.random(); - } while (p > L); - return k - 1; - } -}); + /** + * A static version of [rnd]{@link module:MersenneTwister#rnd} on a randomly seeded instance. + * + * @static + * @function random + * @memberof module:MersenneTwister + * @returns {number} + */ + MersenneTwister.random = function () { + return instance.rnd(); + }; -// extend triangular function with static methods -jStat.extend(jStat.triangular, { - pdf: function pdf(x, a, b, c) { - if (b <= a || c < a || c > b) { - return NaN; - } else { - if (x < a || x > b) { - return 0; - } else if (x < c) { - return (2 * (x - a)) / ((b - a) * (c - a)); - } else if (x === c) { - return (2 / (b - a)); - } else { // x > c - return (2 * (b - x)) / ((b - a) * (b - c)); - } - } - }, + return MersenneTwister; +})); - cdf: function cdf(x, a, b, c) { - if (b <= a || c < a || c > b) - return NaN; - if (x <= a) - return 0; - else if (x >= b) - return 1; - if (x <= c) - return Math.pow(x - a, 2) / ((b - a) * (c - a)); - else // x > c - return 1 - Math.pow(b - x, 2) / ((b - a) * (b - c)); - }, +},{}],3:[function(require,module,exports){ +(function() { + var util = require('./util'), + extend = util.extend, + jStat = require('jStat').jStat, + assert = require('assert') - inv: function inv(p, a, b, c) { - if (b <= a || c < a || c > b) { - return NaN; - } else { - if (p <= ((c - a) / (b - a))) { - return a + (b - a) * Math.sqrt(p * ((c - a) / (b - a))); - } else { // p > ((c - a) / (b - a)) - return a + (b - a) * (1 - Math.sqrt((1 - p) * (1 - ((c - a) / (b - a))))); - } + function toJSON(conf) { + var assocs = this + conf = conf || {} + if (conf.shortForm) { + var gt = [] + for (var g = 0; g < assocs.genes(); ++g) + assocs.termsByGene[g].forEach (function(t) { + gt.push ([assocs.geneName[g], assocs.ontology.termName[t]]) + }) + return gt + } + var seen = {} + var idAliasTerm = assocs.geneName.map (function(gn,gi) { + seen[gn.toUpperCase()] = 1 + return [gn,[],assocs.termsByGene[gi].map (assocs.ontology.getTermName.bind(assocs.ontology))] + }) + Object.keys(assocs.geneIndex).sort().forEach (function(gn) { + var uc = gn.toUpperCase() + if (!seen[uc]) { + seen[uc] = true + var gi = assocs.geneIndex[gn] + if (gn != assocs.geneName[gi]) + idAliasTerm[gi][1].push(gn) + } + }) + return { idAliasTerm: idAliasTerm } } - }, - - mean: function mean(a, b, c) { - return (a + b + c) / 3; - }, - median: function median(a, b, c) { - if (c <= (a + b) / 2) { - return b - Math.sqrt((b - a) * (b - c)) / Math.sqrt(2); - } else if (c > (a + b) / 2) { - return a + Math.sqrt((b - a) * (c - a)) / Math.sqrt(2); + function hypergeometricPValues (geneSet) { + var assocs = this + var ontology = assocs.ontology + return assocs.genesByTerm.map (function (genesForTerm, term) { + var genesForTermInSet = geneSet.filter (function (gene) { + return assocs.geneHasTerm[gene][term] + }) + var n = assocs.genes(), + nPresent = genesForTerm.length, + nAbsent = n - nPresent, + nInSet = geneSet.length, + logDenominator = util.logBinomialCoefficient(n,nInSet), + p = 0 + for (var nPresentInSet = genesForTermInSet.length; + nPresentInSet <= nInSet && nPresentInSet <= nPresent; + ++nPresentInSet) { + var nAbsentInSet = nInSet - nPresentInSet + p += Math.exp (util.logBinomialCoefficient(nPresent,nPresentInSet) + + util.logBinomialCoefficient(nAbsent,nAbsentInSet) + - logDenominator) + } + return p + }) } - }, - mode: function mode(a, b, c) { - return c; - }, + function validateGeneNames (geneNames) { + var assocs = this + var missing = {} + var geneIndices = [] + var suppliedGeneName = assocs.geneName.slice(0) + var regex = /^\s*(.+?)\s*$/ + geneNames.map (function (name) { + var match = regex.exec(name) + if (match != null) { + var g = match[1] + if (g in assocs.geneIndex) { + var gi = assocs.geneIndex[g] + geneIndices.push (gi) + suppliedGeneName[gi] = g + } else if (g.toUpperCase() in assocs.geneIndex) { + var gi = assocs.geneIndex[g.toUpperCase()] + geneIndices.push (gi) + suppliedGeneName[gi] = g + } else + missing[g] = (missing[g] || 0) + 1 + } + }) + var missingGeneNames = Object.keys(missing) + return { geneNames: geneNames, + resolvedGeneIndices: util.removeDups(geneIndices), + missingGeneNames: missingGeneNames, + suppliedGeneName: suppliedGeneName } + } + + function Assocs (conf) { + var assocs = this + conf = extend ({closure:true}, conf) + var ontology = conf.ontology + if (conf.assocs) { + var geneTermList = conf.assocs + conf.idAliasTerm = geneTermList.map (function(gt) { + return [gt[0], [], [gt[1]]] + }) + } + var idAliasTerm = conf.idAliasTerm + extend (assocs, + { 'ontology': ontology, + 'geneName': [], + 'geneIndex': {}, - sample: function sample(a, b, c) { - var u = Math.random(); - if (u < ((c - a) / (b - a))) - return a + Math.sqrt(u * (b - a) * (c - a)) - return b - Math.sqrt((1 - u) * (b - a) * (b - c)); - }, + 'genesByTerm': [], + 'termsByGene': [], + 'geneHasTerm': {}, - variance: function variance(a, b, c) { - return (a * a + b * b + c * c - a * b - a * c - b * c) / 18; - } -}); + 'genes': function() { return this.geneName.length }, + 'terms': function() { return this.ontology.terms() }, -function laplaceSign(x) { return x / Math.abs(x); } + 'relevantTerms': function() { + var assocs = this + return util.iota(assocs.terms()).filter (function(term) { + return assocs.genesByTerm[term].length > 0 + && assocs.termIsExemplar(term) + && !ontology.doNotAnnotate[term] + }) + }, + 'relevantTermsForGeneSet': function (geneSet) { + var assocs = this + return util.removeDups (geneSet.reduce (function(termList,g) { + return termList.concat (assocs.termsByGene[g]) + }, [])).filter (function (term) { + return assocs.termIsExemplar(term) + && !ontology.doNotAnnotate[term] + }).sort(util.numCmp) + }, -jStat.extend(jStat.laplace, { - pdf: function pdf(x, mu, b) { - return (b <= 0) ? 0 : (Math.exp(-Math.abs(x - mu) / b)) / (2 * b); - }, + 'equivClassByTerm': [], + 'termsInEquivClass': [], + 'getExemplar': function(termIndex) { + return this.termsInEquivClass[this.equivClassByTerm[termIndex]][0] + }, + 'termIsExemplar': function(termIndex) { + return this.getExemplar(termIndex) == termIndex + }, + 'termEquivalents': function() { + var assocs = this + return util.keyValListToObj (assocs.termsInEquivClass.filter (function(l) { + return l.length > 1 + }).map (function(l) { + var n = l.map (function(ti) { return assocs.ontology.termName[ti] }) + return [n[0], n.slice(1)] + })) + }, + + 'nAssocs': 0, + 'hypergeometricPValues': hypergeometricPValues, + 'validateGeneNames': validateGeneNames, + 'toJSON': toJSON + }) - cdf: function cdf(x, mu, b) { - if (b <= 0) { return 0; } + var closure + if (conf.closure) + closure = ontology.transitiveClosure() + else { + closure = [] + for (var t = 0; t < ontology.terms(); ++t) + closure.push ([t]) + } - if(x < mu) { - return 0.5 * Math.exp((x - mu) / b); - } else { - return 1 - 0.5 * Math.exp(- (x - mu) / b); - } - }, + var gtCount = [], missing = {} + idAliasTerm.forEach (function(iat) { + var gene = iat[0] + var aliases = iat[1] + var terms = iat[2] - mean: function(mu, b) { - return mu; - }, + if (!(gene in assocs.geneIndex)) { + var gi = assocs.genes() + assocs.geneName.push (gene) + gtCount.push ({}) + assocs.geneIndex[gene] = gi + assocs.geneIndex[gene.toUpperCase()] = gi + assocs.geneIndex[gene.toLowerCase()] = gi + aliases.forEach (function (alias) { + assocs.geneIndex[alias] = gi + assocs.geneIndex[alias.toUpperCase()] = gi + assocs.geneIndex[alias.toLowerCase()] = gi + }) + } - median: function(mu, b) { - return mu; - }, + terms.forEach (function (term) { + if (!(term in ontology.termIndex)) + missing[term] = (missing[term] || 0) + 1 + else { + var g = assocs.geneIndex[gene] + var t = ontology.termIndex[term] + closure[t].forEach (function(c) { + ++gtCount[g][c] + }) + } + }) + }) - mode: function(mu, b) { - return mu; - }, + var missingTerms = Object.keys(missing) + if (missingTerms.length > 0 && !conf.ignoreMissingTerms) + console.warn ("Warning: the following terms were not found in the ontology: " + missingTerms) - variance: function(mu, b) { - return 2 * b * b; - }, + assocs.genesByTerm = assocs.ontology.termName.map (function() { return [] }) + assocs.termsByGene = assocs.geneName.map (function() { return [] }) + assocs.geneHasTerm = assocs.geneName.map (function() { return {} }) - sample: function sample(mu, b) { - var u = Math.random() - 0.5; + for (var g = 0; g < assocs.genes(); ++g) { + Object.keys(gtCount[g]).forEach (function(tStr) { + var t = parseInt (tStr) + assocs.termsByGene[g].push (t) + assocs.genesByTerm[t].push (g) + assocs.geneHasTerm[g][t] = 1 + ++assocs.nAssocs + }) + } - return mu - (b * laplaceSign(u) * Math.log(1 - (2 * Math.abs(u)))); - } -}); + assocs.termsByGene = assocs.termsByGene.map (util.sortAscending) + assocs.genesByTerm = assocs.genesByTerm.map (util.sortAscending) -}(this.jStat, Math)); -/* Provides functions for the solution of linear system of equations, integration, extrapolation, - * interpolation, eigenvalue problems, differential equations and PCA analysis. */ + var termClass = {} + var reverseToposort = ontology.toposortTermIndex().slice(0).reverse() + assocs.equivClassByTerm = ontology.termName.map (function() { return null }) + reverseToposort.forEach (function (term) { + var genesStr = "#" + assocs.genesByTerm[term].join(",") + if (!(genesStr in termClass)) { + termClass[genesStr] = assocs.termsInEquivClass.length + assocs.termsInEquivClass.push ([]) + } + var c = termClass[genesStr] + assocs.equivClassByTerm[term] = c + assocs.termsInEquivClass[c].push (term) + }) + } -(function(jStat, Math) { + module.exports = Assocs +}) () -var push = Array.prototype.push; -var isArray = jStat.utils.isArray; +},{"./util":9,"assert":11,"jStat":1}],4:[function(require,module,exports){ +(function() { + var util = require('./util'), + extend = util.extend, + assert = require('assert'), + jStat = require('jStat').jStat -function isUsable(arg) { - return isArray(arg) || arg instanceof jStat; -} + function update (bp, param) { + var val = bp._params[param] + bp._logYes[param] = Math.log (val) + bp._logNo[param] = Math.log (1 - val) + } -jStat.extend({ + function logLikelihood (params, counts) { + var ll = 0 + for (var param in counts.succ) + if (counts.succ.hasOwnProperty (param)) + ll += params._logYes[param] * counts.succ[param] + for (var param in counts.fail) + if (counts.fail.hasOwnProperty (param)) + ll += params._logNo[param] * counts.fail[param] + return ll + } - // add a vector/matrix to a vector/matrix or scalar - add: function add(arr, arg) { - // check if arg is a vector or scalar - if (isUsable(arg)) { - if (!isUsable(arg[0])) arg = [ arg ]; - return jStat.map(arr, function(value, row, col) { - return value + arg[row][col]; - }); + function logPrior (params, priorCounts) { + var lp = 0 + for (var param in params._params) + lp += Math.log (jStat.beta.pdf (params._params[param], + priorCounts.succ[param] + 1, + priorCounts.fail[param] + 1)) + return lp } - return jStat.map(arr, function(value) { return value + arg; }); - }, - // subtract a vector or scalar from the vector - subtract: function subtract(arr, arg) { - // check if arg is a vector or scalar - if (isUsable(arg)) { - if (!isUsable(arg[0])) arg = [ arg ]; - return jStat.map(arr, function(value, row, col) { - return value - arg[row][col] || 0; - }); + function logBetaBernoulliLikelihood (priorCounts) { + var counts = this + var l = 0 + var allCounts = [priorCounts.succ, priorCounts.fail, counts.succ, counts.fail].reduce (util.extend, {}) + Object.keys(allCounts).forEach (function (param) { + l += util.logBetaBernoulli ((priorCounts.succ[param] || 0) + 1, + (priorCounts.fail[param] || 0) + 1, + counts.succ[param] || 0, + counts.fail[param] || 0) + }) + return l } - return jStat.map(arr, function(value) { return value - arg; }); - }, - // matrix division - divide: function divide(arr, arg) { - if (isUsable(arg)) { - if (!isUsable(arg[0])) arg = [ arg ]; - return jStat.multiply(arr, jStat.inv(arg)); + function deltaLogBetaBernoulliLikelihood (deltaCounts) { + var counts = this + var d = 0 + var allCounts = [deltaCounts.succ, deltaCounts.fail, this.succ, this.fail].reduce (util.extend, {}) + Object.keys(allCounts).forEach (function (param) { + var oldSucc = counts.succ[param] || 0 + var oldFail = counts.fail[param] || 0 + var newSucc = oldSucc + (deltaCounts.succ[param] || 0) + var newFail = oldFail + (deltaCounts.fail[param] || 0) + + d += jStat.betaln(newSucc+1,newFail+1) - jStat.betaln(oldSucc+1,oldFail+1) + }) + return d } - return jStat.map(arr, function(value) { return value / arg; }); - }, - // matrix multiplication - multiply: function multiply(arr, arg) { - var row, col, nrescols, sum, nrow, ncol, res, rescols; - // eg: arr = 2 arg = 3 -> 6 for res[0][0] statement closure - if (arr.length === undefined && arg.length === undefined) { - return arr * arg; + function copyCounts() { + return new BernoulliCounts (this) } - nrow = arr.length, - ncol = arr[0].length, - res = jStat.zeros(nrow, nrescols = (isUsable(arg)) ? arg[0].length : ncol), - rescols = 0; - if (isUsable(arg)) { - for (; rescols < nrescols; rescols++) { - for (row = 0; row < nrow; row++) { - sum = 0; - for (col = 0; col < ncol; col++) - sum += arr[row][col] * arg[col][rescols]; - res[row][rescols] = sum; - } - } - return (nrow === 1 && rescols === 1) ? res[0][0] : res; + + function add (counts) { + return this.copy().accum(counts) } - return jStat.map(arr, function(value) { return value * arg; }); - }, - // outer([1,2,3],[4,5,6]) - // === - // [[1],[2],[3]] times [[4,5,6]] - // -> - // [[4,5,6],[8,10,12],[12,15,18]] - outer:function outer(A, B) { - return jStat.multiply(A.map(function(t){ return [t] }), [B]); - }, + function accWithDelete (c, c2, param) { + var newCount = c2[param] + (c[param] || 0) + if (newCount) + c[param] = newCount + else + delete c[param] + } + function accum (counts) { + assert.equal (this.paramSet, counts.paramSet) + for (var param in counts.succ) + accWithDelete (this.succ, counts.succ, param) + for (var param in counts.fail) + accWithDelete (this.fail, counts.fail, param) + return this + } - // Returns the dot product of two matricies - dot: function dot(arr, arg) { - if (!isUsable(arr[0])) arr = [ arr ]; - if (!isUsable(arg[0])) arg = [ arg ]; - // convert column to row vector - var left = (arr[0].length === 1 && arr.length !== 1) ? jStat.transpose(arr) : arr, - right = (arg[0].length === 1 && arg.length !== 1) ? jStat.transpose(arg) : arg, - res = [], - row = 0, - nrow = left.length, - ncol = left[0].length, - sum, col; - for (; row < nrow; row++) { - res[row] = []; - sum = 0; - for (col = 0; col < ncol; col++) - sum += left[row][col] * right[row][col]; - res[row] = sum; + function scale (factor) { + for (var param in this.succ) + this.succ[param] *= factor + for (var param in this.fail) + this.fail[param] *= factor + return this } - return (res.length === 1) ? res[0] : res; - }, - - // raise every element by a scalar - pow: function pow(arr, arg) { - return jStat.map(arr, function(value) { return Math.pow(value, arg); }); - }, - // exponentiate every element - exp: function exp(arr) { - return jStat.map(arr, function(value) { return Math.exp(value); }); - }, + function subtract (counts) { + return this.copy().accum (counts.copy().scale(-1)) + } - // generate the natural log of every element - log: function exp(arr) { - return jStat.map(arr, function(value) { return Math.log(value); }); - }, + function modalParams(params) { + var counts = this + params = params || counts.paramSet.newParams() + params.paramNames().forEach (function(p) { + params.setParam (p, counts.succ[p] / (counts.succ[p] + counts.fail[p])) + }) + return params + } - // generate the absolute values of the vector - abs: function abs(arr) { - return jStat.map(arr, function(value) { return Math.abs(value); }); - }, + function meanParams(params) { + var counts = this + params = params || counts.paramSet.newParams() + params.paramNames().forEach (function(p) { + params.setParam (p, (counts.succ[p] + 1) / (counts.succ[p] + counts.fail[p] + 2)) + }) + return params + } - // computes the p-norm of the vector - // In the case that a matrix is passed, uses the first row as the vector - norm: function norm(arr, p) { - var nnorm = 0, - i = 0; - // check the p-value of the norm, and set for most common case - if (isNaN(p)) p = 2; - // check if multi-dimensional array, and make vector correction - if (isUsable(arr[0])) arr = arr[0]; - // vector norm - for (; i < arr.length; i++) { - nnorm += Math.pow(Math.abs(arr[i]), p); + function sampleParams(generator,params) { + params = params || this.paramSet.newParams() + // quick & dirty hack to bypass jStat's hardwired use of Math.random()... + var oldRandom = Math.random + if (generator) + Math.random = generator.random.bind(generator) + // sample... + for (var param in params._params) + params.setParam (param, jStat.beta.sample (this.succ[param] + 1, this.fail[param] + 1)) + // restore... + Math.random = oldRandom + // return + return params } - return Math.pow(nnorm, 1 / p); - }, - // computes the angle between two vectors in rads - // In case a matrix is passed, this uses the first row as the vector - angle: function angle(arr, arg) { - return Math.acos(jStat.dot(arr, arg) / (jStat.norm(arr) * jStat.norm(arg))); - }, + function BernoulliParamSet (params) { + var bp = this + extend (bp, { + _params: params || {}, + paramNames: function() { return Object.keys(this._params).sort() }, + addParam: function(param) { this._params[param] = 1 }, + toJSON: function() { return this.paramNames() }, + newParams: function(p) { return new BernoulliParamAssignment (this._params) }, + newCounts: function(c) { return new BernoulliCounts (extend ({ paramSet: this }, c)) }, + laplacePrior: function() { + var c = this.newCounts() + this.paramNames().forEach (function(p) { c.succ[p] = c.fail[p] = 1 }) + return c + } + }) + } - // augment one matrix by another - // Note: this function returns a matrix, not a jStat object - aug: function aug(a, b) { - var newarr = []; - for (var i = 0; i < a.length; i++) { - newarr.push(a[i].slice()); + function BernoulliCounts (counts, paramSet) { + var bc = this + extend (bc, { + paramSet: paramSet + || counts.paramSet + || new BernoulliParamSet (extend (extend ({}, counts.succ), counts.fail)), + succ: extend ({}, counts.succ), + fail: extend ({}, counts.fail), + logLikelihood: function(params) { return logLikelihood(params,this) }, + logPrior: function(params) { return logPrior(params,this) }, + logLikeWithPrior: function(prior,params) { + return prior.logPrior(params) + logLikelihood(params,this) + }, + logBetaBernoulliLikelihood: logBetaBernoulliLikelihood, + deltaLogBetaBernoulliLikelihood: deltaLogBetaBernoulliLikelihood, + copy: copyCounts, + add: add, + accum: accum, + scale: scale, + subtract: subtract, + sampleParams: sampleParams, + modalParams: modalParams, + meanParams: meanParams, + toJSON: function() { return { succ: this.succ, fail: this.fail } } + }) } - for (var i = 0; i < newarr.length; i++) { - push.apply(newarr[i], b[i]); + + function BernoulliParamAssignment (params) { + var bp = this + extend (bp, { + _params: params || {}, + _logYes: {}, + _logNo: {}, + paramNames: function() { return Object.keys(this._params).sort() }, + getParam: function(param) { return this._params[param] }, + setParam: function(param,val) { this._params[param] = val; update (bp, param) }, + setParams: function(params) { + var bp = this + Object.keys(params).map (function(p) { bp.setParam (p, params[p]) }) + }, + logLikelihood: function(count) { return logLikelihood(this,count) }, + logPrior: function(prior) { return logPrior(this,prior) }, + logLikeWithPrior: function(prior,count) { return logPrior(this,prior) + logLikelihood(this,count) }, + toJSON: function() { return extend ({}, this._params) }, + newCounts: function(c) { return new BernoulliCounts (extend ({ paramSet: this }, c)) }, + laplacePrior: function() { + var c = this.newCounts() + this.paramNames().forEach (function(p) { c.succ[p] = c.fail[p] = 1 }) + return c + } + }) + Object.keys(params).forEach (function(param) { + update (bp, param) + }) } - return newarr; - }, - // The inv() function calculates the inverse of a matrix - // Create the inverse by augmenting the matrix by the identity matrix of the - // appropriate size, and then use G-J elimination on the augmented matrix. - inv: function inv(a) { - var rows = a.length; - var cols = a[0].length; - var b = jStat.identity(rows, cols); - var c = jStat.gauss_jordan(a, b); - var result = []; - var i = 0; - var j; + module.exports.BernoulliParamSet = BernoulliParamSet + module.exports.BernoulliParamAssignment = BernoulliParamAssignment + module.exports.BernoulliCounts = BernoulliCounts +}) () - //We need to copy the inverse portion to a new matrix to rid G-J artifacts - for (; i < rows; i++) { - result[i] = []; - for (j = cols; j < c[0].length; j++) - result[i][j - cols] = c[i][j]; - } - return result; - }, +},{"./util":9,"assert":11,"jStat":1}],5:[function(require,module,exports){ +(function() { + var assert = require('assert'), + jStat = require('jStat').jStat, + MersenneTwister = require('mersennetwister'), + Model = require('./model'), + Parameterization = require('./parameterization'), + BernoulliCounts = require('./bernoulli').BernoulliCounts, + util = require('./util'), + extend = util.extend - // calculate the determinant of a matrix - det: function det(a) { - var alen = a.length, - alend = alen * 2, - vals = new Array(alend), - rowshift = alen - 1, - colshift = alend - 1, - mrow = rowshift - alen + 1, - mcol = colshift, - i = 0, - result = 0, - j; - // check for special 2x2 case - if (alen === 2) { - return a[0][0] * a[1][1] - a[0][1] * a[1][0]; + function logMove(text) { + var mcmc = this + console.warn ("Move #" + mcmc.samplesIncludingBurn + ": " + text) } - for (; i < alend; i++) { - vals[i] = 1; + + function logTermMove(move) { + var mcmc = this + logMove.bind(mcmc)("(" + Object.keys(move.termStates).map (function(t) { + return mcmc.assocs.ontology.termName[t] + "=>" + move.termStates[t] + }) + ") " + JSON.stringify(move.delta) + " HastingsRatio=" + move.hastingsRatio + " " + + (move.accepted ? "Accept" : "Reject")) } - for (var i = 0; i < alen; i++) { - for (j = 0; j < alen; j++) { - vals[(mrow < 0) ? mrow + alen : mrow ] *= a[i][j]; - vals[(mcol < alen) ? mcol + alen : mcol ] *= a[i][j]; - mrow++; - mcol--; - } - mrow = --rowshift - alen + 1; - mcol = --colshift; + + function logMoves() { + var mcmc = this + mcmc.postMoveCallback.push (function (mcmc, move) { + logTermMove.bind(mcmc) (move) + }) } - for (var i = 0; i < alen; i++) { - result += vals[i]; + + function logState() { + this.postMoveCallback.push (function (mcmc, move) { + console.warn ("Sample #" + move.sample + + ": log-likelihood " + mcmc.quickCollapsedLogLikelihood() + + " (" + mcmc.collapsedLogLikelihood() + ")" + + ", state " + mcmc.models.map (function (model) { + return JSON.stringify (model.toJSON()) + })) + }) } - for (; i < alend; i++) { - result -= vals[i]; + + function logProgress() { + var progressLogger = util.progressLogger ("Sampled", "states") + this.postMoveCallback.push (function (mcmc, move) { + progressLogger (move.sample + 1, move.totalSamples) + }) } - return result; - }, - gauss_elimination: function gauss_elimination(a, b) { - var i = 0, - j = 0, - n = a.length, - m = a[0].length, - factor = 1, - sum = 0, - x = [], - maug, pivot, temp, k; - a = jStat.aug(a, b); - maug = a[0].length; - for(var i = 0; i < n; i++) { - pivot = a[i][i]; - j = i; - for (k = i + 1; k < m; k++) { - if (pivot < Math.abs(a[k][i])) { - pivot = a[k][i]; - j = k; - } - } - if (j != i) { - for(k = 0; k < maug; k++) { - temp = a[i][k]; - a[i][k] = a[j][k]; - a[j][k] = temp; - } - } - for (j = i + 1; j < n; j++) { - factor = a[j][i] / a[i][i]; - for(k = i; k < maug; k++) { - a[j][k] = a[j][k] - factor * a[i][k]; - } - } + function logActiveTerms() { + var mcmc = this + if (!mcmc.activeTermTrace) { + mcmc.activeTermTrace = mcmc.models.map (function() { return [] }) + mcmc.postMoveCallback.push (function (mcmc, move) { + if (mcmc.finishedBurn()) + mcmc.models.forEach (function (model, m) { + mcmc.activeTermTrace[m].push (model.activeTerms()) + }) + }) + } } - for (var i = n - 1; i >= 0; i--) { - sum = 0; - for (j = i + 1; j<= n - 1; j++) { - sum = sum + x[j] * a[i][j]; - } - x[i] =(a[i][maug - 1] - sum) / a[i][i]; + + function logLogLikelihood (includeBurn) { + var mcmc = this + if (!mcmc.logLikelihoodTrace) { + mcmc.logLikelihoodTrace = [] + mcmc.postMoveCallback.push (function (mcmc, move) { + if (includeBurn || mcmc.finishedBurn()) + mcmc.logLikelihoodTrace.push (mcmc.quickCollapsedLogLikelihood()) + }) + } } - return x; - }, - gauss_jordan: function gauss_jordan(a, b) { - var m = jStat.aug(a, b), - h = m.length, - w = m[0].length; - // find max pivot - for (var y = 0; y < h; y++) { - var maxrow = y; - for (var y2 = y+1; y2 < h; y2++) { - if (Math.abs(m[y2][y]) > Math.abs(m[maxrow][y])) - maxrow = y2; - } - var tmp = m[y]; - m[y] = m[maxrow]; - m[maxrow] = tmp - for (var y2 = y+1; y2 < h; y2++) { - c = m[y2][y] / m[y][y]; - for (var x = y; x < w; x++) { - m[y2][x] -= m[y][x] * c; - } - } + function logTermPairs() { + var mcmc = this + mcmc.termPairOccupancy = mcmc.models.map (function (model) { + return util.keyValListToObj (model.relevantTerms.map (function(ti,i) { + return [ti, util.keyValListToObj (model.relevantTerms.slice(i+1).map (function(tj) { + return [tj, 0] + }))] + }))}) + mcmc.termPairOccupancyNorm = mcmc.models.map (function (model) { + return util.keyValListToObj (model.relevantTerms.map (function(ti,i) { + return [ti, 0] + })) + }) + mcmc.termPairSamples = 0 + mcmc.logTermPairFunc = function (mcmc, move) { + if (mcmc.finishedBurn()) { + mcmc.models.forEach (function (model, m) { + var active = model.activeTerms() + for (var i = 0; i < active.length; ++i) { + for (var j = i + 1; j < active.length; ++j) + ++mcmc.termPairOccupancy[m][active[i]][active[j]] + ++mcmc.termPairOccupancyNorm[m][active[i]] + } + }) + ++mcmc.termPairSamples + } + } + mcmc.postMoveCallback.push (mcmc.logTermPairFunc) } - // backsubstitute - for (var y = h-1; y >= 0; y--) { - c = m[y][y]; - for (var y2 = 0; y2 < y; y2++) { - for (var x = w-1; x > y-1; x--) { - m[y2][x] -= m[y][x] * m[y2][y] / c; - } - } - m[y][y] /= c; - for (var x = h; x < w; x++) { - m[y][x] /= c; - } + + function stopLoggingTermPairs() { + var mcmc = this + mcmc.postMoveCallback = mcmc.postMoveCallback.filter (function (func) { + return func !== mcmc.logTermPairFunc + }) + delete mcmc.logTermPairFunc + delete mcmc.termPairOccupancy + delete mcmc.termPairOccupancyNorm + delete mcmc.termPairSamples } - return m; - }, - // solve equation - // Ax=b - // A is upper triangular matrix - // A=[[1,2,3],[0,4,5],[0,6,7]] - // b=[1,2,3] - // triaUpSolve(A,b) // -> [2.666,0.1666,1.666] - // if you use matrix style - // A=[[1,2,3],[0,4,5],[0,6,7]] - // b=[[1],[2],[3]] - // will return [[2.666],[0.1666],[1.666]] - triaUpSolve: function triaUpSolve(A, b) { - var size = A[0].length; - var x = jStat.zeros(1, size)[0]; - var parts; - var matrix_mode = false; - - if (b[0].length != undefined) { - b = b.map(function(i){ return i[0] }); - matrix_mode = true; - } - - jStat.arange(size - 1, -1, -1).forEach(function(i) { - parts = jStat.arange(i + 1,size).map(function(j) { - return x[j] * A[i][j]; - }); - x[i] = (b[i] - jStat.sum(parts)) / A[i][i]; - }); + function logMixing() { + var mcmc = this + var startTime = Date.now(), moveStartMillisecs + mcmc.traceStats = { logLikelihood: [], + moveElapsedMillisecs: {}, + nProposedMoves: {}, + nAcceptedMoves: {} } + Object.keys(mcmc.moveRate).forEach (function(type) { + mcmc.traceStats.moveElapsedMillisecs[type] = 0 + mcmc.traceStats.nAcceptedMoves[type] = 0 + mcmc.traceStats.nProposedMoves[type] = 0 + }) + mcmc.logActiveTerms() + mcmc.logLogLikelihood (false) + mcmc.preMoveCallback.push (function (mcmc) { + moveStartMillisecs = (new Date).getTime() + }) + mcmc.postMoveCallback.push (function (mcmc, move) { + if (mcmc.finishedBurn()) { + mcmc.traceStats.moveElapsedMillisecs[move.type] += (new Date).getTime() - moveStartMillisecs + ++mcmc.traceStats.nProposedMoves[move.type] + if (move.accepted) + ++mcmc.traceStats.nAcceptedMoves[move.type] + } + }) + mcmc.summaryCallback.push (function (mcmc, summ) { + var endTime = Date.now() + summ.mcmc.samplesPerSecond = mcmc.samples / (endTime - startTime) + var points = util.iota (Math.ceil(Math.log2(mcmc.samples))).map (function(x) { return Math.pow(2,x) }) + console.warn ("Computing log-likelihood autocorrelations") + summ.mcmc.logLikeAutoCorrelation = util.autocorrelation (mcmc.logLikelihoodTrace, points) + console.warn ("Computing term autocorrelations") + summ.mcmc.termAutoCorrelation = [] + mcmc.models.forEach (function (model, m) { + var activeTermTrace = mcmc.activeTermTrace[m] + assert.equal (activeTermTrace.length, mcmc.samples) + var termProb = mcmc.termStateOccupancy[m].map (function (occ) { + return occ / mcmc.samples + }) + var termPrecision = termProb.map (function (p) { return 1 / (p - p*p) }) + var dynamicTerms = model.relevantTerms.filter (function (term) { + var p = termProb[term] + return p > 0 && p < 1 + }) + var isDynamic = util.objPredicate (util.listToCounts (dynamicTerms)) + var nTermsHit = dynamicTerms.length - if (matrix_mode) - return x.map(function(i){ return [i] }); - return x; - }, + // t = time, T = term, tmax = max time, Tmax = number of terms + // X^T_t = term T's state at time t + // Mean term autocorrelation = < <(x^T_t - ) (x^T_{t+tau} - ) / <(x^T_t - )^2> >_t >_T + // = 1/tmax 1/Tmax sum_t^tmax sum_T^Tmax (x^T_t - ) (x^T_{t+tau} - ) / ( - ^2) + // = 1/Tmax sum_T^Tmax ((1/tmax sum_t^tmax x^T_t x^T_{t+tau}) - ^2) / ( - ^2) - triaLowSolve: function triaLowSolve(A, b) { - // like to triaUpSolve but A is lower triangular matrix - var size = A[0].length; - var x = jStat.zeros(1, size)[0]; - var parts; + var baseline = util.sumList (dynamicTerms.map (function (term) { + return termProb[term] * termProb[term] * termPrecision[term] + })) + var termAuto = {} + var progressLogger = util.progressLogger ("Computed autocorrelation at", "lag times") + points.forEach (function(tau,n) { + progressLogger (n + 1, points.length) + var R_tau = [] + for (var i = 0; i + tau < mcmc.samples; ++i) { + var commonTerms = util.commonElements (activeTermTrace[i], activeTermTrace[i+tau]) + .filter (isDynamic) + var sum = util.sumList (commonTerms.map (function (term) { return termPrecision[term] })) + R_tau.push (sum) + } + termAuto[tau] = (jStat.mean(R_tau) - baseline) / nTermsHit + }) + summ.mcmc.termAutoCorrelation.push (termAuto) + }) + summ.mcmc.proposedMovesPerSecond = {} + summ.mcmc.moveAcceptRate = {} + Object.keys(mcmc.moveRate).forEach(function(type) { + summ.mcmc.moveAcceptRate[type] = mcmc.traceStats.nAcceptedMoves[type] / mcmc.traceStats.nProposedMoves[type] + summ.mcmc.proposedMovesPerSecond[type] = 1000 * mcmc.traceStats.nProposedMoves[type] / mcmc.traceStats.moveElapsedMillisecs[type] + }) + }) + } - var matrix_mode=false; - if (b[0].length != undefined) { - b = b.map(function(i){ return i[0] }); - matrix_mode = true; + function getCounts(models,prior) { + return models.reduce (function(c,m) { + return c.accum (m.getCounts()) + }, prior.copy()) } - jStat.arange(size).forEach(function(i) { - parts = jStat.arange(i).map(function(j) { - return A[i][j] * x[j]; - }); - x[i] = (b[i] - jStat.sum(parts)) / A[i][i]; - }) + function run(samples) { + var mcmc = this - if (matrix_mode) - return x.map(function(i){ return [i] }); - return x; - }, + if (util.sumList(mcmc.modelWeight) == 0) { + console.warn ("Refusing to run MCMC on a model with no variables") + return + } - // A -> [L,U] - // A=LU - // L is lower triangular matrix - // U is upper triangular matrix - lu: function lu(A) { - var size = A.length; - //var L=jStat.diagonal(jStat.ones(1,size)[0]); - var L = jStat.identity(size); - var R = jStat.zeros(A.length, A[0].length); - var parts; - jStat.arange(size).forEach(function(t) { - R[0][t] = A[0][t]; - }); - jStat.arange(1, size).forEach(function(l) { - jStat.arange(l).forEach(function(i) { - parts = jStat.arange(i).map(function(jj) { - return L[l][jj] * R[jj][i]; - }); - L[l][i] = (A[l][i] - jStat.sum(parts)) / R[i][i]; - }); - jStat.arange(l, size).forEach(function(j) { - parts = jStat.arange(l).map(function(jj) { - return L[l][jj] * R[jj][j]; - }); - R[l][j] = A[i][j] - jStat.sum(parts); - }); - }); - return [L, R]; - }, - - // A -> T - // A=TT' - // T is lower triangular matrix - cholesky: function cholesky(A) { - var size = A.length; - var T = jStat.zeros(A.length, A[0].length); - var parts; - jStat.arange(size).forEach(function(i) { - parts = jStat.arange(i).map(function(t) { - return Math.pow(T[i][t],2); - }); - T[i][i] = Math.sqrt(A[i][i] - jStat.sum(parts)); - jStat.arange(i + 1, size).forEach(function(j) { - parts = jStat.arange(i).map(function(t) { - return T[i][t] * T[j][t]; - }); - T[j][i] = (A[i][j] - jStat.sum(parts)) / T[i][i]; - }); - }); - return T; - }, + var moveTypes = ['flip', 'step', 'jump', 'randomize'] + var moveProposalFuncs = { flip: 'proposeFlipMove', + step: 'proposeStepMove', + jump: 'proposeJumpMove', + randomize: 'proposeRandomizeMove' } + var moveRates = moveTypes.map (function(t) { return mcmc.moveRate[t] }) + + for (var sample = 0; sample < samples; ++sample) { - gauss_jacobi: function gauss_jacobi(a, b, x, r) { - var i = 0; - var j = 0; - var n = a.length; - var l = []; - var u = []; - var d = []; - var xv, c, h, xk; - for (; i < n; i++) { - l[i] = []; - u[i] = []; - d[i] = []; - for (j = 0; j < n; j++) { - if (i > j) { - l[i][j] = a[i][j]; - u[i][j] = d[i][j] = 0; - } else if (i < j) { - u[i][j] = a[i][j]; - l[i][j] = d[i][j] = 0; - } else { - d[i][j] = a[i][j]; - l[i][j] = u[i][j] = 0; - } - } + mcmc.preMoveCallback.forEach (function(callback) { + callback (mcmc) + }) + + var move = { sample: sample, + totalSamples: samples, + type: moveTypes [util.randomIndex (moveRates, mcmc.generator)], + model: mcmc.models [util.randomIndex (mcmc.modelWeight, mcmc.generator)], + logLikelihoodRatio: 0, + accepted: false } + + extend (move, move.model[moveProposalFuncs[move.type]].bind(move.model) ()) + move.model.sampleMoveCollapsed (move, mcmc.countsWithPrior) + + ++mcmc.samplesIncludingBurn + if (mcmc.finishedBurn()) { + + ++mcmc.samples + + mcmc.models.forEach (function(model,n) { + var termStateOccupancy = mcmc.termStateOccupancy[n] + model.activeTerms().forEach (function(term) { + ++termStateOccupancy[term] + }) + var geneFalseOccupancy = mcmc.geneFalseOccupancy[n] + model.falseGenes().forEach (function(gene) { + ++geneFalseOccupancy[gene] + }) + }) + } + + mcmc.postMoveCallback.forEach (function(callback) { + callback (mcmc, move) + }) + } } - h = jStat.multiply(jStat.multiply(jStat.inv(d), jStat.add(l, u)), -1); - c = jStat.multiply(jStat.inv(d), b); - xv = x; - xk = jStat.add(jStat.multiply(h, x), c); - i = 2; - while (Math.abs(jStat.norm(jStat.subtract(xk,xv))) > r) { - xv = xk; - xk = jStat.add(jStat.multiply(h, xv), c); - i++; + + function termSummary (modelIndex, threshold) { + var mcmc = this + threshold = threshold || .01 + return util.keyValListToObj (mcmc.termStateOccupancy[modelIndex].map (function (occ, term) { + return [mcmc.assocs.ontology.termName[term], occ / mcmc.samples] + }).filter (function (keyVal) { return keyVal[1] >= threshold })) } - return xk; - }, - gauss_seidel: function gauss_seidel(a, b, x, r) { - var i = 0; - var n = a.length; - var l = []; - var u = []; - var d = []; - var j, xv, c, h, xk; - for (; i < n; i++) { - l[i] = []; - u[i] = []; - d[i] = []; - for (j = 0; j < n; j++) { - if (i > j) { - l[i][j] = a[i][j]; - u[i][j] = d[i][j] = 0; - } else if (i < j) { - u[i][j] = a[i][j]; - l[i][j] = d[i][j] = 0; - } else { - d[i][j] = a[i][j]; - l[i][j] = u[i][j] = 0; - } - } + function termPairSummary (modelIndex, terms) { + var mcmc = this + var termIndices = terms.map (function(n) { return mcmc.assocs.ontology.termIndex[n] }) + var termName = mcmc.assocs.ontology.termName + var pairProb = util.keyValListToObj (termIndices.map (function(t1) { + return [termName[t1], + util.keyValListToObj (termIndices + .filter (function(t2) { return t2 != t1 }) + .map (function(t2) { + var ti, tj + if (t1 < t2) { ti = t1; tj = t2 } + else { ti = t2; tj = t1 } + return [termName[t2], + mcmc.termPairOccupancy[modelIndex][ti][tj] / mcmc.termPairSamples] + }))] + })) + var singleProb = util.keyValListToObj (termIndices.map (function(t) { + return [termName[t], mcmc.termPairOccupancyNorm[modelIndex][t] / mcmc.termPairSamples] + })) + return { pair: pairProb, + single: singleProb } } - h = jStat.multiply(jStat.multiply(jStat.inv(jStat.add(d, l)), u), -1); - c = jStat.multiply(jStat.inv(jStat.add(d, l)), b); - xv = x; - xk = jStat.add(jStat.multiply(h, x), c); - i = 2; - while (Math.abs(jStat.norm(jStat.subtract(xk, xv))) > r) { - xv = xk; - xk = jStat.add(jStat.multiply(h, xv), c); - i = i + 1; + + function geneFalsePosSummary (modelIndex, threshold) { + return geneSummary (this, modelIndex, true, threshold) } - return xk; - }, - SOR: function SOR(a, b, x, r, w) { - var i = 0; - var n = a.length; - var l = []; - var u = []; - var d = []; - var j, xv, c, h, xk; - for (; i < n; i++) { - l[i] = []; - u[i] = []; - d[i] = []; - for (j = 0; j < n; j++) { - if (i > j) { - l[i][j] = a[i][j]; - u[i][j] = d[i][j] = 0; - } else if (i < j) { - u[i][j] = a[i][j]; - l[i][j] = d[i][j] = 0; - } else { - d[i][j] = a[i][j]; - l[i][j] = u[i][j] = 0; - } - } + function geneFalseNegSummary (modelIndex, threshold) { + return geneSummary (this, modelIndex, false, threshold) } - h = jStat.multiply(jStat.inv(jStat.add(d, jStat.multiply(l, w))), - jStat.subtract(jStat.multiply(d, 1 - w), - jStat.multiply(u, w))); - c = jStat.multiply(jStat.multiply(jStat.inv(jStat.add(d, - jStat.multiply(l, w))), b), w); - xv = x; - xk = jStat.add(jStat.multiply(h, x), c); - i = 2; - while (Math.abs(jStat.norm(jStat.subtract(xk, xv))) > r) { - xv = xk; - xk = jStat.add(jStat.multiply(h, xv), c); - i++; + + function geneSummary (mcmc, modelIndex, wantGeneSet, threshold) { + var model = mcmc.models[modelIndex] + threshold = threshold || .01 + return util.keyValListToObj (mcmc.geneFalseOccupancy[modelIndex].map (function (occ, gene) { + return [gene, occ / mcmc.samples] + }).filter (function (keyVal) { + var inGeneSet = model.inGeneSet[keyVal[0]] + return keyVal[1] >= threshold && (wantGeneSet ? inGeneSet : !inGeneSet) + }).map (function (keyVal) { + return [mcmc.assocs.geneName[keyVal[0]], keyVal[1]] + })) } - return xk; - }, - householder: function householder(a) { - var m = a.length; - var n = a[0].length; - var i = 0; - var w = []; - var p = []; - var alpha, r, k, j, factor; - for (; i < m - 1; i++) { - alpha = 0; - for (j = i + 1; j < n; j++) - alpha += (a[j][i] * a[j][i]); - factor = (a[i + 1][i] > 0) ? -1 : 1; - alpha = factor * Math.sqrt(alpha); - r = Math.sqrt((((alpha * alpha) - a[i + 1][i] * alpha) / 2)); - w = jStat.zeros(m, 1); - w[i + 1][0] = (a[i + 1][i] - alpha) / (2 * r); - for (k = i + 2; k < m; k++) w[k][0] = a[k][i] / (2 * r); - p = jStat.subtract(jStat.identity(m, n), - jStat.multiply(jStat.multiply(w, jStat.transpose(w)), 2)); - a = jStat.multiply(p, jStat.multiply(a, p)); + function hypergeometricSummary (modelIndex, maxPValue) { + var mcmc = this + maxPValue = maxPValue || .05 // default 95% significance + var multiMaxPValue = maxPValue / mcmc.assocs.terms() // Bonferroni correction + return { maxThreshold: maxPValue, + bonferroniMaxThreshold: multiMaxPValue, + term: util.keyValListToObj (mcmc.hypergeometric[modelIndex].map (function (pvalue, term) { + return [mcmc.assocs.ontology.termName[term], pvalue] + }).filter (function (keyVal) { return keyVal[1] <= multiMaxPValue })) + } } - return a; - }, - // A -> [Q,R] - // Q is orthogonal matrix - // R is upper triangular - QR: (function() { - // x -> Q - // find a orthogonal matrix Q st. - // Qx=y - // y is [||x||,0,0,...] - function get_Q1(x) { - var size = x.length; - var norm_x = jStat.norm(x,2); - var e1 = jStat.zeros(1, size)[0]; - e1[0] = 1; - var u = jStat.add(jStat.multiply(jStat.multiply(e1, norm_x), -1), x); - var norm_u = jStat.norm(u, 2); - var v = jStat.divide(u, norm_u); - var Q = jStat.subtract(jStat.identity(size), - jStat.multiply(jStat.outer(v, v), 2)); - return Q; - } - - function qr(A) { - var size = A[0].length; - var QList = []; - jStat.arange(size).forEach(function(i) { - var x = jStat.slice(A, { row: { start: i }, col: i }); - var Q = get_Q1(x); - var Qn = jStat.identity(A.length); - Qn = jStat.sliceAssign(Qn, { row: { start: i }, col: { start: i }}, Q); - A = jStat.multiply(Qn, A); - QList.push(Qn); - }); - var Q = QList.reduce(function(x, y){ return jStat.multiply(x,y) }); - var R = A; - return [Q, R]; - } - - return qr; - })(), - - lstsq: (function(A, b) { - // solve least squard problem for Ax=b as QR decomposition way if b is - // [[b1],[b2],[b3]] form will return [[x1],[x2],[x3]] array form solution - // else b is [b1,b2,b3] form will return [x1,x2,x3] array form solution - function R_I(A) { - A = jStat.copy(A); - var size = A.length; - var I = jStat.identity(size); - jStat.arange(size - 1, -1, -1).forEach(function(i) { - jStat.sliceAssign( - I, { row: i }, jStat.divide(jStat.slice(I, { row: i }), A[i][i])); - jStat.sliceAssign( - A, { row: i }, jStat.divide(jStat.slice(A, { row: i }), A[i][i])); - jStat.arange(i).forEach(function(j) { - var c = jStat.multiply(A[j][i], -1); - var Aj = jStat.slice(A, { row: j }); - var cAi = jStat.multiply(jStat.slice(A, { row: i }), c); - jStat.sliceAssign(A, { row: j }, jStat.add(Aj, cAi)); - var Ij = jStat.slice(I, { row: j }); - var cIi = jStat.multiply(jStat.slice(I, { row: i }), c); - jStat.sliceAssign(I, { row: j }, jStat.add(Ij, cIi)); + function summary (threshold) { + var mcmc = this + threshold = threshold || .01 + var summ = { model: { prior: mcmc.prior.toJSON() }, + termEquivalents: {}, + mcmc: { + samples: mcmc.samples, + burn: mcmc.burn, + moveRate: mcmc.moveRate + }, + summary: mcmc.models.map (function (model, modelIndex) { + return { + hypergeometricPValue: hypergeometricSummary.call (mcmc, modelIndex), + posteriorMarginal: { + minThreshold: threshold, + term: termSummary.bind(mcmc) (modelIndex, threshold), + gene: { + falsePos: geneFalsePosSummary.bind(mcmc) (modelIndex, threshold), + falseNeg: geneFalseNegSummary.bind(mcmc) (modelIndex, threshold) + } + } + } + }) + } + mcmc.summaryCallback.forEach (function(callback) { + callback (mcmc, summ) + }) + var equiv = mcmc.assocs.termEquivalents() + summ.summary.forEach (function (s) { + Object.keys(s.posteriorMarginal.term).forEach (function (t) { + summ.termEquivalents[t] = equiv[t] + }) }) - }); - return I; + return summ } - function qr_solve(A, b){ - var array_mode = false; - if (b[0].length === undefined) { - // [c1,c2,c3] mode - b = b.map(function(x){ return [x] }); - array_mode = true; - } - var QR = jStat.QR(A); - var Q = QR[0]; - var R = QR[1]; - var attrs = A[0].length; - var Q1 = jStat.slice(Q,{col:{end:attrs}}); - var R1 = jStat.slice(R,{row:{end:attrs}}); - var RI = R_I(R1); - var x = jStat.multiply(jStat.multiply(RI, jStat.transpose(Q1)), b); - if (array_mode) - return x.map(function(i){ return i[0] }); - return x; + function nVariables() { + return util.sumList (this.models.map (function (model) { + return model.relevantTerms.length + })) } + + function MCMC (conf) { + var mcmc = this - return qr_solve; - })(), - - jacobi: function jacobi(a) { - var condition = 1; - var count = 0; - var n = a.length; - var e = jStat.identity(n, n); - var ev = []; - var b, i, j, p, q, maxim, theta, s; - // condition === 1 only if tolerance is not reached - while (condition === 1) { - count++; - maxim = a[0][1]; - p = 0; - q = 1; - for (var i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - if (i != j) { - if (maxim < Math.abs(a[i][j])) { - maxim = Math.abs(a[i][j]); - p = i; - q = j; - } - } - } - } - if (a[p][p] === a[q][q]) - theta = (a[p][q] > 0) ? Math.PI / 4 : -Math.PI / 4; - else - theta = Math.atan(2 * a[p][q] / (a[p][p] - a[q][q])) / 2; - s = jStat.identity(n, n); - s[p][p] = Math.cos(theta); - s[p][q] = -Math.sin(theta); - s[q][p] = Math.sin(theta); - s[q][q] = Math.cos(theta); - // eigen vector matrix - e = jStat.multiply(e, s); - b = jStat.multiply(jStat.multiply(jStat.inv(s), a), s); - a = b; - condition = 0; - for (var i = 1; i < n; i++) { - for (j = 1; j < n; j++) { - if (i != j && Math.abs(a[i][j]) > 0.001) { - condition = 1; - } - } - } - } - for (var i = 0; i < n; i++) ev.push(a[i][i]); - //returns both the eigenvalue and eigenmatrix - return [e, ev]; - }, + var assocs = conf.assocs + var parameterization = conf.parameterization || new Parameterization (conf) + var prior = conf.prior + ? new BernoulliCounts(conf.prior,parameterization.paramSet) + : parameterization.paramSet.laplacePrior() + var generator = conf.generator || new MersenneTwister (conf.seed) + var initTerms = conf.initTerms || [] + var models = conf.models + || (conf.geneSets || [conf.geneSet]).map (function(geneSet,n) { + return new Model ({ assocs: assocs, + geneSet: geneSet, + parameterization: parameterization, + prior: prior, + initTerms: initTerms[n], + generator: generator }) + }) + var geneSets = models.map (function(model) { return model.geneSet }) + + var moveRate = conf.moveRate + ? extend ( { flip: 0, step: 0, jump: 0, randomize: 0 }, conf.moveRate) + : { flip: 1, step: 1, jump: 0, randomize: 0 } - rungekutta: function rungekutta(f, h, p, t_j, u_j, order) { - var k1, k2, u_j1, k3, k4; - if (order === 2) { - while (t_j <= p) { - k1 = h * f(t_j, u_j); - k2 = h * f(t_j + h, u_j + k1); - u_j1 = u_j + (k1 + k2) / 2; - u_j = u_j1; - t_j = t_j + h; - } - } - if (order === 4) { - while (t_j <= p) { - k1 = h * f(t_j, u_j); - k2 = h * f(t_j + h / 2, u_j + k1 / 2); - k3 = h * f(t_j + h / 2, u_j + k2 / 2); - k4 = h * f(t_j +h, u_j + k3); - u_j1 = u_j + (k1 + 2 * k2 + 2 * k3 + k4) / 6; - u_j = u_j1; - t_j = t_j + h; - } - } - return u_j; - }, + extend (mcmc, + { + assocs: assocs, + paramSet: parameterization.paramSet, + prior: prior, + models: models, + nVariables: nVariables, - romberg: function romberg(f, a, b, order) { - var i = 0; - var h = (b - a) / 2; - var x = []; - var h1 = []; - var g = []; - var m, a1, j, k, I, d; - while (i < order / 2) { - I = f(a); - for (j = a, k = 0; j <= b; j = j + h, k++) x[k] = j; - m = x.length; - for (j = 1; j < m - 1; j++) { - I += (((j % 2) !== 0) ? 4 : 2) * f(x[j]); - } - I = (h / 3) * (I + f(b)); - g[i] = I; - h /= 2; - i++; - } - a1 = g.length; - m = 1; - while (a1 !== 1) { - for (j = 0; j < a1 - 1; j++) - h1[j] = ((Math.pow(4, m)) * g[j + 1] - g[j]) / (Math.pow(4, m) - 1); - a1 = h1.length; - g = h1; - h1 = []; - m++; - } - return g; - }, + geneSets: geneSets, + hypergeometric: geneSets.map (function (geneSet) { + return assocs.hypergeometricPValues (geneSet) + }), - richardson: function richardson(X, f, x, h) { - function pos(X, x) { - var i = 0; - var n = X.length; - var p; - for (; i < n; i++) - if (X[i] === x) p = i; - return p; + countsWithPrior: getCounts(models,prior), + computeCounts: function() { + return getCounts (this.models, this.paramSet.newCounts()) + }, + computeCountsWithPrior: function() { + return getCounts (this.models, this.prior) + }, + collapsedLogLikelihood: function() { + return this.computeCounts().logBetaBernoulliLikelihood (this.prior) + }, + quickCollapsedLogLikelihood: function() { + return this.countsWithPrior.subtract(this.prior).logBetaBernoulliLikelihood (this.prior) + }, + + generator: generator, + + moveRate: moveRate, + modelWeight: models.map (function(model) { + return model.relevantTerms.length + }), + + samples: 0, + samplesIncludingBurn: 0, + burn: 0, + finishedBurn: function() { return this.samplesIncludingBurn > this.burn }, + + termStateOccupancy: models.map (function(model) { + return model.termName.map (function() { return 0 }) + }), + geneFalseOccupancy: models.map (function(model) { + return model.geneName.map (function() { return 0 }) + }), + + preMoveCallback: [], + postMoveCallback: [], + summaryCallback: [], + + logMoves: logMoves, + logState: logState, + logProgress: logProgress, + logActiveTerms: logActiveTerms, + logMixing: logMixing, + logLogLikelihood: logLogLikelihood, + logRandomNumbers: function() { util.logRandomNumbers(this.generator) }, + + logTermPairs: logTermPairs, + stopLoggingTermPairs: stopLoggingTermPairs, + + run: run, + hypergeometricSummary: hypergeometricSummary, + termSummary: termSummary, + geneFalsePosSummary: geneFalsePosSummary, + geneFalseNegSummary: geneFalseNegSummary, + termPairSummary: termPairSummary, + summary: summary + }) } - var n = X.length, - h_min = Math.abs(x - X[pos(X, x) + 1]), - i = 0, - g = [], - h1 = [], - y1, y2, m, a, j; - while (h >= h_min) { - y1 = pos(X, x + h); - y2 = pos(X, x); - g[i] = (f[y1] - 2 * f[y2] + f[2 * y2 - y1]) / (h * h); - h /= 2; - i++; + + module.exports = MCMC +}) () + +},{"./bernoulli":4,"./model":6,"./parameterization":8,"./util":9,"assert":11,"jStat":1,"mersennetwister":2}],6:[function(require,module,exports){ +(function() { + var assert = require('assert'), + MersenneTwister = require('mersennetwister'), + Parameterization = require('./parameterization'), + util = require('./util'), + extend = util.extend + + function getTermState(t) { return this._termState[t] } + + function setTermState(t,val) { + var model = this + assert (model.isRelevant[t]) + if (model._termState[t] != val) { + var delta = val ? +1 : -1 + model.assocs.genesByTerm[t].forEach (function(g) { + var newCount = model._nActiveTermsByGene[g] + delta + model._nActiveTermsByGene[g] = newCount + var inGeneSet = model.inGeneSet[g] + var isFalse = newCount > 0 ? !inGeneSet : inGeneSet + if (isFalse) + model._isFalseGene[g] = 1 + else + delete model._isFalseGene[g] + }) + if (val) + model._isActiveTerm[t] = true + else + delete model._isActiveTerm[t] + model._termState[t] = val + } } - a = g.length; - m = 1; - while (a != 1) { - for (j = 0; j < a - 1; j++) - h1[j] = ((Math.pow(4, m)) * g[j + 1] - g[j]) / (Math.pow(4, m) - 1); - a = h1.length; - g = h1; - h1 = []; - m++; + + function setTermStates(termStateAssignment) { + for (var t in termStateAssignment) + if (termStateAssignment.hasOwnProperty(t)) + this.setTermState (t, termStateAssignment[t]) } - return g; - }, - simpson: function simpson(f, a, b, n) { - var h = (b - a) / n; - var I = f(a); - var x = []; - var j = a; - var k = 0; - var i = 1; - var m; - for (; j <= b; j = j + h, k++) - x[k] = j; - m = x.length; - for (; i < m - 1; i++) { - I += ((i % 2 !== 0) ? 4 : 2) * f(x[i]); + function countTerm(model,counts,inc,t,state) { + var countObj = state ? counts.succ : counts.fail + var countParam = model.parameterization.names.termPrior[t] + var newCount = inc + (countObj[countParam] || 0) + if (newCount) + countObj[countParam] = newCount + else + delete countObj[countParam] } - return (h / 3) * (I + f(b)); - }, - hermite: function hermite(X, F, dF, value) { - var n = X.length; - var p = 0; - var i = 0; - var l = []; - var dl = []; - var A = []; - var B = []; - var j; - for (; i < n; i++) { - l[i] = 1; - for (j = 0; j < n; j++) { - if (i != j) l[i] *= (value - X[j]) / (X[i] - X[j]); - } - dl[i] = 0; - for (j = 0; j < n; j++) { - if (i != j) dl[i] += 1 / (X [i] - X[j]); - } - A[i] = (1 - 2 * (value - X[i]) * dl[i]) * (l[i] * l[i]); - B[i] = (value - X[i]) * (l[i] * l[i]); - p += (A[i] * F[i] + B[i] * dF[i]); + function countObs(model,counts,inc,isActive,g) { + var inGeneSet = model.inGeneSet[g] + // isActive inGeneSet param + // 0 0 !falsePos + // 0 1 falsePos + // 1 0 falseNeg + // 1 1 !falseNeg + var isFalse = isActive ? !inGeneSet : inGeneSet + var countObj = isFalse ? counts.succ : counts.fail + var countParam = (isActive ? model.parameterization.names.geneFalseNeg : model.parameterization.names.geneFalsePos)[g] + var newCount = inc + (countObj[countParam] || 0) + if (newCount) + countObj[countParam] = newCount + else + delete countObj[countParam] + } + + function getCounts() { + var model = this + var counts = model.paramSet.newCounts() + var param = model.param + model.relevantTerms.forEach (function (t) { + countTerm (model, counts, +1, t, model._termState[t]) + }) + model._nActiveTermsByGene.forEach (function (active, g) { + countObs (model, counts, +1, active > 0, g) + }) + return counts } - return p; - }, - lagrange: function lagrange(X, F, value) { - var p = 0; - var i = 0; - var j, l; - var n = X.length; - for (; i < n; i++) { - l = F[i]; - for (j = 0; j < n; j++) { - // calculating the lagrange polynomial L_i - if (i != j) l *= (value - X[j]) / (X[i] - X[j]); - } - // adding the lagrange polynomials found above - p += l; + function getCountDelta(termStateAssignment) { + var model = this + var param = model.param + var cd = model.paramSet.newCounts() + var nActiveTermsByGene = { + _val: {}, + val: function(g) { return g in this._val ? this._val[g] : model._nActiveTermsByGene[g] }, + add: function(g,delta) { var oldval = this.val(g); this._val[g] = oldval + delta; return oldval } + } + for (var t in termStateAssignment) + if (termStateAssignment.hasOwnProperty(t)) { + assert (model.isRelevant[t]) + var val = termStateAssignment[t] + if (model._termState[t] != val) { + countTerm (model, cd, -1, t, model._termState[t]) + countTerm (model, cd, +1, t, val) + var delta = val ? +1 : -1 + model.assocs.genesByTerm[t].forEach (function(g) { + var oldActive = nActiveTermsByGene.add(g,delta) + var newActive = nActiveTermsByGene.val(g) + if (oldActive != newActive) { + countObs (model, cd, -1, oldActive, g) + countObs (model, cd, +1, newActive, g) + } + }) + } + } + return cd } - return p; - }, - cubic_spline: function cubic_spline(X, F, value) { - var n = X.length; - var i = 0, j; - var A = []; - var B = []; - var alpha = []; - var c = []; - var h = []; - var b = []; - var d = []; - for (; i < n - 1; i++) - h[i] = X[i + 1] - X[i]; - alpha[0] = 0; - for (var i = 1; i < n - 1; i++) { - alpha[i] = (3 / h[i]) * (F[i + 1] - F[i]) - - (3 / h[i-1]) * (F[i] - F[i-1]); + function invert(termStateAssignment) { + var inv = extend ({}, termStateAssignment) + for (var t in inv) + inv[t] = this._termState[t] + return inv } - for (var i = 1; i < n - 1; i++) { - A[i] = []; - B[i] = []; - A[i][i-1] = h[i-1]; - A[i][i] = 2 * (h[i - 1] + h[i]); - A[i][i+1] = h[i]; - B[i][0] = alpha[i]; + + function proposeFlipMove() { + var model = this + var term = util.randomElement (model.relevantTerms, model.generator) + var tsa = {} + + tsa[term] = !model._termState[term] + return { termStates: tsa, + proposalHastingsRatio: 1 } } - c = jStat.multiply(jStat.inv(A), B); - for (j = 0; j < n - 1; j++) { - b[j] = (F[j + 1] - F[j]) / h[j] - h[j] * (c[j + 1][0] + 2 * c[j][0]) / 3; - d[j] = (c[j + 1][0] - c[j][0]) / (3 * h[j]); + + function proposeStepMove() { + return proposeExchangeMove.bind(this) (getNeighbors) } - for (j = 0; j < n; j++) { - if (X[j] > value) break; + + function proposeJumpMove() { + return proposeExchangeMove.bind(this) (getActives) } - j -= 1; - return F[j] + (value - X[j]) * b[j] + jStat.sq(value-X[j]) * - c[j] + (value - X[j]) * jStat.sq(value - X[j]) * d[j]; - }, - gauss_quadrature: function gauss_quadrature() { - throw new Error('gauss_quadrature not yet implemented'); - }, + function getNeighbors(model,term) { return model.relevantNeighbors[term] } + function getActives(model,term) { return model.relevantTerms } - PCA: function PCA(X) { - var m = X.length; - var n = X[0].length; - var flag = false; - var i = 0; - var j, temp1; - var u = []; - var D = []; - var result = []; - var temp2 = []; - var Y = []; - var Bt = []; - var B = []; - var C = []; - var V = []; - var Vt = []; - for (var i = 0; i < m; i++) { - u[i] = jStat.sum(X[i]) / n; + function proposeExchangeMove (getExchangePartners) { + var model = this + var move = { termStates: {}, + proposalHastingsRatio: 1 } + var activeTerms = model.activeTerms() + if (activeTerms.length > 0) { + var term = util.randomElement (activeTerms, model.generator) + var nbrs = getExchangePartners(model,term) + if (nbrs.length > 0) { + var nbr = util.randomElement (nbrs, model.generator) + if (!this._termState[nbr]) { + move.termStates[term] = false + move.termStates[nbr] = true + move.proposalHastingsRatio = nbrs.length / getExchangePartners(model,nbr).length + } + } + } + return move } - for (var i = 0; i < n; i++) { - B[i] = []; - for(j = 0; j < m; j++) { - B[i][j] = X[j][i] - u[j]; - } + + function proposeRandomizeMove() { + var model = this + var tsa = {} + model.relevantTerms.forEach (function (term) { + tsa[term] = (model.generator.random() > 0.5) + }) + return { termStates: tsa, + proposalHastingsRatio: 1 } } - B = jStat.transpose(B); - for (var i = 0; i < m; i++) { - C[i] = []; - for (j = 0; j < m; j++) { - C[i][j] = (jStat.dot([B[i]], [B[j]])) / (n - 1); - } + + function sampleMoveCollapsed(move,counts) { + move.delta = this.getCountDelta (move.termStates) + move.logLikelihoodRatio = counts.deltaLogBetaBernoulliLikelihood (move.delta) + move.hastingsRatio = move.proposalHastingsRatio * Math.exp(move.logLikelihoodRatio) + if (move.hastingsRatio >= 1 || this.generator.random() < move.hastingsRatio) { + this.setTermStates (move.termStates) + move.accepted = true + counts.accum (move.delta) + } else + move.accepted = false + return move.accepted } - result = jStat.jacobi(C); - V = result[0]; - D = result[1]; - Vt = jStat.transpose(V); - for (var i = 0; i < D.length; i++) { - for (j = i; j < D.length; j++) { - if(D[i] < D[j]) { - temp1 = D[i]; - D[i] = D[j]; - D[j] = temp1; - temp2 = Vt[i]; - Vt[i] = Vt[j]; - Vt[j] = temp2; + + function Model (conf) { + var model = this + + var assocs = conf.assocs + var termName = assocs.ontology.termName + var geneName = assocs.geneName + + var validation = assocs.validateGeneNames (conf.geneSet) + if (validation.missingGeneNames.length > 0) + console.warn ("Warning: the following genes were not found in the associations list: " + validation.missingGeneNames) + var geneSet = validation.resolvedGeneIndices + + // "relevant" terms are ones which have at least one associated gene in the geneSet, + // excluding those which are indistinguishable from other terms in the ontology + var relevantTerms = assocs.relevantTermsForGeneSet (geneSet) + var isRelevant = util.listToCounts (relevantTerms) + function relevantFilter(termList) { + return termList.filter (util.objPredicate(isRelevant)) } - } - } - Bt = jStat.transpose(B); - for (var i = 0; i < m; i++) { - Y[i] = []; - for (j = 0; j < Bt.length; j++) { - Y[i][j] = jStat.dot([Vt[i]], [Bt[j]]); - } - } - return [X, D, Vt, Y]; - } -}); + var relevantParents = assocs.ontology.parents.map (relevantFilter) + var relevantChildren = assocs.ontology.children.map (relevantFilter) -// extend jStat.fn with methods that require one argument -(function(funcs) { - for (var i = 0; i < funcs.length; i++) (function(passfunc) { - jStat.fn[passfunc] = function(arg, func) { - var tmpthis = this; - // check for callback - if (func) { - setTimeout(function() { - func.call(tmpthis, jStat.fn[passfunc].call(tmpthis, arg)); - }, 15); - return this; - } - if (typeof jStat[passfunc](this, arg) === 'number') - return jStat[passfunc](this, arg); - else - return jStat(jStat[passfunc](this, arg)); - }; - }(funcs[i])); -}('add divide multiply subtract dot pow exp log abs norm angle'.split(' '))); + // initial state + var termState = termName.map (function() { return false }) + if (conf.initTerms) + conf.initTerms.forEach (function (initTermName) { + if (initTermName in assocs.ontology.termIndex) { + var initTerm = assocs.ontology.termIndex[initTermName] + if (isRelevant[initTerm]) + termState[initTerm] = true + } + }) + + // parameterization + var parameterization = conf.parameterization || new Parameterization (conf) + + // this object encapsulates both the graphical model itself, + // and an assignment of state to the model variables + extend (model, + { + // the graphical model + assocs: assocs, + geneSet: geneSet, + termName: termName, + geneName: geneName, + + inGeneSet: geneName.map (function() { return false }), -}(this.jStat, Math)); -(function(jStat, Math) { + isRelevant: isRelevant, + relevantTerms: relevantTerms, + relevantNeighbors: relevantParents.map (function (parents, term) { + return util.removeDups + ([parents, + relevantChildren[term]] + .concat (assocs.ontology.parents[term].map (function (parent) { + return relevantChildren[parent] + })) + .reduce (function(r,l) { return r.concat(l) }, [])) + .filter (function(t) { return t != term }) + .map(util.parseDecInt) + .sort(util.numCmp) + }), + + parameterization: parameterization, + paramSet: conf.paramSet || parameterization.paramSet, + prior: conf.prior || parameterization.paramSet.laplacePrior(), -var slice = [].slice; -var isNumber = jStat.utils.isNumber; -var isArray = jStat.utils.isArray; + genes: function() { return this.assocs.genes() }, + terms: function() { return this.assocs.terms() }, -// flag==true denotes use of sample standard deviation -// Z Statistics -jStat.extend({ - // 2 different parameter lists: - // (value, mean, sd) - // (value, array, flag) - zscore: function zscore() { - var args = slice.call(arguments); - if (isNumber(args[1])) { - return (args[0] - args[1]) / args[2]; - } - return (args[0] - jStat.mean(args[1])) / jStat.stdev(args[1], args[2]); - }, + // current state of the model + _termState: termState, + _isActiveTerm: {}, - // 3 different paramter lists: - // (value, mean, sd, sides) - // (zscore, sides) - // (value, array, sides, flag) - ztest: function ztest() { - var args = slice.call(arguments); - var z; - if (isArray(args[1])) { - // (value, array, sides, flag) - z = jStat.zscore(args[0],args[1],args[3]); - return (args[2] === 1) ? - (jStat.normal.cdf(-Math.abs(z), 0, 1)) : - (jStat.normal.cdf(-Math.abs(z), 0, 1)*2); - } else { - if (args.length > 2) { - // (value, mean, sd, sides) - z = jStat.zscore(args[0],args[1],args[2]); - return (args[3] === 1) ? - (jStat.normal.cdf(-Math.abs(z),0,1)) : - (jStat.normal.cdf(-Math.abs(z),0,1)* 2); - } else { - // (zscore, sides) - z = args[0]; - return (args[1] === 1) ? - (jStat.normal.cdf(-Math.abs(z),0,1)) : - (jStat.normal.cdf(-Math.abs(z),0,1)*2); - } - } - } -}); + _nActiveTermsByGene: assocs.termsByGene.map (function(terms) { + return terms.reduce (function(accum,t) { + return accum + (termState[t] ? 1 : 0) + }, 0) + }), + _isFalseGene: {}, + + hasActiveTerms: function() { return Object.keys(this._isActiveTerm).length > 0 }, + activeTerms: function() { + return Object.keys(this._isActiveTerm).sort(util.numCmp) + }, -jStat.extend(jStat.fn, { - zscore: function zscore(value, flag) { - return (value - this.mean()) / this.stdev(flag); - }, + falseGenes: function() { + return Object.keys(this._isFalseGene).sort(util.numCmp) + }, + + getTermState: getTermState, + setTermState: setTermState, + setTermStates: setTermStates, + invert: invert, + + getCounts: getCounts, + getCountDelta: getCountDelta, + + toJSON: function() { + var model = this + return model.activeTerms() + .map (function(t) { return model.termName[t] }) + }, - ztest: function ztest(value, sides, flag) { - var zscore = Math.abs(this.zscore(value, flag)); - return (sides === 1) ? - (jStat.normal.cdf(-zscore, 0, 1)) : - (jStat.normal.cdf(-zscore, 0, 1) * 2); - } -}); + // MCMC methods + generator: conf.generator || new MersenneTwister (conf.seed), + + proposeFlipMove: proposeFlipMove, + proposeStepMove: proposeStepMove, + proposeJumpMove: proposeJumpMove, + proposeRandomizeMove: proposeRandomizeMove, -// T Statistics -jStat.extend({ - // 2 parameter lists - // (value, mean, sd, n) - // (value, array) - tscore: function tscore() { - var args = slice.call(arguments); - return (args.length === 4) ? - ((args[0] - args[1]) / (args[2] / Math.sqrt(args[3]))) : - ((args[0] - jStat.mean(args[1])) / - (jStat.stdev(args[1], true) / Math.sqrt(args[1].length))); - }, + sampleMoveCollapsed: sampleMoveCollapsed + }) - // 3 different paramter lists: - // (value, mean, sd, n, sides) - // (tscore, n, sides) - // (value, array, sides) - ttest: function ttest() { - var args = slice.call(arguments); - var tscore; - if (args.length === 5) { - tscore = Math.abs(jStat.tscore(args[0], args[1], args[2], args[3])); - return (args[4] === 1) ? - (jStat.studentt.cdf(-tscore, args[3]-1)) : - (jStat.studentt.cdf(-tscore, args[3]-1)*2); - } - if (isNumber(args[1])) { - tscore = Math.abs(args[0]) - return (args[2] == 1) ? - (jStat.studentt.cdf(-tscore, args[1]-1)) : - (jStat.studentt.cdf(-tscore, args[1]-1) * 2); + geneSet.forEach (function(g) { model.inGeneSet[g] = true }) + termState.forEach (function(s,t) { if (s) model._isActiveTerm[t] = true }) + + model._nActiveTermsByGene.map (function(terms,gene) { + if (terms > 0 ? !model.inGeneSet[gene] : model.inGeneSet[gene]) + model._isFalseGene[gene] = true + }) } - tscore = Math.abs(jStat.tscore(args[0], args[1])) - return (args[2] == 1) ? - (jStat.studentt.cdf(-tscore, args[1].length-1)) : - (jStat.studentt.cdf(-tscore, args[1].length-1) * 2); - } -}); -jStat.extend(jStat.fn, { - tscore: function tscore(value) { - return (value - this.mean()) / (this.stdev(true) / Math.sqrt(this.cols())); - }, + module.exports = Model +}) () - ttest: function ttest(value, sides) { - return (sides === 1) ? - (1 - jStat.studentt.cdf(Math.abs(this.tscore(value)), this.cols()-1)) : - (jStat.studentt.cdf(-Math.abs(this.tscore(value)), this.cols()-1)*2); - } -}); +},{"./parameterization":8,"./util":9,"assert":11,"mersennetwister":2}],7:[function(require,module,exports){ +(function() { + var util = require('./util'), + extend = util.extend, + assert = require('assert') -// F Statistics -jStat.extend({ - // Paramter list is as follows: - // (array1, array2, array3, ...) - // or it is an array of arrays - // array of arrays conversion - anovafscore: function anovafscore() { - var args = slice.call(arguments), - expVar, sample, sampMean, sampSampMean, tmpargs, unexpVar, i, j; - if (args.length === 1) { - tmpargs = new Array(args[0].length); - for (var i = 0; i < args[0].length; i++) { - tmpargs[i] = args[0][i]; - } - args = tmpargs; - } - // 2 sample case - if (args.length === 2) { - return jStat.variance(args[0]) / jStat.variance(args[1]); - } - // Builds sample array - sample = new Array(); - for (var i = 0; i < args.length; i++) { - sample = sample.concat(args[i]); - } - sampMean = jStat.mean(sample); - // Computes the explained variance - expVar = 0; - for (var i = 0; i < args.length; i++) { - expVar = expVar + args[i].length * Math.pow(jStat.mean(args[i]) - sampMean, 2); + function toJSON (conf) { + var onto = this + var json = [] + conf = extend ({ compress: false, + includeTermInfo: true }, + conf) + var parentLookup = conf.compress + ? function(j) { return j } + : function(j) { return onto.termName[j] } + for (var i = 0; i < onto.terms(); ++i) { + json.push ([onto.termName[i]].concat (onto.parents[i].map (parentLookup))) + } + var result = { "termParents" : json } + if (conf.includeTermInfo && onto.termInfo) + result.termInfo = onto.termInfo + var doNotAnnotate = util.iota(onto.terms()).filter (util.objPredicate (onto.doNotAnnotate)) + if (doNotAnnotate.length) + result.doNotAnnotate = doNotAnnotate.map (onto.getTermName.bind(onto)) + return result } - expVar /= (args.length - 1); - // Computes unexplained variance - unexpVar = 0; - for (var i = 0; i < args.length; i++) { - sampSampMean = jStat.mean(args[i]); - for (j = 0; j < args[i].length; j++) { - unexpVar += Math.pow(args[i][j] - sampSampMean, 2); - } + + function toposortTermIndex (onto) { + // Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558–562, doi:10.1145/368996.369025 + // https://en.wikipedia.org/wiki/Topological_sorting + var S = [], L = [] + var nParents = [], edges = 0 + for (var c = 0; c < onto.terms(); ++c) { + nParents[c] = onto.parents[c].length + edges += nParents[c] + if (nParents[c] == 0) + S.push (c) + } + while (S.length > 0) { + var n = S.shift() + L.push (n) + onto.children[n].forEach (function(m) { + --edges + if (--nParents[m] == 0) + S.push (m) + }) + } + if (edges > 0) + return undefined + + return L } - unexpVar /= (sample.length - args.length); - return expVar / unexpVar; - }, - // 2 different paramter setups - // (array1, array2, array3, ...) - // (anovafscore, df1, df2) - anovaftest: function anovaftest() { - var args = slice.call(arguments), - df1, df2, n, i; - if (isNumber(args[0])) { - return 1 - jStat.centralF.cdf(args[0], args[1], args[2]); + function isCyclic() { + var L = toposortTermIndex(this) + return typeof(L) === 'undefined' } - anovafscore = jStat.anovafscore(args); - df1 = args.length - 1; - n = 0; - for (var i = 0; i < args.length; i++) { - n = n + args[i].length; + + function toposortTermIndexOrDie (onto) { + var L = toposortTermIndex(onto) + if (typeof(L) === 'undefined') + throw new Error ("Ontology graph is not a DAG") + + return L } - df2 = n - df1 - 1; - return 1 - jStat.centralF.cdf(anovafscore, df1, df2); - }, - ftest: function ftest(fscore, df1, df2) { - return 1 - jStat.centralF.cdf(fscore, df1, df2); - } -}); + function toposort() { + var onto = this -jStat.extend(jStat.fn, { - anovafscore: function anovafscore() { - return jStat.anovafscore(this.toArray()); - }, + if (onto.isToposorted()) + return onto + + var L = toposortTermIndexOrDie (onto) - anovaftes: function anovaftes() { - var n = 0; - var i; - for (var i = 0; i < this.length; i++) { - n = n + this[i].length; + var json = onto.toJSON() + var toposortedJson = { termParents: util.permuteList (json.termParents, L) } + if (json.termInfo) + toposortedJson.termInfo = util.permuteList (json.termInfo, L) + + return new Ontology (toposortedJson) } - return jStat.ftest(this.anovafscore(), this.length - 1, n - this.length); - } -}); -// Error Bounds -jStat.extend({ - // 2 different parameter setups - // (value, alpha, sd, n) - // (value, alpha, array) - normalci: function normalci() { - var args = slice.call(arguments), - ans = new Array(2), - change; - if (args.length === 4) { - change = Math.abs(jStat.normal.inv(args[1] / 2, 0, 1) * - args[2] / Math.sqrt(args[3])); - } else { - change = Math.abs(jStat.normal.inv(args[1] / 2, 0, 1) * - jStat.stdev(args[2]) / Math.sqrt(args[2].length)); + function isToposorted() { + for (var i = 0; i < this.terms(); ++i) + if (this.parents[i].some (function(p) { return p >= i })) + return false; + return true; } - ans[0] = args[0] - change; - ans[1] = args[0] + change; - return ans; - }, - // 2 different parameter setups - // (value, alpha, sd, n) - // (value, alpha, array) - tci: function tci() { - var args = slice.call(arguments), - ans = new Array(2), - change; - if (args.length === 4) { - change = Math.abs(jStat.studentt.inv(args[1] / 2, args[3] - 1) * - args[2] / Math.sqrt(args[3])); - } else { - change = Math.abs(jStat.studentt.inv(args[1] / 2, args[2].length - 1) * - jStat.stdev(args[2], true) / Math.sqrt(args[2].length)); + function toposortTermOrder() { + var onto = this + var L = toposortTermIndexOrDie (onto) + var order = onto.termName.map (function() { return null }) + L.forEach (function (term, index) { order[term] = index }) + return order } - ans[0] = args[0] - change; - ans[1] = args[0] + change; - return ans; - }, - - significant: function significant(pvalue, alpha) { - return pvalue < alpha; - } -}); -jStat.extend(jStat.fn, { - normalci: function normalci(value, alpha) { - return jStat.normalci(value, alpha, this.toArray()); - }, + function buildChildren (onto) { + onto.children = onto.parents.map (function() { return [] }) + for (var c = 0; c < onto.terms(); ++c) + onto.parents[c].forEach (function(p) { + onto.children[p].push (c) + }) + } - tci: function tci(value, alpha) { - return jStat.tci(value, alpha, this.toArray()); - } -}); + function equals (onto) { + return JSON.stringify (this.toJSON({'compress':true})) == JSON.stringify (onto.toJSON({'compress':true})); + } -// internal method for calculating the z-score for a difference of proportions test -function differenceOfProportions(p1, n1, p2, n2) { - if (p1 > 1 || p2 > 1 || p1 <= 0 || p2 <= 0) { - throw new Error("Proportions should be greater than 0 and less than 1") - } - var pooled = (p1 * n1 + p2 * n2) / (n1 + n2); - var se = Math.sqrt(pooled * (1 - pooled) * ((1/n1) + (1/n2))); - return (p1 - p2) / se; -} + function transitiveClosure() { + var onto = this + if (!('_closure' in onto)) { + var clos = [] + var L = toposortTermIndexOrDie (onto) + L.forEach (function(n) { + var closIndex = {} + onto.parents[n].forEach (function(p) { + clos[p].forEach (function(c) { + closIndex[c] = 1 + }) + }) + closIndex[n] = 1 + clos[n] = Object.keys(closIndex) + .sort (util.numCmp) + }) + onto._closure = clos + } + return onto._closure + } -// Difference of Proportions -jStat.extend(jStat.fn, { - oneSidedDifferenceOfProportions: function oneSidedDifferenceOfProportions(p1, n1, p2, n2) { - var z = differenceOfProportions(p1, n1, p2, n2); - return jStat.ztest(z, 1); - }, + function getTermInfo (name) { + var onto = this + if (name in onto.termIndex && onto.termInfo) + return onto.termInfo[onto.termIndex[name]] + return undefined + } - twoSidedDifferenceOfProportions: function twoSidedDifferenceOfProportions(p1, n1, p2, n2) { - var z = differenceOfProportions(p1, n1, p2, n2); - return jStat.ztest(z, 2); - } -}); + function subgraphRootedAt (rootTermList) { + var onto = this + var inSubgraph = {} + var inSubFunc = util.objPredicate(inSubgraph) + rootTermList.forEach (function (tn) { + if (tn in onto.termIndex) + inSubgraph[onto.termIndex[tn]] = true + }) + onto.toposortTermIndex().forEach (function (ti) { + var parents = onto.parents[ti] + inSubgraph[ti] = inSubgraph[ti] || (parents.length ? parents.every(inSubFunc) : false) + }) + return onto.subgraph (inSubFunc) + } -}(this.jStat, Math)); -this.jStat.models=(function(){ + function subgraphWithAncestors (termList) { + var onto = this + var inSubgraph = {} + var closure = onto.transitiveClosure() + termList.forEach (function (tn) { + if (tn in onto.termIndex) + closure[onto.termIndex[tn]].forEach (function(c) { + inSubgraph[c] = true + }) + }) + return onto.subgraph (util.objPredicate (inSubgraph)) + } - function sub_regress(endog, exog) { - return ols(endog, exog); - } + function subgraph (termIndexPredicate) { + var onto = this + var termParents = [], + termInfo = onto.termInfo ? [] : undefined + onto.termName.forEach (function (tn, ti) { + if (termIndexPredicate(ti)) { + termParents.push ([tn].concat (onto.parents[ti].filter(termIndexPredicate).map (onto.getTermName.bind(onto)))) + if (termInfo) + termInfo.push (onto.termInfo[ti]) + } + }) + + return new Ontology ({ termParents: termParents, termInfo: termInfo }) + } + + function Ontology (conf) { + var onto = this + extend (onto, + { 'termName': [], // this is actually the term ID. ahem + 'termIndex': {}, // mapping from term ID to term index + 'termInfo': null, // the array of term names (in the OBO sense) goes here, if available + 'doNotAnnotate': {}, + 'parents': [], + 'children': [], + 'terms': function() { return this.termName.length }, + 'toJSON': toJSON, + 'isCyclic': isCyclic, + 'isToposorted': isToposorted, + 'toposort': toposort, + 'toposortTermOrder': toposortTermOrder, + 'toposortTermIndex': function() { return toposortTermIndexOrDie(this) }, + 'equals': equals, + 'transitiveClosure': transitiveClosure, + 'getTermInfo': getTermInfo, + 'getTermName': function(ti) { return this.termName[ti] }, + 'subgraphRootedAt': subgraphRootedAt, + 'subgraphWithAncestors': subgraphWithAncestors, + 'subgraph': subgraph + }) - function sub_regress(exog) { - var var_count = exog[0].length; - var modelList = jStat.arange(var_count).map(function(endog_index) { - var exog_index = - jStat.arange(var_count).filter(function(i){return i!==endog_index}); - return ols(jStat.col(exog, endog_index).map(function(x){ return x[0] }), - jStat.col(exog, exog_index)) - }); - return modelList; - } - - // do OLS model regress - // exog have include const columns ,it will not generate it .In fact, exog is - // "design matrix" look at - //https://en.wikipedia.org/wiki/Design_matrix - function ols(endog, exog) { - var nobs = endog.length; - var df_model = exog[0].length - 1; - var df_resid = nobs-df_model - 1; - var coef = jStat.lstsq(exog, endog); - var predict = - jStat.multiply(exog, coef.map(function(x) { return [x] })) - .map(function(p) { return p[0] }); - var resid = jStat.subtract(endog, predict); - var ybar = jStat.mean(endog); - // constant cause problem - // var SST = jStat.sum(endog.map(function(y) { - // return Math.pow(y-ybar,2); - // })); - var SSE = jStat.sum(predict.map(function(f) { - return Math.pow(f - ybar, 2); - })); - var SSR = jStat.sum(endog.map(function(y, i) { - return Math.pow(y - predict[i], 2); - })); - var SST = SSE + SSR; - var R2 = (SSE / SST); - return { - exog:exog, - endog:endog, - nobs:nobs, - df_model:df_model, - df_resid:df_resid, - coef:coef, - predict:predict, - resid:resid, - ybar:ybar, - SST:SST, - SSE:SSE, - SSR:SSR, - R2:R2 - }; - } + if (Object.prototype.toString.call(conf) === '[object Array]') + conf = { 'termParents': conf } + + if ('termParents' in conf) { + var extTermParents = [] + conf.termParents.forEach (function (tp) { + extTermParents.push (tp) + var term = tp[0] + onto.termIndex[term] = onto.terms() + onto.termName.push (term) + }) + conf.termParents.forEach (function (tp) { + for (var n = 1; n < tp.length; ++n) { + if (typeof(tp[n]) === 'string' && !(tp[n] in onto.termIndex)) { + onto.termIndex[tp[n]] = onto.terms() + onto.termName.push (tp[n]) + extTermParents.push ([tp[n]]) + } + } + }) + + extTermParents.forEach (function (tp,term) { + onto.parents[term] = tp.slice([1]) + .map (function(n) { + return typeof(n) === 'number' ? n : onto.termIndex[n] + }) + }) + } else + throw new Error ("Can't parse Ontology config") - // H0: b_I=0 - // H1: b_I!=0 - function t_test(model) { - var subModelList = sub_regress(model.exog); - //var sigmaHat=jStat.stdev(model.resid); - var sigmaHat = Math.sqrt(model.SSR / (model.df_resid)); - var seBetaHat = subModelList.map(function(mod) { - var SST = mod.SST; - var R2 = mod.R2; - return sigmaHat / Math.sqrt(SST * (1 - R2)); - }); - var tStatistic = model.coef.map(function(coef, i) { - return (coef - 0) / seBetaHat[i]; - }); - var pValue = tStatistic.map(function(t) { - var leftppf = jStat.studentt.cdf(t, model.df_resid); - return (leftppf > 0.5 ? 1 - leftppf : leftppf) * 2; - }); - var c = jStat.studentt.inv(0.975, model.df_resid); - var interval95 = model.coef.map(function(coef, i) { - var d = c * seBetaHat[i]; - return [coef - d, coef + d]; - }) - return { - se: seBetaHat, - t: tStatistic, - p: pValue, - sigmaHat: sigmaHat, - interval95: interval95 - }; - } + if ('termInfo' in conf) + onto.termInfo = conf.termInfo - function F_test(model) { - var F_statistic = - (model.R2 / model.df_model) / ((1 - model.R2) / model.df_resid); - var fcdf = function(x, n1, n2) { - return jStat.beta.cdf(x / (n2 / n1 + x), n1 / 2, n2 / 2) + if ('doNotAnnotate' in conf) + conf.doNotAnnotate.forEach (function (tn) { + if (tn in onto.termIndex) + onto.doNotAnnotate[onto.termIndex[tn]] = true + }) + + buildChildren (onto) } - var pvalue = 1 - fcdf(F_statistic, model.df_model, model.df_resid); - return { F_statistic: F_statistic, pvalue: pvalue }; - } - - function ols_wrap(endog, exog) { - var model = ols(endog,exog); - var ttest = t_test(model); - var ftest = F_test(model); - var adjust_R2 = - 1 - (1 - model.rsquared) * ((model.nobs - 1) / (model.df_resid)); - model.t = ttest; - model.f = ftest; - model.adjust_R2 = adjust_R2; - return model; - } - return { ols: ols_wrap }; -})(); + module.exports = Ontology +}) () -},{}],9:[function(require,module,exports){ -arguments[4][8][0].apply(exports,arguments) -},{"dup":8}],10:[function(require,module,exports){ -(function (root, factory) { - 'use strict'; +},{"./util":9,"assert":11}],8:[function(require,module,exports){ +(function() { + var assert = require('assert'), + BernoulliParamSet = require('./bernoulli').BernoulliParamSet, + util = require('./util'), + extend = util.extend - if (typeof exports === 'object') { - module.exports = factory(); - } else if (typeof define === 'function' && define.amd) { - define(factory); - } else { - root.MersenneTwister = factory(); - } -}(this, function () { - /** - * A standalone, pure JavaScript implementation of the Mersenne Twister pseudo random number generator. Compatible - * with Node.js, requirejs and browser environments. Packages are available for npm, Jam and Bower. - * - * @module MersenneTwister - * @author Raphael Pigulla - * @license See the attached LICENSE file. - * @version 0.2.3 - */ + function Parameterization (conf) { + var parameterization = this - /* - * Most comments were stripped from the source. If needed you can still find them in the original C code: - * http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.c - * - * The original port to JavaScript, on which this file is based, was done by Sean McCullough. It can be found at: - * https://gist.github.com/banksean/300494 - */ - 'use strict'; + conf = extend ({ termPrior: function(term) { return 't' }, + geneFalsePos: function(gene) { return 'fp' }, + geneFalseNeg: function(gene) { return 'fn' }, + }, + conf) - var MAX_INT = 4294967296.0, - N = 624, - M = 397, - UPPER_MASK = 0x80000000, - LOWER_MASK = 0x7fffffff, - MATRIX_A = 0x9908b0df; + var assocs = conf.assocs + var termName = assocs.ontology.termName + var geneName = assocs.geneName - /** - * Instantiates a new Mersenne Twister. - * - * @constructor - * @alias module:MersenneTwister - * @since 0.1.0 - * @param {number=} seed The initial seed value. - */ - var MersenneTwister = function (seed) { - if (typeof seed === 'undefined') { - seed = new Date().getTime(); + var params = {} + function init(f) { + return function(x) { + var name = f(x) + params[name] = 1 + return name + } } + + parameterization.names = { + termPrior: termName.map (init (conf.termPrior)), + geneFalsePos: geneName.map (init (conf.geneFalsePos)), + geneFalseNeg: geneName.map (init (conf.geneFalseNeg)) + } + + parameterization.paramSet = new BernoulliParamSet (params) + } - this.mt = new Array(N); - this.mti = N + 1; - - this.seed(seed); - }; + module.exports = Parameterization +}) () - /** - * Initializes the state vector by using one unsigned 32-bit integer "seed", which may be zero. - * - * @since 0.1.0 - * @param {number} seed The seed value. - */ - MersenneTwister.prototype.seed = function (seed) { - var s; +},{"./bernoulli":4,"./util":9,"assert":11}],9:[function(require,module,exports){ +(function() { + var extend = require('util')._extend, + assert = require('assert'), + jStat = require('jStat').jStat - this.mt[0] = seed >>> 0; + function numCmp (a, b) { return a-b } - for (this.mti = 1; this.mti < N; this.mti++) { - s = this.mt[this.mti - 1] ^ (this.mt[this.mti - 1] >>> 30); - this.mt[this.mti] = - (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253) + this.mti; - this.mt[this.mti] >>>= 0; + function reverseCmp (comparisonFunc) { + return function(a,b) { + return comparisonFunc(b,a) } - }; + } - /** - * Initializes the state vector by using an array key[] of unsigned 32-bit integers of the specified length. If - * length is smaller than 624, then each array of 32-bit integers gives distinct initial state vector. This is - * useful if you want a larger seed space than 32-bit word. - * - * @since 0.1.0 - * @param {array} vector The seed vector. - */ - MersenneTwister.prototype.seedArray = function (vector) { - var i = 1, - j = 0, - k = N > vector.length ? N : vector.length, - s; + function sortAscending (list) { + return list.sort (numCmp) + } - this.seed(19650218); + function listToCounts (list) { + var c = {} + list.forEach (function(x) { + c[x] = (c[x] || 0) + 1 + }) + return c + } + + function removeDups (list) { + return Object.keys (listToCounts (list)) + } - for (; k > 0; k--) { - s = this.mt[i-1] ^ (this.mt[i-1] >>> 30); + function parseDecInt (x) { + return parseInt (x) + } - this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525))) + - vector[j] + j; - this.mt[i] >>>= 0; - i++; - j++; - if (i >= N) { - this.mt[0] = this.mt[N - 1]; - i = 1; - } - if (j >= vector.length) { - j = 0; - } + function objPredicate (obj) { + return function(x) { + return obj[x] ? true : false } + } - for (k = N - 1; k; k--) { - s = this.mt[i - 1] ^ (this.mt[i - 1] >>> 30); - this.mt[i] = - (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1566083941) << 16) + (s & 0x0000ffff) * 1566083941)) - i; - this.mt[i] >>>= 0; - i++; - if (i >= N) { - this.mt[0] = this.mt[N - 1]; - i = 1; - } - } + function negate (predicateFunc) { + return function () { + return !predicateFunc.apply(this, arguments) + } + } - this.mt[0] = 0x80000000; - }; + function sumList (list) { + return list.reduce (function(tot,x) { return tot+x }, 0) + } + + function randomElement (list, generator) { + return list.length > 0 ? list [Math.floor (generator.random() * list.length)] : undefined + } - /** - * Generates a random unsigned 32-bit integer. - * - * @since 0.1.0 - * @returns {number} - */ - MersenneTwister.prototype.int = function () { - var y, - kk, - mag01 = new Array(0, MATRIX_A); + function randomIndex (distrib, generator) { + var sum = sumList (distrib) + var rnd = generator.random() * sum + for (var idx = 0; idx < distrib.length; ++idx) + if ((rnd -= distrib[idx]) <= 0) + return idx + return undefined + } - if (this.mti >= N) { - if (this.mti === N + 1) { - this.seed(5489); - } + function randomKey (obj, generator) { + var keys = Object.keys (obj) + var distrib = keys.map (function(k) { return obj[k] }) + return keys [randomIndex (distrib, generator)] + } + + function iota(n) { + var list = [] + for (var i = 0; i < n; ++i) + list.push(i) + return list + } + + function sortKeys (obj, sortFunc, keys) { + sortFunc = sortFunc || numCmp + return (keys || Object.keys(obj)).sort (function(a,b) { + return sortFunc (obj[a], obj[b]) + }) + } - for (kk = 0; kk < N - M; kk++) { - y = (this.mt[kk] & UPPER_MASK) | (this.mt[kk + 1] & LOWER_MASK); - this.mt[kk] = this.mt[kk + M] ^ (y >>> 1) ^ mag01[y & 1]; - } + function sortIndices (order, indexList, sortFunc) { + sortFunc = sortFunc || numCmp + return (indexList || iota(order.length)).sort (function(a,b) { + return sortFunc (order[a], order[b]) + }) + } - for (; kk < N - 1; kk++) { - y = (this.mt[kk] & UPPER_MASK) | (this.mt[kk + 1] & LOWER_MASK); - this.mt[kk] = this.mt[kk + (M - N)] ^ (y >>> 1) ^ mag01[y & 1]; - } + function permuteList (list, order) { + return order.map (function(idx) { return list[idx] }) + } - y = (this.mt[N - 1] & UPPER_MASK) | (this.mt[0] & LOWER_MASK); - this.mt[N - 1] = this.mt[M - 1] ^ (y >>> 1) ^ mag01[y & 1]; - this.mti = 0; - } + function keyValListToObj (keyValList) { + var obj = {} + keyValList.forEach (function (keyVal) { + obj[keyVal[0]] = keyVal[1] + }) + return obj + } - y = this.mt[this.mti++]; + function values (obj) { + return Object.keys(obj).map (function(k) { return obj[k] }) + } - y ^= (y >>> 11); - y ^= (y << 7) & 0x9d2c5680; - y ^= (y << 15) & 0xefc60000; - y ^= (y >>> 18); + function commonKeys (obj1, obj2) { + return Object.keys(obj1).filter (function(k) { return obj2.hasOwnProperty(k) }) + } - return y >>> 0; - }; + function commonElements (list1, list2) { + return commonKeys (listToCounts(list1), listToCounts(list2)) + } - /** - * Generates a random unsigned 31-bit integer. - * - * @since 0.1.0 - * @returns {number} - */ - MersenneTwister.prototype.int31 = function () { - return this.int() >>> 1; - }; + function logBinomialCoefficient (n, k) { + return jStat.gammaln(n+1) - jStat.gammaln(k+1) - jStat.gammaln(n-k+1) + } + + function logBetaBinomial (alpha, beta, n, k) { + return logBinomialCoefficient(n,k) + logBetaBernoulli(alpha,beta,k,n-k) + } + + function logBetaBernoulli (alpha, beta, succ, fail) { + return jStat.betaln(alpha+succ,beta+fail) - jStat.betaln(alpha,beta) + } - /** - * Generates a random real in the interval [0;1] with 32-bit resolution. - * - * @since 0.1.0 - * @returns {number} - */ - MersenneTwister.prototype.real = function () { - return this.int() * (1.0 / (MAX_INT - 1)); - }; + function autocorrelation (list, points) { + points = points || iota(list.length-1) + var mean = jStat.mean(list), variance = jStat.variance(list) + var list_minus_mean = list.map (function(x) { return x - mean }) + var R = {} + points.forEach (function(tau) { + var R_tau = [] + for (var i = 0; i + tau < list.length; ++i) + R_tau.push (list_minus_mean[i] * list_minus_mean[i + tau]) + R[tau] = jStat.mean(R_tau) / variance + }) + return R + } - /** - * Generates a random real in the interval ]0;1[ with 32-bit resolution. - * - * @since 0.1.0 - * @returns {number} - */ - MersenneTwister.prototype.realx = function () { - return (this.int() + 0.5) * (1.0 / MAX_INT); - }; + function arraysEqual (a, b) { + if (a === b) return true; + if (a == null || b == null) return false + if (a.length != b.length) return false + for (var i = 0; i < a.length; ++i) + if (a[i] !== b[i]) return false + return true + } - /** - * Generates a random real in the interval [0;1[ with 32-bit resolution. - * - * @since 0.1.0 - * @returns {number} - */ - MersenneTwister.prototype.rnd = function () { - return this.int() * (1.0 / MAX_INT); - }; + function approxEqual (a, b, epsilon) { + epsilon = epsilon || .0001 + if (Math.max (Math.abs(a), Math.abs(b)) > 0) + return Math.abs(a-b) / Math.max (Math.abs(a), Math.abs(b)) < epsilon + else + return Math.abs(a-b) < epsilon + } + + function assertApproxEqual (a, b, epsilon, message) { + assert (approxEqual(a,b,epsilon), message || ("Difference between a ("+a+") and b ("+b+") is too large")) + } + + function plural (count, singular, plural) { + plural = plural || (singular + 's') + return count + ' ' + (count == 1 ? singular : plural) + } + + function toHHMMSS (milliseconds) { + var sec_num = Math.floor (milliseconds / 1000) + var hours = Math.floor (sec_num / 3600) + var minutes = Math.floor ((sec_num - (hours * 3600)) / 60) + var seconds = sec_num - (hours * 3600) - (minutes * 60) - /** - * Generates a random real in the interval [0;1[ with 32-bit resolution. - * - * Same as .rnd() method - for consistency with Math.random() interface. - * - * @since 0.2.0 - * @returns {number} - */ - MersenneTwister.prototype.random = MersenneTwister.prototype.rnd; + if (hours < 10) + hours = "0" + hours + if (minutes < 10) + minutes = "0" + minutes + if (seconds < 10) + seconds = "0" + seconds - /** - * Generates a random real in the interval [0;1[ with 53-bit resolution. - * - * @since 0.1.0 - * @returns {number} - */ - MersenneTwister.prototype.rndHiRes = function () { - var a = this.int() >>> 5, - b = this.int() >>> 6; + return hours+':'+minutes+':'+seconds + } - return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0); - }; + function progressLogger (pastTenseVerb, pluralNoun) { + var startTime = Date.now(), lastTime = startTime, delay = 1000 + return function (stepsCompleted, totalSteps) { + var nowTime = Date.now() + if (nowTime - lastTime > delay) { + lastTime = nowTime + delay = Math.min (30000, delay*2) + var progress = stepsCompleted / totalSteps + console.warn (pastTenseVerb + " " + stepsCompleted + "/" + totalSteps + " " + pluralNoun + " (" + Math.round(100*progress) + "%), estimated time left " + toHHMMSS ((1/progress - 1) * (nowTime - startTime))) + } + } + } - var instance = new MersenneTwister(); + function logRandomNumbers(generator,nMax) { + var rnd = generator.int, nRnd = 0 + generator.int = function() { + var r = rnd.apply (this, arguments) + if (typeof(nMax) === 'undefined' || nRnd < nMax) + console.warn ("Random number #" + (++nRnd) + ": " + r) + return r + } + } - /** - * A static version of [rnd]{@link module:MersenneTwister#rnd} on a randomly seeded instance. - * - * @static - * @function random - * @memberof module:MersenneTwister - * @returns {number} - */ - MersenneTwister.random = function () { - return instance.rnd(); - }; + function HSVtoRGB(h, s, v) { + var r, g, b, i, f, p, q, t + i = Math.floor(h * 6) + f = h * 6 - i + p = v * (1 - s) + q = v * (1 - f * s) + t = v * (1 - (1 - f) * s) + switch (i % 6) { + case 0: r = v, g = t, b = p; break + case 1: r = q, g = v, b = p; break + case 2: r = p, g = v, b = t; break + case 3: r = p, g = q, b = v; break + case 4: r = t, g = p, b = v; break + case 5: r = v, g = p, b = q; break + } + return { + r: Math.round(r * 255), + g: Math.round(g * 255), + b: Math.round(b * 255) + } + } - return MersenneTwister; -})); + module.exports.numCmp = numCmp + module.exports.reverseCmp = reverseCmp + module.exports.sortAscending = sortAscending + module.exports.listToCounts = listToCounts + module.exports.removeDups = removeDups + module.exports.parseDecInt = parseDecInt + module.exports.objPredicate = objPredicate + module.exports.negate = negate + module.exports.sumList = sumList + module.exports.randomElement = randomElement + module.exports.randomIndex = randomIndex + module.exports.randomKey = randomKey + module.exports.iota = iota + module.exports.sortKeys = sortKeys + module.exports.sortIndices = sortIndices + module.exports.permuteList = permuteList + module.exports.keyValListToObj = keyValListToObj + module.exports.values = values + module.exports.commonKeys = commonKeys + module.exports.commonElements = commonElements + module.exports.logBinomialCoefficient = logBinomialCoefficient + module.exports.logBetaBinomial = logBetaBinomial + module.exports.logBetaBernoulli = logBetaBernoulli + module.exports.autocorrelation = autocorrelation + module.exports.arraysEqual = arraysEqual + module.exports.approxEqual = approxEqual + module.exports.assertApproxEqual = assertApproxEqual + module.exports.toHHMMSS = toHHMMSS + module.exports.plural = plural + module.exports.progressLogger = progressLogger + module.exports.logRandomNumbers = logRandomNumbers + module.exports.HSVtoRGB = HSVtoRGB + module.exports.extend = extend +}) () -},{}],11:[function(require,module,exports){ +},{"assert":11,"jStat":1,"util":15}],10:[function(require,module,exports){ (function (global){ (function() { var MersenneTwister = require('mersennetwister'), @@ -7201,11 +6699,13 @@ arguments[4][8][0].apply(exports,arguments) .done (function (datasetsJson) { wtf.datasets = datasetsJson wtf.log ("Loaded " + wtf.datasets.organisms.length + " organisms") - wtf.datasets.organisms.forEach (function (orgJson) { - $('#wtf-organism-list').append - ($('
  • ' + orgJson.name + '
  • ') - .click (organismSelector(wtf,orgJson))) + var organismMenu = wtf.datasets.organisms.map (function (orgJson) { + return $('
  • ' + orgJson.name + '
  • ') + .click (organismSelector(wtf,orgJson)) }) + $('#wtf-organism-list').append (organismMenu) + if (organismMenu.length == 1) + organismMenu[0].click() }).fail (function() { wtf.log("Problem loading " + wtf.datasetsURL) @@ -7216,7 +6716,7 @@ arguments[4][8][0].apply(exports,arguments) })() }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../lib/assocs":1,"../lib/mcmc":3,"../lib/model":4,"../lib/ontology":5,"../lib/util":7,"assert":12,"mersennetwister":10}],12:[function(require,module,exports){ +},{"../lib/assocs":3,"../lib/mcmc":5,"../lib/model":6,"../lib/ontology":7,"../lib/util":9,"assert":11,"mersennetwister":2}],11:[function(require,module,exports){ // http://wiki.commonjs.org/wiki/Unit_Testing/1.0 // // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! @@ -7577,7 +7077,7 @@ var objectKeys = Object.keys || function (obj) { return keys; }; -},{"util/":16}],13:[function(require,module,exports){ +},{"util/":15}],12:[function(require,module,exports){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { @@ -7602,7 +7102,7 @@ if (typeof Object.create === 'function') { } } -},{}],14:[function(require,module,exports){ +},{}],13:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; @@ -7695,14 +7195,14 @@ process.chdir = function (dir) { }; process.umask = function() { return 0; }; -},{}],15:[function(require,module,exports){ +},{}],14:[function(require,module,exports){ module.exports = function isBuffer(arg) { return arg && typeof arg === 'object' && typeof arg.copy === 'function' && typeof arg.fill === 'function' && typeof arg.readUInt8 === 'function'; } -},{}],16:[function(require,module,exports){ +},{}],15:[function(require,module,exports){ (function (process,global){ // Copyright Joyent, Inc. and other Node contributors. // @@ -8292,4 +7792,4 @@ function hasOwnProperty(obj, prop) { } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./support/isBuffer":15,"_process":14,"inherits":13}]},{},[11]); +},{"./support/isBuffer":14,"_process":13,"inherits":12}]},{},[10]); diff --git a/web/client.js b/web/client.js index 60ed302..c823952 100644 --- a/web/client.js +++ b/web/client.js @@ -1032,11 +1032,13 @@ .done (function (datasetsJson) { wtf.datasets = datasetsJson wtf.log ("Loaded " + wtf.datasets.organisms.length + " organisms") - wtf.datasets.organisms.forEach (function (orgJson) { - $('#wtf-organism-list').append - ($('
  • ' + orgJson.name + '
  • ') - .click (organismSelector(wtf,orgJson))) + var organismMenu = wtf.datasets.organisms.map (function (orgJson) { + return $('
  • ' + orgJson.name + '
  • ') + .click (organismSelector(wtf,orgJson)) }) + $('#wtf-organism-list').append (organismMenu) + if (organismMenu.length == 1) + organismMenu[0].click() }).fail (function() { wtf.log("Problem loading " + wtf.datasetsURL) diff --git a/web/datasets.json b/web/datasets.json index 6308666..ce2c388 100644 --- a/web/datasets.json +++ b/web/datasets.json @@ -1,73 +1,73 @@ { - "organisms": - [ + "organisms": + [ + { + "name": "Saccharomyces cerevisiae", + "ontologies": + [ { - "name": "Saccharomyces cerevisiae", - "ontologies": - [ - { - "name": "Gene Ontology", - "ontology": "../test/data/yeast/go-basic.json", - "assocs": "../test/data/yeast/gene_association.sgd.json", - "examples": - [ - { - "name": "Yeast mating genes", - "genes": ["STE2","STE3","STE5","GPA1","SST2","STE11","STE50","STE20","STE4","STE18","FUS3","KSS1","PTP2","MSG5","DIG1","DIG2","STE12"] - }, - { - "name": "Yeast ascospore (from geneontology.org/experimental/enrichment-genesets)", - "genes": ["ADE16","AMA1","AQY1","BMH1","BMH2","BUD30","CPR1","EMI1","EMI2","EMI5","ERV14","FMP45","GLC7","GPA2","GPB2","IML3","IRC14","IRC19","LSG1","MCK1","MRE11","OSW1","PAU8","RAS2","RIM101","RIM11","RIM21","RIM9","SEC14","SET1","SET2","SGA1","SHC1","SHP1","SMC3","SPO16","SPO73","SPO74","SPO75","SPO77","SPR1","SPR3","SPR6","SPS1","SPS19","SPS2","SPS4","SPT3","SSO1","SSP2","SUR7","UBI4","VID28","YLR269C","YNL194C","YNL296W","YOR338W","YPL205C","IML3","CPR1","GLC7","AMA1","RIM101","SPO16","SMC3","SET2","MRE11","MCK1","BIG1","CWH41","DCW1","DFG5","FLC1","FLC2","LRG1","RIM101","RIM21","ROT1","ROT2","RPI1","SLT2","UTH1","AZF1","CBK1","MSG5","RHO1","RNT1","ROM2","SSD1"] - }, - { - "name": "Yeast genes involved in phosphate and nitrogen utilization (from geneontology.org/experimental/enrichment-genesets)", - "genes": ["AGP1","BAP2","BAP3","GNP1","TAT1","TAT2","GAP1","HIP1","MMP1","SAM3","CAN1","ALP1","LYP1","PUT4","DIP5","SSY1","AGP2","AGP3","MUP1","MUP3","UGA4","TPO5","HNM1","Bio5","MEP1","MEP2","MEP3","DAL5","yct1","TNA1","VHT1","FEN2","SEO1","fur4","dal4","fui1","thi7","thi72","net1","fcy2","tpn1","dur3","ptr2","opt1","opt2","aqr1","tpo1","BAT2","CAR1","DAL1","DAL2","DAL3","DAL7","DCG1","GDH2","GDH3","GLN1","PUT1","PUT2","UGA1","DAL80","GAT1","GZF3","PHO84","pho80","pho85","pho4","pho2","spl2","pho87","pho90","adk1","vip1","ado1","kcs1","ipk1","arg82","plc1","pho12","ppn1","vtc2","vtc1","vtc3","GPD1","plb1","PLB3","INM1","GIT1","KCS1","DDP1","TAX4","HOR2","GDE1","PHM8","VIP1","SUR1"] - }, - { - "name": "Yeast genes involved in pheromone response (from geneontology.org/experimental/enrichment-genesets)", - "genes": ["CLA4","FPK1","KIN82","PRM1","YPP1","AXL1","KEX2","RAM2","RCE1","STE13","STE14","STE23","STE24","FAR1","GRR1","OPY2","STE6","CLN2","FAR10","FAR11","FAR3","FAR7","FAR8","POG1","VPS64","AKR1","DSE1","ADF1","AFR1","CDC42","FAR1","FUS3","FYV5","GET3","GPA1","KSS1","MDG1","MF(ALPHA)1","MF(ALPHA)2","MFA1","MFA2","PTC1","RGA1","RGA2","SCP160","STE11","STE18","STE2","STE20","STE3","STE4","STE5","STE50","BAR1","CMP2","CNA1","CNB1","CSI1","CSN9","GPA1","MSG5","RRI1","RRI2","SST2","YJR084W","STE7","DIG1","DIG2","HOG1","ITC1","KAR4","PRR1","PRR2","STE12","CRZ1","KAR4","PCC1","PLP1","PLP2","STE12"] - }, - { - "name": "Yeast genes involved in sulfate assimilation and nitrogen utilization (from geneontology.org/experimental/enrichment-genesets)", - "genes": ["MET10","MET1","MET14","MET22","MET3","MET5","MET8","TRX1","SUL1","FZF1","SUL2","OAC1","ATF1","ATF2","ADY2","ATO2","ATO3","MEP1","MEP2","MEP3","UGA1","UGA3","YGR125W","YPR003C","YIL165C","MKS1","NPR1","RSP5","URE2","VID30","AGC1","CPS1","GDH2","DAL80","GZF3","PPH3","GAT1","RTG2","UME6"] - }, - { - "name": "Yeast genes involved in fermentative growth (from Bauer et al, NAR 2010)", - "genes": ["YAL062W","YAL061W","YAL012W","YAR035W","YBL099W","YBL081W","YBL059W","YBL043W","YBL039W-B","YBL015W","YBR039W","YBR047W","YBR054W","YBR072W","YBR084W","YBR085W","YBR104W","YBR118W","YBR149W","YBR187W","YBR230W-A","YBR244W","YBR285W","YBR297W","YCL048W-A","YCL042W","YCL040W","YCR048W","YCR083W","YDL204W","YDL181W","YDL136W","YDL130W-A","YDL085W","YDL021W","YDL004W","YDR009W","YDR031W","YDR034W-B","YDR119W-A","YDR178W","YDR210W","YDR216W","YDR222W","YDR223W","YDR262W","YDR321W","YDR377W","YDR380W","YDR421W","YDR471W","YDR536W","YEL071W","YEL065W","YEL040W","YEL038W","YEL026W","YEL024W","YEL012W","YEL011W","YER011W","YER015W","YER024W","YER049W","YER060W","YER067W","YER069W","YER074W","YER079W","YER096W","YER121W","YER146W","YER170W","YFL030W","YFL014W","YFR049W","YFR055W","YGL263W","YGL225W","YGL209W","YGL208W","YGL205W","YGL191W","YGL157W","YGL156W","YGL062W","YGR032W","YGR088W","YGR097W","YGR110W","YGR174W-A","YGR208W","YGR230W","YGR243W","YHL028W","YHL024W","YHR001W-A","YHR020W","YHR033W","YHR037W","YHR068W","YHR071W","YHR123W","YHR128W","YHR137W","YHR163W","YHR183W","YHR184W","YHR208W","YIL136W","YIL125W","YIL121W","YIL111W","YIL110W","YIL053W","YIL050W","YIL011W","YJL213W","YJL198W","YJL166W","YJL161W","YJL102W","YJL089W","YJL088W","YJL045W","YJR008W","YJR048W","YJR094W-A","YJR095W","YJR096W","YJR120W","YJR121W","YJR144W","YJR148W","YJR149W","YKL217W","YKL180W","YKL163W","YKL142W","YKL141W","YKL127W","YKL120W","YKL109W","YKL106W","YKL093W","YKL085W","YKR016W","YKR057W","YKR059W","YKR067W","YKR076W","YKR097W","YKR097W","YLL061W","YLR061W","YLR064W","YLR109W","YLR142W","YLR174W","YLR267W","YLR345W","YLR356W","YLR367W","snR44","YLR413W","YLR438W","YLR449W","YML131W","YML042W","YML042W","YML030W","YMR011W","YMR035W","YMR058W","YMR107W","YMR175W","YMR191W","YMR196W","YMR206W","YMR250W","YMR251W","YNL277W-A","YNL277W","YNL237W","YNL202W","YNL162W","YNL160W","YNL117W","YNL112W","YNL100W","YNL093W","YNL065W","YNL052W","YNL036W","YNL015W","YNL009W","YNR014W","YNR034W-A","YOL152W","YOL132W","YOL083W","YOL077W-A","YOL059W","YOL058W","YOL053W","YOL045W","YOL014W","YOR020W-A","YOR040W","YOR065W","YOR084W","YOR108W","YOR136W","YOR147W","YOR173W","YOR289W","YOR328W","YOR359W","YOR374W","YOR382W","YOR384W","YOR385W","YPL271W","YPL262W","YPL245W","YPL230W","YPL226W","YPL222W","YPL200W","YPL198W","snR59","YPL147W","YPL147W","YPL111W","YPL054W","YPL024W","YPL014W","YPR001W","YPR002W","YPR020W","YPR026W","YPR030W","YPR030W","YPR036W-A","YPR043W","YPR080W","YPR160W","YPR160W-A","YPR184W","YPR191W","YAL067C","YAL054C","YAL054C","YAL005C","YBL078C","YBL066C","YBL064C","YBL045C","YBL030C","YBL029C-A","YBR004C","YBR045C","YBR046C","YBR050C","YBR052C","YBR053C","YBR085C-A","YBR092C","YBR093C","YBR105C","YBR116C","YBR117C","YBR132C","YBR222C","YBR230C","YBR238C","YBR255C-A","YBR262C","YBR269C","YBR291C","YBR296C","YCL037C","YCL030C","YCR005C","YCR010C","YCR098C","YDL223C","YDL222C","YDL215C","YDL174C","YDL169C","YDL110C","YDL085C-A","YDL079C","YDL067C","YDL061C","YDL049C","YDL027C","YDR018C","YDR059C","YDR070C","YDR144C","YDR148C","YDR231C","YDR256C","YDR261C","YDR298C","YDR309C","YDR322C-A","YDR326C","YDR345C","YDR357C","YDR375C","YDR379C-A","YDR461C-A","YDR529C","YEL060C","YER055C","YER056C-A","YER065C","YFL018C","YFR011C","YFR017C","YFR032C-A","YFR032C-B","YFR033C","YGL224C","YGL146C","YGL121C","YGL032C","YGR008C","YGR027C","YGR043C","YGR067C","YGR067C","YGR138C","YGR148C","YGR174C","YGR177C","YGR194C","YGR236C","YGR239C","YGR244C","YGR250C","YHL040C","YHL032C","YHR046C","YHR092C","YHR094C","YHR096C","YHR141C","YIL166C","YIL160C","YIL155C","YIL117C","YIL107C","YIL065C","YIL057C","YIL014C-A","YIR017C","YIR035C","YIR038C","YIR039C","YJL212C","YJL153C","YJL137C","YJL116C","YJL103C","YJL079C","YJL066C","YJL048C","YJR019C","YJR025C","YJR073C","YJR077C","YJR080C","YJR124C","YKL187C","YKL187C","YKL148C","YKL128C","YKL026C","YKL016C","YKL008C","YKR009C","YKR009C","YKR046C","YKR049C","YLL042C","YLL041C","YLL039C","YLL024C","YLL020C","YLL019C","YLL018C-A","YLR020C","YLR038C","YLR070C","YLR073C","YLR136C","YLR149C","YLR168C","YLR178C","YLR205C","YLR280C","YLR281C","YLR287C-A","YLR294C","YLR295C","YLR304C","YLR307C-A","YLR312C","YLR327C","YLR346C","YLR377C","YLR392C","YLR395C","YLR437C","YML130C","YML128C","YML120C","YML091C","YML087C","YML081C-A","YML056C","snR54","YML054C","YML018C","YMR041C","YMR056C","YMR062C","YMR081C","YMR110C","YMR118C","YMR174C","YMR221C","YMR242C","YMR256C","YMR280C","YMR303C","YMR319C","YNL305C","YNL274C","YNL270C","YNL259C","YNL200C","YNL195C","YNL194C","YNL173C","YNL134C","YNL125C","YNL069C","YNL037C","YNR001C","YNR002C","YNR013C","YNR050C","YNR064C","YNR074C","YOL147C","YOL136C","YOL126C","YOL105C","YOL080C","YOL052C-A","YOL031C","YOL016C","YOL002C","YOR047C","YOR051C","YOR064C","YOR100C","YOR103C","YOR178C","YOR185C","YOR215C","YOR312C","YOR316C","YOR348C","YOR360C","YOR383C","YPL227C","YPL223C","YPL201C","YPL186C","YPL177C","YPL171C","YPL156C","YPL134C","YPL119C","YPL113C","YPL109C","YPL078C","YPL058C","YPR006C","YPR006C","YPR010C","YPR010C-A","YPR061C","YPR151C","YPR155C","YPR194C"] - } - ] - } - ] + "name": "Gene Ontology", + "ontology": "../test/data/yeast/go-basic.json", + "assocs": "../test/data/yeast/gene_association.sgd.json", + "examples": + [ + { + "name": "Yeast mating genes", + "genes": ["STE2","STE3","STE5","GPA1","SST2","STE11","STE50","STE20","STE4","STE18","FUS3","KSS1","PTP2","MSG5","DIG1","DIG2","STE12"] + }, + { + "name": "Yeast ascospore (from geneontology.org/experimental/enrichment-genesets)", + "genes": ["ADE16","AMA1","AQY1","BMH1","BMH2","BUD30","CPR1","EMI1","EMI2","EMI5","ERV14","FMP45","GLC7","GPA2","GPB2","IML3","IRC14","IRC19","LSG1","MCK1","MRE11","OSW1","PAU8","RAS2","RIM101","RIM11","RIM21","RIM9","SEC14","SET1","SET2","SGA1","SHC1","SHP1","SMC3","SPO16","SPO73","SPO74","SPO75","SPO77","SPR1","SPR3","SPR6","SPS1","SPS19","SPS2","SPS4","SPT3","SSO1","SSP2","SUR7","UBI4","VID28","YLR269C","YNL194C","YNL296W","YOR338W","YPL205C","IML3","CPR1","GLC7","AMA1","RIM101","SPO16","SMC3","SET2","MRE11","MCK1","BIG1","CWH41","DCW1","DFG5","FLC1","FLC2","LRG1","RIM101","RIM21","ROT1","ROT2","RPI1","SLT2","UTH1","AZF1","CBK1","MSG5","RHO1","RNT1","ROM2","SSD1"] + }, + { + "name": "Yeast genes involved in phosphate and nitrogen utilization (from geneontology.org/experimental/enrichment-genesets)", + "genes": ["AGP1","BAP2","BAP3","GNP1","TAT1","TAT2","GAP1","HIP1","MMP1","SAM3","CAN1","ALP1","LYP1","PUT4","DIP5","SSY1","AGP2","AGP3","MUP1","MUP3","UGA4","TPO5","HNM1","Bio5","MEP1","MEP2","MEP3","DAL5","yct1","TNA1","VHT1","FEN2","SEO1","fur4","dal4","fui1","thi7","thi72","net1","fcy2","tpn1","dur3","ptr2","opt1","opt2","aqr1","tpo1","BAT2","CAR1","DAL1","DAL2","DAL3","DAL7","DCG1","GDH2","GDH3","GLN1","PUT1","PUT2","UGA1","DAL80","GAT1","GZF3","PHO84","pho80","pho85","pho4","pho2","spl2","pho87","pho90","adk1","vip1","ado1","kcs1","ipk1","arg82","plc1","pho12","ppn1","vtc2","vtc1","vtc3","GPD1","plb1","PLB3","INM1","GIT1","KCS1","DDP1","TAX4","HOR2","GDE1","PHM8","VIP1","SUR1"] + }, + { + "name": "Yeast genes involved in pheromone response (from geneontology.org/experimental/enrichment-genesets)", + "genes": ["CLA4","FPK1","KIN82","PRM1","YPP1","AXL1","KEX2","RAM2","RCE1","STE13","STE14","STE23","STE24","FAR1","GRR1","OPY2","STE6","CLN2","FAR10","FAR11","FAR3","FAR7","FAR8","POG1","VPS64","AKR1","DSE1","ADF1","AFR1","CDC42","FAR1","FUS3","FYV5","GET3","GPA1","KSS1","MDG1","MF(ALPHA)1","MF(ALPHA)2","MFA1","MFA2","PTC1","RGA1","RGA2","SCP160","STE11","STE18","STE2","STE20","STE3","STE4","STE5","STE50","BAR1","CMP2","CNA1","CNB1","CSI1","CSN9","GPA1","MSG5","RRI1","RRI2","SST2","YJR084W","STE7","DIG1","DIG2","HOG1","ITC1","KAR4","PRR1","PRR2","STE12","CRZ1","KAR4","PCC1","PLP1","PLP2","STE12"] + }, + { + "name": "Yeast genes involved in sulfate assimilation and nitrogen utilization (from geneontology.org/experimental/enrichment-genesets)", + "genes": ["MET10","MET1","MET14","MET22","MET3","MET5","MET8","TRX1","SUL1","FZF1","SUL2","OAC1","ATF1","ATF2","ADY2","ATO2","ATO3","MEP1","MEP2","MEP3","UGA1","UGA3","YGR125W","YPR003C","YIL165C","MKS1","NPR1","RSP5","URE2","VID30","AGC1","CPS1","GDH2","DAL80","GZF3","PPH3","GAT1","RTG2","UME6"] + }, + { + "name": "Yeast genes involved in fermentative growth (from Bauer et al, NAR 2010)", + "genes": ["YAL062W","YAL061W","YAL012W","YAR035W","YBL099W","YBL081W","YBL059W","YBL043W","YBL039W-B","YBL015W","YBR039W","YBR047W","YBR054W","YBR072W","YBR084W","YBR085W","YBR104W","YBR118W","YBR149W","YBR187W","YBR230W-A","YBR244W","YBR285W","YBR297W","YCL048W-A","YCL042W","YCL040W","YCR048W","YCR083W","YDL204W","YDL181W","YDL136W","YDL130W-A","YDL085W","YDL021W","YDL004W","YDR009W","YDR031W","YDR034W-B","YDR119W-A","YDR178W","YDR210W","YDR216W","YDR222W","YDR223W","YDR262W","YDR321W","YDR377W","YDR380W","YDR421W","YDR471W","YDR536W","YEL071W","YEL065W","YEL040W","YEL038W","YEL026W","YEL024W","YEL012W","YEL011W","YER011W","YER015W","YER024W","YER049W","YER060W","YER067W","YER069W","YER074W","YER079W","YER096W","YER121W","YER146W","YER170W","YFL030W","YFL014W","YFR049W","YFR055W","YGL263W","YGL225W","YGL209W","YGL208W","YGL205W","YGL191W","YGL157W","YGL156W","YGL062W","YGR032W","YGR088W","YGR097W","YGR110W","YGR174W-A","YGR208W","YGR230W","YGR243W","YHL028W","YHL024W","YHR001W-A","YHR020W","YHR033W","YHR037W","YHR068W","YHR071W","YHR123W","YHR128W","YHR137W","YHR163W","YHR183W","YHR184W","YHR208W","YIL136W","YIL125W","YIL121W","YIL111W","YIL110W","YIL053W","YIL050W","YIL011W","YJL213W","YJL198W","YJL166W","YJL161W","YJL102W","YJL089W","YJL088W","YJL045W","YJR008W","YJR048W","YJR094W-A","YJR095W","YJR096W","YJR120W","YJR121W","YJR144W","YJR148W","YJR149W","YKL217W","YKL180W","YKL163W","YKL142W","YKL141W","YKL127W","YKL120W","YKL109W","YKL106W","YKL093W","YKL085W","YKR016W","YKR057W","YKR059W","YKR067W","YKR076W","YKR097W","YKR097W","YLL061W","YLR061W","YLR064W","YLR109W","YLR142W","YLR174W","YLR267W","YLR345W","YLR356W","YLR367W","snR44","YLR413W","YLR438W","YLR449W","YML131W","YML042W","YML042W","YML030W","YMR011W","YMR035W","YMR058W","YMR107W","YMR175W","YMR191W","YMR196W","YMR206W","YMR250W","YMR251W","YNL277W-A","YNL277W","YNL237W","YNL202W","YNL162W","YNL160W","YNL117W","YNL112W","YNL100W","YNL093W","YNL065W","YNL052W","YNL036W","YNL015W","YNL009W","YNR014W","YNR034W-A","YOL152W","YOL132W","YOL083W","YOL077W-A","YOL059W","YOL058W","YOL053W","YOL045W","YOL014W","YOR020W-A","YOR040W","YOR065W","YOR084W","YOR108W","YOR136W","YOR147W","YOR173W","YOR289W","YOR328W","YOR359W","YOR374W","YOR382W","YOR384W","YOR385W","YPL271W","YPL262W","YPL245W","YPL230W","YPL226W","YPL222W","YPL200W","YPL198W","snR59","YPL147W","YPL147W","YPL111W","YPL054W","YPL024W","YPL014W","YPR001W","YPR002W","YPR020W","YPR026W","YPR030W","YPR030W","YPR036W-A","YPR043W","YPR080W","YPR160W","YPR160W-A","YPR184W","YPR191W","YAL067C","YAL054C","YAL054C","YAL005C","YBL078C","YBL066C","YBL064C","YBL045C","YBL030C","YBL029C-A","YBR004C","YBR045C","YBR046C","YBR050C","YBR052C","YBR053C","YBR085C-A","YBR092C","YBR093C","YBR105C","YBR116C","YBR117C","YBR132C","YBR222C","YBR230C","YBR238C","YBR255C-A","YBR262C","YBR269C","YBR291C","YBR296C","YCL037C","YCL030C","YCR005C","YCR010C","YCR098C","YDL223C","YDL222C","YDL215C","YDL174C","YDL169C","YDL110C","YDL085C-A","YDL079C","YDL067C","YDL061C","YDL049C","YDL027C","YDR018C","YDR059C","YDR070C","YDR144C","YDR148C","YDR231C","YDR256C","YDR261C","YDR298C","YDR309C","YDR322C-A","YDR326C","YDR345C","YDR357C","YDR375C","YDR379C-A","YDR461C-A","YDR529C","YEL060C","YER055C","YER056C-A","YER065C","YFL018C","YFR011C","YFR017C","YFR032C-A","YFR032C-B","YFR033C","YGL224C","YGL146C","YGL121C","YGL032C","YGR008C","YGR027C","YGR043C","YGR067C","YGR067C","YGR138C","YGR148C","YGR174C","YGR177C","YGR194C","YGR236C","YGR239C","YGR244C","YGR250C","YHL040C","YHL032C","YHR046C","YHR092C","YHR094C","YHR096C","YHR141C","YIL166C","YIL160C","YIL155C","YIL117C","YIL107C","YIL065C","YIL057C","YIL014C-A","YIR017C","YIR035C","YIR038C","YIR039C","YJL212C","YJL153C","YJL137C","YJL116C","YJL103C","YJL079C","YJL066C","YJL048C","YJR019C","YJR025C","YJR073C","YJR077C","YJR080C","YJR124C","YKL187C","YKL187C","YKL148C","YKL128C","YKL026C","YKL016C","YKL008C","YKR009C","YKR009C","YKR046C","YKR049C","YLL042C","YLL041C","YLL039C","YLL024C","YLL020C","YLL019C","YLL018C-A","YLR020C","YLR038C","YLR070C","YLR073C","YLR136C","YLR149C","YLR168C","YLR178C","YLR205C","YLR280C","YLR281C","YLR287C-A","YLR294C","YLR295C","YLR304C","YLR307C-A","YLR312C","YLR327C","YLR346C","YLR377C","YLR392C","YLR395C","YLR437C","YML130C","YML128C","YML120C","YML091C","YML087C","YML081C-A","YML056C","snR54","YML054C","YML018C","YMR041C","YMR056C","YMR062C","YMR081C","YMR110C","YMR118C","YMR174C","YMR221C","YMR242C","YMR256C","YMR280C","YMR303C","YMR319C","YNL305C","YNL274C","YNL270C","YNL259C","YNL200C","YNL195C","YNL194C","YNL173C","YNL134C","YNL125C","YNL069C","YNL037C","YNR001C","YNR002C","YNR013C","YNR050C","YNR064C","YNR074C","YOL147C","YOL136C","YOL126C","YOL105C","YOL080C","YOL052C-A","YOL031C","YOL016C","YOL002C","YOR047C","YOR051C","YOR064C","YOR100C","YOR103C","YOR178C","YOR185C","YOR215C","YOR312C","YOR316C","YOR348C","YOR360C","YOR383C","YPL227C","YPL223C","YPL201C","YPL186C","YPL177C","YPL171C","YPL156C","YPL134C","YPL119C","YPL113C","YPL109C","YPL078C","YPL058C","YPR006C","YPR006C","YPR010C","YPR010C-A","YPR061C","YPR151C","YPR155C","YPR194C"] + } + ] + } + ] + }, + { + "name": "Saccharomyces cerevisiae (2)", + "examples": + [ + { + "name": "Yeast mating genes", + "genes": ["STE2","STE3","STE5","GPA1","SST2","STE11","STE50","STE20","STE4","STE18","FUS3","KSS1","PTP2","MSG5","DIG1","DIG2","STE12"] + } + ], + "ontologies": + [ + { + "name": "Gene Ontology", + "ontology": "../test/data/yeast/go-basic.json", + "assocs": "../test/data/yeast/gene_association.sgd.json", + "examples": + [ + { + "name": "Yeast mating genes (2)", + "genes": ["STE2","STE3","STE5","GPA1","SST2","STE11","STE50","STE20","STE4","STE18","FUS3","KSS1","PTP2","MSG5","DIG1","DIG2","STE12"] + } + ] }, { - "name": "Saccharomyces cerevisiae (2)", - "examples": - [ - { - "name": "Yeast mating genes", - "genes": ["STE2","STE3","STE5","GPA1","SST2","STE11","STE50","STE20","STE4","STE18","FUS3","KSS1","PTP2","MSG5","DIG1","DIG2","STE12"] - } - ], - "ontologies": - [ - { - "name": "Gene Ontology", - "ontology": "../test/data/yeast/go-basic.json", - "assocs": "../test/data/yeast/gene_association.sgd.json", - "examples": - [ - { - "name": "Yeast mating genes (2)", - "genes": ["STE2","STE3","STE5","GPA1","SST2","STE11","STE50","STE20","STE4","STE18","FUS3","KSS1","PTP2","MSG5","DIG1","DIG2","STE12"] - } - ] - }, - { - "name": "Gene Ontology (2)", - "ontology": "../test/data/yeast/go-basic.json", - "assocs": "../test/data/yeast/gene_association.sgd.json" - } - ] + "name": "Gene Ontology (2)", + "ontology": "../test/data/yeast/go-basic.json", + "assocs": "../test/data/yeast/gene_association.sgd.json" } - ] + ] + } + ] }