function jstat(){}
j = jstat;
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){
xyz;
}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();
/******************************************************************************/
/* Constants */
/******************************************************************************/
jstat.ONE_SQRT_2PI = 0.3989422804014327;
jstat.LN_SQRT_2PI = 0.9189385332046727417803297;
jstat.LN_SQRT_PId2 = 0.225791352644727432363097614947;
jstat.DBL_MIN = 2.22507e-308;
jstat.DBL_EPSILON = 2.220446049250313e-16;
jstat.SQRT_32 = 5.656854249492380195206754896838;
jstat.TWO_PI = 6.283185307179586;
jstat.DBL_MIN_EXP = -999;
jstat.SQRT_2dPI = 0.79788456080287;
jstat.LN_SQRT_PI = 0.5723649429247;
/******************************************************************************/
/* jstat Functions */
/******************************************************************************/
jstat.seq = function(min, max, length) {
var r = new Range(min, max, length);
return r.getPoints();
}
jstat.dnorm = function(x, mean, sd, log) {
if(mean == null) mean = 0;
if(sd == null) sd = 1;
if(log == null) log = false;
var n = new NormalDistribution(mean, sd);
if(!isNaN(x)) {
// is a number
return n._pdf(x, log);
} else if(x.length) {
var res = [];
for(var i = 0; i < x.length; i++) {
res.push(n._pdf(x[i], log));
}
return res;
} else {
throw "Illegal argument: x";
}
}
jstat.pnorm = function(q, mean, sd, lower_tail, log) {
if(mean == null) mean = 0;
if(sd == null) sd = 1;
if(lower_tail == null) lower_tail = true;
if(log == null) log = false;
var n = new NormalDistribution(mean, sd);
if(!isNaN(q)) {
// is a number
return n._cdf(q, lower_tail, log);
} else if(q.length) {
var res = [];
for(var i = 0; i < q.length; i++) {
res.push(n._cdf(q[i], lower_tail, log));
}
return res;
} else {
throw "Illegal argument: x";
}
}
jstat.dlnorm = function(x, meanlog, sdlog, log) {
if(meanlog == null) meanlog = 0;
if(sdlog == null) sdlog = 1;
if(log == null) log = false;
var n = new LogNormalDistribution(meanlog, sdlog);
if(!isNaN(x)) {
// is a number
return n._pdf(x, log);
} else if(x.length) {
var res = [];
for(var i = 0; i < x.length; i++) {
res.push(n._pdf(x[i], log));
}
return res;
} else {
throw "Illegal argument: x";
}
}
jstat.plnorm = function(q, meanlog, sdlog, lower_tail, log) {
if(meanlog == null) meanlog = 0;
if(sdlog == null) sdlog = 1;
if(lower_tail == null) lower_tail = true;
if(log == null) log = false;
var n = new LogNormalDistribution(meanlog, sdlog);
if(!isNaN(q)) {
// is a number
return n._cdf(q, lower_tail, log);
}
else if(q.length) {
var res = [];
for(var i = 0; i < q.length; i++) {
res.push(n._cdf(q[i], lower_tail, log));
}
return res;
} else {
throw "Illegal argument: x";
}
}
jstat.dbeta = function(x, alpha, beta, ncp, log) {
if(ncp == null) ncp = 0;
if(log == null) log = false;
var b = new BetaDistribution(alpha, beta);
if(!isNaN(x)) {
// is a number
return b._pdf(x, log);
}
else if(x.length) {
var res = [];
for(var i = 0; i < x.length; i++) {
res.push(b._pdf(x[i], log));
}
return res;
} else {
throw "Illegal argument: x";
}
}
jstat.pbeta = function(q, alpha, beta, ncp, lower_tail, log) {
if(ncp == null) ncp = 0;
if(log == null) log = false;
if(lower_tail == null) lower_tail = true;
var b = new BetaDistribution(alpha, beta);
if(!isNaN(q)) {
// is a number
return b._cdf(q, lower_tail, log);
} else if(q.length) {
var res = [];
for(var i = 0; i < q.length; i++) {
res.push(b._cdf(q[i], lower_tail, log));
}
return res;
}
else {
throw "Illegal argument: x";
}
}
jstat.dgamma = function(x, shape, rate, scale, log) {
if(rate == null) rate = 1;
if(scale == null) scale = 1/rate;
if(log == null) log = false;
var g = new GammaDistribution(shape, scale);
if(!isNaN(x)) {
// is a number
return g._pdf(x, log);
} else if(x.length) {
var res = [];
for(var i = 0; i < x.length; i++) {
res.push(g._pdf(x[i], log));
}
return res;
} else {
throw "Illegal argument: x";
}
}
jstat.pgamma = function(q, shape, rate, scale, lower_tail, log) {
if(rate == null) rate = 1;
if(scale == null) scale = 1/rate;
if(lower_tail == null) lower_tail = true;
if(log == null) log = false;
var g = new GammaDistribution(shape, scale);
if(!isNaN(q)) {
// is a number
return g._cdf(q, lower_tail, log);
} else if(q.length) {
var res = [];
for(var i = 0; i < q.length; i++) {
res.push(g._cdf(q[i], lower_tail, log));
}
return res;
} else {
throw "Illegal argument: x";
}
}
jstat.dt = function(x, df, ncp, log) {
if(log == null) log = false;
var t = new StudentTDistribution(df, ncp);
if(!isNaN(x)) {
// is a number
return t._pdf(x, log);
} else if(x.length) {
var res = [];
for(var i = 0; i < x.length; i++) {
res.push(t._pdf(x[i], log));
}
return res;
} else {
throw "Illegal argument: x";
}
}
jstat.pt = function(q, df, ncp, lower_tail, log) {
if(lower_tail == null) lower_tail = true;
if(log == null) log = false;
var t = new StudentTDistribution(df, ncp);
if(!isNaN(q)) {
// is a number
return t._cdf(q, lower_tail, log);
} else if(q.length) {
var res = [];
for(var i = 0; i < q.length; i++) {
res.push(t._cdf(q[i], lower_tail, log));
}
return res;
} else {
throw "Illegal argument: x";
}
}
jstat.plot = function(x, y, options) {
if(x == null) {
throw "x is undefined in jstat.plot";
}
if(y == null) {
throw "y is undefined in jstat.plot";
}
if(x.length != y.length) {
throw "x and y lengths differ in jstat.plot";
}
var flotOpt = {
series: {
lines: {
},
points: {
}
}
};
// combine x & y
var series = [];
if(x.length == undefined) {
// single point
series.push([x, y]);
flotOpt.series.points.show = true;
} else {
// array
for(var i = 0; i < x.length; i++) {
series.push([x[i], y[i]]);
}
}
var title = 'jstat graph';
// configure Flot options
if(options != null) {
// options = JSON.parse(String(options));
if(options.type != null) {
if(options.type == 'l') {
flotOpt.series.lines.show = true;
} else if (options.type == 'p') {
flotOpt.series.lines.show = false;
flotOpt.series.points.show = true;
}
}
if(options.hover != null) {
flotOpt.grid = {
hoverable: options.hover
}
}
if(options.main != null) {
title = options.main;
}
}
var now = new Date();
var hash = now.getMilliseconds() * now.getMinutes() + now.getSeconds();
$('body').append('
');
$('#' + hash).dialog({
modal: false,
width: 475,
height: 475,
resizable: true,
resize: function() {
$.plot($('#graph-' + hash), [series], flotOpt);
},
open: function(event, ui) {
var id = '#graph-' + hash;
$.plot($('#graph-' + hash), [series], flotOpt);
}
})
}
/******************************************************************************/
/* Special Functions */
/******************************************************************************/
jstat.log10 = function(arg) {
return Math.log(arg) / Math.LN10;
}
/*
*
*/
jstat.toSigFig = function(num, n) {
if(num == 0) {
return 0;
}
var d = Math.ceil(jstat.log10(num < 0 ? -num: num));
var power = n - parseInt(d);
var magnitude = Math.pow(10,power);
var shifted = Math.round(num*magnitude);
return shifted/magnitude;
}
jstat.trunc = function(x) {
return (x > 0) ? Math.floor(x) : Math.ceil(x);
}
/**
* Tests whether x is a finite number
*/
jstat.isFinite = function(x) {
return (!isNaN(x) && (x != Number.POSITIVE_INFINITY) && (x != Number.NEGATIVE_INFINITY));
}
/**
* dopois_raw() computes the Poisson probability lb^x exp(-lb) / x!.
* This does not check that x is an integer, since dgamma() may
* call this with a fractional x argument. Any necessary argument
* checks should be done in the calling function.
*/
jstat.dopois_raw = function(x, lambda, give_log) {
/* x >= 0 ; integer for dpois(), but not e.g. for pgamma()!
lambda >= 0
*/
if (lambda == 0) {
if(x == 0) {
return(give_log) ? 0.0 : 1.0; //R_D__1
}
return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0
}
if (!jstat.isFinite(lambda)) return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0;
if (x < 0) return(give_log) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0
if (x <= lambda * jstat.DBL_MIN) {
return (give_log) ? -lambda : Math.exp(-lambda); // R_D_exp(-lambda)
}
if (lambda < x * jstat.DBL_MIN) {
var param = -lambda + x*Math.log(lambda) -jstat.lgamma(x+1);
return (give_log) ? param : Math.exp(param); // R_D_exp(-lambda + x*log(lambda) -lgammafn(x+1))
}
var param1 = jstat.TWO_PI * x; // f
var param2 = -jstat.stirlerr(x)-jstat.bd0(x,lambda); // x
return (give_log) ? -0.5*Math.log(param1)+param2 : Math.exp(param2)/Math.sqrt(param1); // R_D_fexp(M_2PI*x, -stirlerr(x)-bd0(x,lambda))
//return(R_D_fexp( , -stirlerr(x)-bd0(x,lambda) ));
}
/** Evaluates the "deviance part"
* bd0(x,M) := M * D0(x/M) = M*[ x/M * log(x/M) + 1 - (x/M) ] =
* = x * log(x/M) + M - x
* where M = E[X] = n*p (or = lambda), for x, M > 0
*
* in a manner that should be stable (with small relative error)
* for all x and M=np. In particular for x/np close to 1, direct
* evaluation fails, and evaluation is based on the Taylor series
* of log((1+v)/(1-v)) with v = (x-np)/(x+np).
*/
jstat.bd0 = function(x, np) {
var ej, s, s1, v, j;
if(!jstat.isFinite(x) || !jstat.isFinite(np) || np == 0.0) throw "illegal parameter in jstat.bd0";
if(Math.abs(x-np) > 0.1*(x+np)) {
v = (x-np)/(x+np);
s = (x-np)*v;/* s using v -- change by MM */
ej = 2*x*v;
v = v*v;
for (j=1; ; j++) { /* Taylor series */
ej *= v;
s1 = s+ej/((j<<1)+1);
if (s1==s) /* last term was effectively 0 */
return(s1);
s = s1;
}
}
/* else: | x - np | is not too small */
return(x*Math.log(x/np)+np-x);
}
/** Computes the log of the error term in Stirling's formula.
* For n > 15, uses the series 1/12n - 1/360n^3 + ...
* For n <=15, integers or half-integers, uses stored values.
* For other n < 15, uses lgamma directly (don't use this to
* write lgamma!)
*/
jstat.stirlerr= function(n) {
var S0 = 0.083333333333333333333;
var S1 = 0.00277777777777777777778;
var S2 = 0.00079365079365079365079365;
var S3 = 0.000595238095238095238095238;
var S4 = 0.0008417508417508417508417508;
var sferr_halves = [
0.0, /* n=0 - wrong, place holder only */
0.1534264097200273452913848, /* 0.5 */
0.0810614667953272582196702, /* 1.0 */
0.0548141210519176538961390, /* 1.5 */
0.0413406959554092940938221, /* 2.0 */
0.03316287351993628748511048, /* 2.5 */
0.02767792568499833914878929, /* 3.0 */
0.02374616365629749597132920, /* 3.5 */
0.02079067210376509311152277, /* 4.0 */
0.01848845053267318523077934, /* 4.5 */
0.01664469118982119216319487, /* 5.0 */
0.01513497322191737887351255, /* 5.5 */
0.01387612882307074799874573, /* 6.0 */
0.01281046524292022692424986, /* 6.5 */
0.01189670994589177009505572, /* 7.0 */
0.01110455975820691732662991, /* 7.5 */
0.010411265261972096497478567, /* 8.0 */
0.009799416126158803298389475, /* 8.5 */
0.009255462182712732917728637, /* 9.0 */
0.008768700134139385462952823, /* 9.5 */
0.008330563433362871256469318, /* 10.0 */
0.007934114564314020547248100, /* 10.5 */
0.007573675487951840794972024, /* 11.0 */
0.007244554301320383179543912, /* 11.5 */
0.006942840107209529865664152, /* 12.0 */
0.006665247032707682442354394, /* 12.5 */
0.006408994188004207068439631, /* 13.0 */
0.006171712263039457647532867, /* 13.5 */
0.005951370112758847735624416, /* 14.0 */
0.005746216513010115682023589, /* 14.5 */
0.005554733551962801371038690 /* 15.0 */
];
var nn;
if (n <= 15.0) {
nn = n + n;
if (nn == parseInt(nn)) return(sferr_halves[parseInt(nn)]);
return(jstat.lgamma(n + 1.0) - (n + 0.5)*Math.log(n) + n - jstat.LN_SQRT_2PI);
}
nn = n*n;
if (n>500) return((S0-S1/nn)/n);
if (n> 80) return((S0-(S1-S2/nn)/nn)/n);
if (n> 35) return((S0-(S1-(S2-S3/nn)/nn)/nn)/n);
/* 15 < n <= 35 : */
return((S0-(S1-(S2-(S3-S4/nn)/nn)/nn)/nn)/n);
}
/** The function lgamma computes log|gamma(x)|. The function
* lgammafn_sign in addition assigns the sign of the gamma function
* to the address in the second argument if this is not null.
*/
jstat.lgamma = function(x) {
function lgammafn_sign(x, sgn) {
var ans, y, sinpiy;
var xmax = 2.5327372760800758e+305;
var dxrel = 1.490116119384765696e-8;
// if (xmax == 0) {/* initialize machine dependent constants _ONCE_ */
// xmax = jstat.DBL_MAX/Math.log(jstat.DBL_MAX);/* = 2.533 e305 for IEEE double */
// dxrel = Math.sqrt(jstat.DBL_EPSILON);/* sqrt(Eps) ~ 1.49 e-8 for IEEE double */
// }
/* For IEEE double precision DBL_EPSILON = 2^-52 = 2.220446049250313e-16 :
xmax = DBL_MAX / log(DBL_MAX) = 2^1024 / (1024 * log(2)) = 2^1014 / log(2)
dxrel = sqrt(DBL_EPSILON) = 2^-26 = 5^26 * 1e-26 (is *exact* below !)
*/
if (sgn != null) sgn = 1;
if(isNaN(x)) return x;
if (x < 0 && (Math.floor(-x) % 2.0) == 0)
if (sgn != null) sgn = -1;
if (x <= 0 && x == jstat.trunc(x)) { /* Negative integer argument */
console.warn("Negative integer argument in lgammafn_sign");
return Number.POSITIVE_INFINITY;/* +Inf, since lgamma(x) = log|gamma(x)| */
}
y = Math.abs(x);
if(y <= 10) return Math.log(Math.abs(jstat.gamma(x))); // TODO: implement jstat.gamma
if(y > xmax) {
console.warn("Illegal arguement passed to lgammafn_sign");
return Number.POSITIVE_INFINITY;
}
if(x > 0) {
if(x > 1e17) {
return (x*(Math.log(x)-1.0));
} else if(x > 4934720.0) {
return (jstat.LN_SQRT_2PI + (x-0.5) * Math.log(x) - x);
} else {
return jstat.LN_SQRT_2PI + (x-0.5) * Math.log(x) - x + jstat.lgammacor(x); // TODO: implement lgammacor
}
}
sinpiy = Math.abs(Math.sin(Math.PI * y));
if(sinpiy == 0) {
throw "Should never happen!!";
}
ans = jstat.LN_SQRT_PId2 + (x - 0.5) * Math.log(y) - x - Math.log(sinpiy) - jstat.lgammacor(y);
if(Math.abs((x-jstat.trunc(x-0.5))* ans / x) < dxrel) {
throw "The answer is less than half the precision argument too close to a negative integer";
}
return ans;
}
return lgammafn_sign(x, null);
}
jstat.gamma = function(x) {
var xbig = 171.624;
var p = [
-1.71618513886549492533811,
24.7656508055759199108314,-379.804256470945635097577,
629.331155312818442661052,866.966202790413211295064,
-31451.2729688483675254357,-36144.4134186911729807069,
66456.1438202405440627855
];
var q = [
-30.8402300119738975254353,
315.350626979604161529144,-1015.15636749021914166146,
-3107.77167157231109440444,22538.1184209801510330112,
4755.84627752788110767815,-134659.959864969306392456,
-115132.259675553483497211
];
var c = [
-.001910444077728,8.4171387781295e-4,
-5.952379913043012e-4,7.93650793500350248e-4,
-.002777777777777681622553,.08333333333333333331554247,
.0057083835261
];
var i,n,parity,fact,xden,xnum,y,z,yi,res,sum,ysq;
parity = (0);
fact = 1.0;
n = 0;
y=x;
if(y <= 0.0) {
/* -------------------------------------------------------------
Argument is negative
------------------------------------------------------------- */
y = -x;
yi = jstat.trunc(y);
res = y - yi;
if (res != 0.0) {
if (yi != jstat.trunc(yi * 0.5) * 2.0)
parity = (1);
fact = -Math.PI / Math.sin(Math.PI * res);
y += 1.0;
} else {
return(Number.POSITIVE_INFINITY);
}
}
/* -----------------------------------------------------------------
Argument is positive
-----------------------------------------------------------------*/
if (y < jstat.DBL_EPSILON) {
/* --------------------------------------------------------------
Argument < EPS
-------------------------------------------------------------- */
if (y >= jstat.DBL_MIN) {
res = 1.0 / y;
} else {
return(Number.POSITIVE_INFINITY);
}
} else if (y < 12.0) {
yi = y;
if (y < 1.0) {
/* ---------------------------------------------------------
EPS < argument < 1
--------------------------------------------------------- */
z = y;
y += 1.0;
} else {
/* -----------------------------------------------------------
1 <= argument < 12, reduce argument if necessary
----------------------------------------------------------- */
n = parseInt(y) - 1;
y -= parseFloat(n);
z = y - 1.0;
}
/* ---------------------------------------------------------
Evaluate approximation for 1. < argument < 2.
---------------------------------------------------------*/
xnum = 0.0;
xden = 1.0;
for (i = 0; i < 8; ++i) {
xnum = (xnum + p[i]) * z;
xden = xden * z + q[i];
}
res = xnum / xden + 1.0;
if (yi < y) {
/* --------------------------------------------------------
Adjust result for case 0. < argument < 1.
-------------------------------------------------------- */
res /= yi;
} else if (yi > y) {
/* ----------------------------------------------------------
Adjust result for case 2. < argument < 12.
---------------------------------------------------------- */
for (i = 0; i < n; ++i) {
res *= y;
y += 1.0;
}
}
} else {
/* -------------------------------------------------------------
Evaluate for argument >= 12.,
------------------------------------------------------------- */
if (y <= xbig) {
ysq = y * y;
sum = c[6];
for (i = 0; i < 6; ++i) {
sum = sum / ysq + c[i];
}
sum = sum / y - y + jstat.LN_SQRT_2PI;
sum += (y - 0.5) * Math.log(y);
res = Math.exp(sum);
} else {
return(Number.POSITIVE_INFINITY);
}
}
/* ----------------------------------------------------------------------
Final adjustments and return
----------------------------------------------------------------------*/
if (parity)
res = -res;
if (fact != 1.0)
res = fact / res;
return res;
}
/** Compute the log gamma correction factor for x >= 10 so that
*
* log(gamma(x)) = .5*log(2*pi) + (x-.5)*log(x) -x + lgammacor(x)
*
* [ lgammacor(x) is called Del(x) in other contexts (e.g. dcdflib)]
*/
jstat.lgammacor = function(x) {
var algmcs = [
+.1666389480451863247205729650822e+0,
-.1384948176067563840732986059135e-4,
+.9810825646924729426157171547487e-8,
-.1809129475572494194263306266719e-10,
+.6221098041892605227126015543416e-13,
-.3399615005417721944303330599666e-15,
+.2683181998482698748957538846666e-17,
-.2868042435334643284144622399999e-19,
+.3962837061046434803679306666666e-21,
-.6831888753985766870111999999999e-23,
+.1429227355942498147573333333333e-24,
-.3547598158101070547199999999999e-26,
+.1025680058010470912000000000000e-27,
-.3401102254316748799999999999999e-29,
+.1276642195630062933333333333333e-30
];
var tmp;
var nalgm = 5;
var xbig = 94906265.62425156;
var xmax = 3.745194030963158e306;
if(x < 10) {
return Number.NaN;
} else if (x >= xmax) {
throw "Underflow error in lgammacor";
} else if (x < xbig) {
tmp = 10 / x;
return jstat.chebyshev(tmp*tmp*2-1,algmcs,nalgm) / x;
}
return 1 / (x*12);
}
/*
* Incomplete Beta function
*/
jstat.incompleteBeta = function(a, b, x) {
/*
* Used by incompleteBeta: Evaluates continued fraction for incomplete
* beta function by modified Lentz's method.
*/
function betacf(a, b, x) {
var MAXIT = 100;
var EPS = 3.0e-12;
var FPMIN = 1.0e-30;
var m,m2,aa,c,d,del,h,qab,qam,qap;
qab=a+b;
qap=a+1.0;
qam=a-1.0;
c=1.0;
d=1.0-qab*x/qap;
if(Math.abs(d) < FPMIN) {
d=FPMIN;
}
d = 1.0/d;
h=d;
for(m = 1; m <= MAXIT; m++) {
m2=2*m;
aa=m*(b-m)*x/((qam+m2)*(a+m2));
d=1.0+aa*d;
if(Math.abs(d) < FPMIN) {
d = FPMIN;
}
c=1.0+aa/c;
if(Math.abs(c) < FPMIN) {
c = FPMIN;
}
d=1.0/d;
h *= d*c;
aa = -(a+m)*(qab+m)*x/((a+m2) * (qap+m2));
d=1.0+aa*d;
if(Math.abs(d) < FPMIN) {
d = FPMIN;
}
c=1.0+aa/c;
if(Math.abs(c) < FPMIN) {
c=FPMIN;
}
d=1.0/d;
del=d*c;
h *= del;
if(Math.abs(del-1.0) < EPS) {
// are we done?
break;
}
}
if(m > MAXIT) {
console.warn("a or b too big, or MAXIT too small in betacf: " + a + ", " + b + ", " + x + ", " + h);
return h;
}
if(isNaN(h)) {
console.warn(a + ", " + b + ", " + x);
}
return h;
}
var bt;
if(x < 0.0 || x > 1.0) {
throw "bad x in routine incompleteBeta";
}
if(x == 0.0 || x == 1.0) {
bt = 0.0;
} else {
bt = Math.exp(jstat.lgamma(a+b) - jstat.lgamma(a) - jstat.lgamma(b) + a * Math.log(x)+ b * Math.log(1.0-x));
}
if(x < (a + 1.0)/(a+b+2.0)) {
return bt * betacf(a,b,x)/a;
} else {
return 1.0-bt*betacf(b,a,1.0-x)/b;
}
}
/** Evaluates the n-term Chebyshev series
* "a" at "x".
*/
jstat.chebyshev = function(x, a, n) {
var b0, b1, b2, twox;
var i;
if (n < 1 || n > 1000) return Number.NaN;
if (x < -1.1 || x > 1.1) return Number.NaN;
twox = x * 2;
b2 = b1 = 0;
b0 = 0;
for (i = 1; i <= n; i++) {
b2 = b1;
b1 = b0;
b0 = twox * b1 - b2 + a[n - i];
}
return (b0 - b2) * 0.5;
}
jstat.fmin2 = function(x, y) {
return (x < y) ? x : y;
}
jstat.log1p = function(x) {
// http://kevin.vanzonneveld.net
// + original by: Brett Zamir (http://brett-zamir.me)
// % note 1: Precision 'n' can be adjusted as desired
// * example 1: log1p(1e-15);
// * returns 1: 9.999999999999995e-16
var ret = 0,
n = 50; // degree of precision
if (x <= -1) {
return Number.NEGATIVE_INFINITY; // JavaScript style would be to return Number.NEGATIVE_INFINITY
}
if (x < 0 || x > 1) {
return Math.log(1 + x);
}
for (var i = 1; i < n; i++) {
if ((i % 2) === 0) {
ret -= Math.pow(x, i) / i;
} else {
ret += Math.pow(x, i) / i;
}
}
return ret;
}
jstat.expm1 = function(x) {
var y, a = Math.abs(x);
if(a < jstat.DBL_EPSILON) return x;
if(a > 0.697) return Math.exp(x) - 1; /* negligable cancellation */
if(a > 1e-8) {
y = Math.exp(x) - 1;
} else {
y = (x / 2 + 1) * x;
}
/* Newton step for solving log(1 + y) = x for y : */
/* WARNING: does not work for y ~ -1: bug in 1.5.0 */
y -= (1 + y) * (jstat.log1p(y) - x);
return y;
}
jstat.logBeta = function(a, b) {
var corr, p, q;
p = q = a;
if(b < p) p = b;/* := min(a,b) */
if(b > q) q = b;/* := max(a,b) */
/* both arguments must be >= 0 */
if (p < 0) {
console.warn('Both arguements must be >= 0');
return Number.NaN;
}
else if (p == 0) {
return Number.POSITIVE_INFINITY;
}
else if (!jstat.isFinite(q)) { /* q == +Inf */
return Number.NEGATIVE_INFINITY;
}
if (p >= 10) {
/* p and q are big. */
corr = jstat.lgammacor(p) + jstat.lgammacor(q) - jstat.lgammacor(p + q);
return Math.log(q) * -0.5 + jstat.LN_SQRT_2PI + corr
+ (p - 0.5) * Math.log(p / (p + q)) + q * jstat.log1p(-p / (p + q));
}
else if (q >= 10) {
/* p is small, but q is big. */
corr = jstat.lgammacor(q) - jstat.lgammacor(p + q);
return jstat.lgamma(p) + corr + p - p * Math.log(p + q)
+ (q - 0.5) * jstat.log1p(-p / (p + q));
}
else
/* p and q are small: p <= q < 10. */
return Math.log(jstat.gamma(p) * (jstat.gamma(q) / jstat.gamma(p + q)));
}
jstat.dbinom_raw = function(x, n, p, q, give_log) {
if(give_log == null) give_log = false;
var lf, lc;
if(p == 0) {
if(x == 0) {
// R_D__1
return (give_log) ? 0.0 : 1.0;
} else {
// R_D__0
return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
}
}
if(q == 0) {
if(x == n) {
// R_D__1
return (give_log) ? 0.0 : 1.0;
} else {
// R_D__0
return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
}
}
if (x == 0) {
if(n == 0) return (give_log) ? 0.0 : 1.0; //R_D__1;
lc = (p < 0.1) ? -jstat.bd0(n,n*q) - n*p : n*Math.log(q);
return ( give_log ) ? lc : Math.exp(lc); //R_D_exp(lc)
}
if (x == n) {
lc = (q < 0.1) ? -jstat.bd0(n,n*p) - n*q : n*Math.log(p);
return ( give_log ) ? lc : Math.exp(lc); //R_D_exp(lc)
}
if (x < 0 || x > n) return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0;
/* n*p or n*q can underflow to zero if n and p or q are small. This
used to occur in dbeta, and gives NaN as from R 2.3.0. */
lc = jstat.stirlerr(n) - jstat.stirlerr(x) - jstat.stirlerr(n-x) - jstat.bd0(x,n*p) - jstat.bd0(n-x,n*q);
/* f = (M_2PI*x*(n-x))/n; could overflow or underflow */
/* Upto R 2.7.1:
* lf = log(M_2PI) + log(x) + log(n-x) - log(n);
* -- following is much better for x << n : */
lf = Math.log(jstat.TWO_PI) + Math.log(x) + jstat.log1p(- x/n);
return (give_log) ? lc - 0.5*lf : Math.exp(lc - 0.5*lf); // R_D_exp(lc - 0.5*lf);
}
jstat.max = function(values) {
var max = Number.NEGATIVE_INFINITY;
for(var i = 0; i < values.length; i++) {
if(values[i] > max) {
max = values[i];
}
}
return max;
}
/******************************************************************************/
/* Probability Distributions */
/******************************************************************************/
/**
* Range class
*/
var Range = Class.extend({
init: function(min, max, numPoints) {
this._minimum = parseFloat(min);
this._maximum = parseFloat(max);
this._numPoints = parseFloat(numPoints);
},
getMinimum: function() {
return this._minimum;
},
getMaximum: function() {
return this._maximum;
},
getNumPoints: function() {
return this._numPoints;
},
getPoints: function() {
var results = [];
var x = this._minimum;
var step = (this._maximum-this._minimum)/(this._numPoints-1);
for(var i = 0; i < this._numPoints; i++) {
results[i] = parseFloat(x.toFixed(6));
x += step;
}
return results;
}
});
Range.validate = function(range) {
if( ! range instanceof Range) {
return false;
}
if(isNaN(range.getMinimum()) || isNaN(range.getMaximum()) || isNaN(range.getNumPoints()) || range.getMaximum() < range.getMinimum() || range.getNumPoints() <= 0) {
return false;
}
return true;
}
var ContinuousDistribution = Class.extend({
init: function(name) {
this._name = name;
},
toString: function() {
return this._string;
},
getName: function() {
return this._name;
},
getClassName: function() {
return this._name + 'Distribution';
},
density: function(valueOrRange) {
if(!isNaN(valueOrRange)) {
// single value
return parseFloat(this._pdf(valueOrRange).toFixed(15));
} else if (Range.validate(valueOrRange)) {
// multiple values
var points = valueOrRange.getPoints();
var result = [];
// For each point in the range
for(var i = 0; i < points.length; i++) {
result[i] = parseFloat(this._pdf(points[i]));
}
return result;
} else {
// neither value or range
throw "Invalid parameter supplied to " + this.getClassName() + ".density()";
}
},
cumulativeDensity: function(valueOrRange) {
if(!isNaN(valueOrRange)) {
// single value
return parseFloat(this._cdf(valueOrRange).toFixed(15));
} else if (Range.validate(valueOrRange)) {
// multiple values
var points = valueOrRange.getPoints();
var result = [];
// For each point in the range
for(var i = 0; i < points.length; i++) {
result[i] = parseFloat(this._cdf(points[i]));
}
return result;
} else {
// neither value or range
throw "Invalid parameter supplied to " + this.getClassName() + ".cumulativeDensity()";
}
},
getRange: function(standardDeviations, numPoints) {
if(standardDeviations == null) {
standardDeviations = 5;
}
if(numPoints == null) {
numPoints = 100;
}
var min = this.getMean() - standardDeviations * Math.sqrt(this.getVariance());
var max = this.getMean() + standardDeviations * Math.sqrt(this.getVariance());
if(this.getClassName() == 'GammaDistribution' || this.getClassName() == 'LogNormalDistribution') {
min = 0.0;
max = this.getMean() + standardDeviations * Math.sqrt(this.getVariance());
} else if(this.getClassName() == 'BetaDistribution') {
min = 0.0;
max = 1.0;
}
var range = new Range(min, max, numPoints);
return range;
},
getVariance: function(){},
getMean: function(){},
getQuantile: function(p) {
var self = this;
/*
* Recursive function to find the closest match
*/
function findClosestMatch(range, p) {
var ERR = 1.0e-5;
var xs = range.getPoints();
var closestIndex = 0;
var closestDistance = 999;
for(var i=0; i x] = 1 - P[X <= x]
*/
var a = [
2.2352520354606839287,
161.02823106855587881,
1067.6894854603709582,
18154.981253343561249,
0.065682337918207449113
];
var b = [
47.20258190468824187,
976.09855173777669322,
10260.932208618978205,
45507.789335026729956
];
var c = [
0.39894151208813466764,
8.8831497943883759412,
93.506656132177855979,
597.27027639480026226,
2494.5375852903726711,
6848.1904505362823326,
11602.651437647350124,
9842.7148383839780218,
1.0765576773720192317e-8
];
var d = [
22.266688044328115691,
235.38790178262499861,
1519.377599407554805,
6485.558298266760755,
18615.571640885098091,
34900.952721145977266,
38912.003286093271411,
19685.429676859990727
];
var p = [
0.21589853405795699,
0.1274011611602473639,
0.022235277870649807,
0.001421619193227893466,
2.9112874951168792e-5,
0.02307344176494017303
];
var q = [
1.28426009614491121,
0.468238212480865118,
0.0659881378689285515,
0.00378239633202758244,
7.29751555083966205e-5
];
var xden, xnum, temp, del, eps, xsq, y, i, lower, upper;
/* Consider changing these : */
eps = jstat.DBL_EPSILON * 0.5;
/* i_tail in {0,1,2} =^= {lower, upper, both} */
lower = i_tail != 1;
upper = i_tail != 0;
y = Math.abs(x);
if (y <= 0.67448975) { /* qnorm(3/4) = .6744.... -- earlier had 0.66291 */
if (y > eps) {
xsq = x * x;
xnum = a[4] * xsq;
xden = xsq;
for (i = 0; i < 3; ++i) {
xnum = (xnum + a[i]) * xsq;
xden = (xden + b[i]) * xsq;
}
} else {
xnum = xden = 0.0;
}
temp = x * (xnum + a[3]) / (xden + b[3]);
if(lower) cum = 0.5 + temp;
if(upper) ccum = 0.5 - temp;
if(log_p) {
if(lower) cum = Math.log(cum);
if(upper) ccum = Math.log(ccum);
}
} else if (y <= jstat.SQRT_32) {
/* Evaluate pnorm for 0.674.. = qnorm(3/4) < |x| <= sqrt(32) ~= 5.657 */
xnum = c[8] * y;
xden = y;
for (i = 0; i < 7; ++i) {
xnum = (xnum + c[i]) * y;
xden = (xden + d[i]) * y;
}
temp = (xnum + c[7]) / (xden + d[7]);
/* do_del */
xsq = jstat.trunc(x * 16) / 16;
del = (x - xsq) * (x + xsq);
if(log_p) {
cum = (-xsq * xsq * 0.5) + (-del * 0.5) + Math.log(temp);
if((lower && x > 0.) || (upper && x <= 0.))
ccum = jstat.log1p(-Math.exp(-xsq * xsq * 0.5) *
Math.exp(-del * 0.5) * temp);
}
else {
cum = Math.exp(-xsq * xsq * 0.5) * Math.exp(-del * 0.5) * temp;
ccum = 1.0 - cum;
}
/* end do_del */
/* swap_tail */
if (x > 0.0) {/* swap ccum <--> cum */
temp = cum;
if(lower) {
cum = ccum;
}
ccum = temp;
}
/* end swap_tail */
}
/* else |x| > sqrt(32) = 5.657 :
* the next two case differentiations were really for lower=T, log=F
* Particularly *not* for log_p !
* Cody had (-37.5193 < x && x < 8.2924) ; R originally had y < 50
*
* Note that we do want symmetry(0), lower/upper -> hence use y
*/
else if((log_p && y < 1e170)|| (lower && -37.5193 < x && x < 8.2924)
|| (upper && -8.2924 < x && x < 37.5193)) {
/* Evaluate pnorm for x in (-37.5, -5.657) union (5.657, 37.5) */
xsq = 1.0 / (x * x); /* (1./x)*(1./x) might be better */
xnum = p[5] * xsq;
xden = xsq;
for (i = 0; i < 4; ++i) {
xnum = (xnum + p[i]) * xsq;
xden = (xden + q[i]) * xsq;
}
temp = xsq * (xnum + p[4]) / (xden + q[4]);
temp = (jstat.ONE_SQRT_2PI - temp) / y;
/* do_del */
xsq = jstat.trunc(x * 16) / 16;
del = (x - xsq) * (x + xsq);
if(log_p) {
cum = (-xsq * xsq * 0.5) + (-del * 0.5) + Math.log(temp);
if((lower && x > 0.) || (upper && x <= 0.))
ccum = jstat.log1p(-Math.exp(-xsq * xsq * 0.5) *
Math.exp(-del * 0.5) * temp);
}
else {
cum = Math.exp(-xsq * xsq * 0.5) * Math.exp(-del * 0.5) * temp;
ccum = 1.0 - cum;
}
/* end do_del */
/* swap_tail */
if (x > 0.0) {/* swap ccum <--> cum */
temp = cum;
if(lower) {
cum = ccum;
}
ccum = temp;
}
/* end swap_tail */
} else { /* large x such that probs are 0 or 1 */
if(x > 0) {
cum = (log_p) ? 0.0 : 1.0; // R_D__1
ccum = (log_p) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0;
} else {
cum = (log_p) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0;
ccum = (log_p) ? 0.0 : 1.0; // R_D__1
}
}
return [cum, ccum];
}
var p, cp;
var mu = this._mean;
var sigma = this._sigma;
var R_DT_0, R_DT_1;
if(lower_tail) {
if(log_p) {
R_DT_0 = Number.NEGATIVE_INFINITY;
R_DT_1 = 0.0;
} else {
R_DT_0 = 0.0;
R_DT_1 = 1.0;
}
} else {
if(log_p) {
R_DT_0 = 0.0;
R_DT_1 = Number.NEGATIVE_INFINITY;
} else {
R_DT_0 = 1.0;
R_DT_1 = 0.0;
}
}
if(!jstat.isFinite(x) && mu == x) return Number.NaN;
if(sigma <= 0) {
if(sigma < 0) {
console.warn("Sigma is less than 0");
return Number.NaN;
}
return (x < mu) ? R_DT_0 : R_DT_1;
}
p = (x - mu) / sigma;
if(!jstat.isFinite(p)) {
return (x < mu) ? R_DT_0 : R_DT_1;
}
x = p;
// pnorm_both(x, &p, &cp, (lower_tail ? 0 : 1), log_p);
// result[0] == &p
// result[1] == &cp
var result = pnorm_both(x, p, cp, (lower_tail ? false : true), log_p);
return (lower_tail ? result[0] : result[1]);
},
getMean: function() {
return this._mean;
},
getSigma: function() {
return this._sigma;
},
getVariance: function() {
return this._sigma*this._sigma;
}
});
/**
* A Log-normal distribution object
*/
var LogNormalDistribution = ContinuousDistribution.extend({
init: function(location, scale) {
this._super('LogNormal')
this._location = parseFloat(location);
this._scale = parseFloat(scale);
this._string = "LogNormal ("+this._location.toFixed(2)+", " + this._scale.toFixed(2) + ")";
},
_pdf: function(x, give_log) {
var y;
var sdlog = this._scale;
var meanlog = this._location;
if(give_log == null) {
give_log = false;
}
if(sdlog <= 0) throw "Illegal parameter in _pdf";
if(x <= 0) {
return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
}
y = (Math.log(x) - meanlog) / sdlog;
return (give_log ? -(jstat.LN_SQRT_2PI + 0.5 * y * y + Math.log(x * sdlog)) :
jstat.ONE_SQRT_2PI * Math.exp(-0.5 * y * y) / (x * sdlog));
},
_cdf: function(x, lower_tail, log_p) {
var sdlog = this._scale;
var meanlog = this._location;
if(lower_tail == null) {
lower_tail = true;
}
if(log_p == null) {
log_p = false;
}
if(sdlog <= 0) {
throw "illegal std in _cdf";
}
if(x > 0) {
var nd = new NormalDistribution(meanlog, sdlog);
return nd._cdf(Math.log(x), lower_tail, log_p);
}
if(lower_tail) {
return (log_p) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0
} else {
return (log_p) ? 0.0 : 1.0; // R_D__1
}
},
getLocation: function() {
return this._location;
},
getScale: function() {
return this._scale;
},
getMean: function() {
return Math.exp((this._location + this._scale) / 2);
},
getVariance: function() {
var ans = (Math.exp(this._scale)-1)*Math.exp(2*this._location+this._scale);
return ans;
}
});
/**
* Gamma distribution object
*/
var GammaDistribution = ContinuousDistribution.extend({
init: function(shape, scale) {
this._super('Gamma');
this._shape = parseFloat(shape);
this._scale = parseFloat(scale);
this._string = "Gamma ("+this._shape.toFixed(2)+", " + this._scale.toFixed(2) + ")";
},
_pdf: function(x, give_log) {
var pr;
var shape = this._shape;
var scale = this._scale;
if(give_log == null) {
give_log = false; // default value
}
if(shape < 0 || scale <= 0) {
throw "Illegal argument in _pdf";
}
if(x < 0) {
return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0
}
if(shape == 0) { /* point mass at 0 */
return (x == 0) ? Number.POSITIVE_INFINITY : (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0
}
if(x == 0) {
if(shape < 1) return Number.POSITIVE_INFINITY;
if(shape > 1) return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0
/* else */
return (give_log) ? -Math.log(scale) : 1/scale;
}
if(shape < 1) {
pr = jstat.dopois_raw(shape, x/scale, give_log);
return give_log ? pr + Math.log(shape/x) : pr*shape/x;
}
/* else shape >= 1 */
pr = jstat.dopois_raw(shape-1, x/scale, give_log);
return give_log ? pr - Math.log(scale) : pr/scale;
},
/**
* This function computes the distribution function for the
* gamma distribution with shape parameter alph and scale parameter
* scale. This is also known as the incomplete gamma function.
* See Abramowitz and Stegun (6.5.1) for example.
*/
_cdf: function(x, lower_tail, log_p) {
/* define USE_PNORM */
function USE_PNORM() {
pn1 = Math.sqrt(alph) * 3.0 * (Math.pow(x/alph,1.0/3.0) + 1.0 / (9.0 * alph) - 1.0);
var norm_dist = new NormalDistribution(0.0, 1.0);
return norm_dist._cdf(pn1, lower_tail, log_p);
}
/* Defaults */
if(lower_tail == null) lower_tail = true;
if(log_p == null) log_p = false;
var alph = this._shape;
var scale = this._scale;
var xbig = 1.0e+8;
var xlarge = 1.0e+37;
var alphlimit = 1e5;
var pn1,pn2,pn3,pn4,pn5,pn6,arg,a,b,c,an,osum,sum,n,pearson;
if(alph <= 0. || scale <= 0.) {
console.warn('Invalid gamma params in _cdf');
return Number.NaN;
}
x/=scale;
if(isNaN(x)) return x;
if(x <= 0.0) {
// R_DT_0
if(lower_tail) {
// R_D__0
return (log_p) ? Number.NEGATIVE_INFINITY : 0.0;
} else {
// R_D__1
return (log_p) ? 0.0 : 1.0;
}
}
if(alph > alphlimit) {
return USE_PNORM();
}
if(x > xbig * alph) {
if(x > jstat.DBL_MAX * alph) {
// R_DT_1
if(lower_tail) {
// R_D__1
return (log_p) ? 0.0 : 1.0;
} else {
// R_D__0
return (log_p) ? Number.NEGATIVE_INFINITY : 0.0;
}
} else {
return USE_PNORM();
}
}
if(x <= 1.0 || x < alph) {
pearson = 1; /* use pearson's series expansion */
arg = alph * Math.log(x) - x - jstat.lgamma(alph + 1.0);
c = 1.0;
sum = 1.0;
a = alph;
do {
a += 1.0;
c *= x / a;
sum += c;
} while(c > jstat.DBL_EPSILON * sum);
} else { /* x >= max( 1, alph) */
pearson = 0;/* use a continued fraction expansion */
arg = alph * Math.log(x) - x - jstat.lgamma(alph);
a = 1. - alph;
b = a + x + 1.;
pn1 = 1.;
pn2 = x;
pn3 = x + 1.;
pn4 = x * b;
sum = pn3 / pn4;
for (n = 1; ; n++) {
a += 1.;/* = n+1 -alph */
b += 2.;/* = 2(n+1)-alph+x */
an = a * n;
pn5 = b * pn3 - an * pn1;
pn6 = b * pn4 - an * pn2;
if (Math.abs(pn6) > 0.) {
osum = sum;
sum = pn5 / pn6;
if (Math.abs(osum - sum) <= jstat.DBL_EPSILON * jstat.fmin2(1.0, sum))
break;
}
pn1 = pn3;
pn2 = pn4;
pn3 = pn5;
pn4 = pn6;
if (Math.abs(pn5) >= xlarge) {
pn1 /= xlarge;
pn2 /= xlarge;
pn3 /= xlarge;
pn4 /= xlarge;
}
}
}
arg += Math.log(sum);
lower_tail = (lower_tail == pearson);
if (log_p && lower_tail)
return(arg);
/* else */
/* sum = exp(arg); and return if(lower_tail) sum else 1-sum : */
if(lower_tail) {
return Math.exp(arg);
} else {
if(log_p) {
// R_Log1_Exp(arg);
return (arg > -Math.LN2) ? Math.log(-jstat.expm1(arg)) : jstat.log1p(-Math.exp(arg));
} else {
return -jstat.expm1(arg);
}
}
},
getShape: function() {
return this._shape;
},
getScale: function() {
return this._scale;
},
getMean: function() {
return this._shape * this._scale;
},
getVariance: function() {
return this._shape*Math.pow(this._scale,2);
}
});
/**
* A Beta distribution object
*/
var BetaDistribution = ContinuousDistribution.extend({
init: function(alpha, beta) {
this._super('Beta');
this._alpha = parseFloat(alpha);
this._beta = parseFloat(beta);
this._string = "Beta ("+this._alpha.toFixed(2)+", " + this._beta.toFixed(2) + ")";
},
_pdf: function(x, give_log) {
if(give_log == null) give_log = false; // default;
var a = this._alpha;
var b = this._beta;
var lval;
if(a <= 0 || b <= 0) {
console.warn('Illegal arguments in _pdf');
return Number.NaN;
}
if(x < 0 || x > 1) {
// R_D__0
return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
}
if(x == 0) {
if(a > 1) {
// R_D__0
return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
}
if(a < 1) {
return Number.POSITIVE_INFINITY;
}
/*a == 1 */ return (give_log) ? Math.log(b) : b; // R_D_val(b)
}
if(x == 1) {
if(b > 1) {
// R_D__0
return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
}
if(b < 1) {
return Number.POSITIVE_INFINITY;
}
/* b == 1 */ return (give_log) ? Math.log(a) : a; // R_D_val(a)
}
if(a<=2||b<=2) {
lval = (a-1)*Math.log(x) + (b-1)*jstat.log1p(-x) - jstat.logBeta(a, b);
} else {
lval = Math.log(a+b-1) + jstat.dbinom_raw(a-1, a+b-2, x, 1-x, true);
}
//R_D_exp(lval)
return (give_log) ? lval : Math.exp(lval);
},
_cdf: function(x, lower_tail, log_p) {
if(lower_tail == null) lower_tail = true;
if(log_p == null) log_p = false;
var pin = this._alpha;
var qin = this._beta;
if(pin <= 0 || qin <= 0) {
console.warn('Invalid argument in _cdf');
return Number.NaN;
}
if(x <= 0) {
//R_DT_0;
if(lower_tail) {
// R_D__0
return (log_p) ? Number.NEGATIVE_INFINITY : 0.0;
} else {
// R_D__1
return (log_p) ? 0.1 : 1.0;
}
}
if(x >= 1){
// R_DT_1
if(lower_tail) {
// R_D__1
return (log_p) ? 0.1 : 1.0;
} else {
// R_D__0
return (log_p) ? Number.NEGATIVE_INFINITY : 0.0;
}
}
/* else */
return jstat.incompleteBeta(pin, qin, x);
},
getAlpha: function() {
return this._alpha;
},
getBeta: function() {
return this._beta;
},
getMean: function() {
return this._alpha / (this._alpha+ this._beta);
},
getVariance: function() {
var ans = (this._alpha * this._beta) / (Math.pow(this._alpha+this._beta,2)*(this._alpha+this._beta+1));
return ans;
}
});
var StudentTDistribution = ContinuousDistribution.extend({
init: function(degreesOfFreedom, mu) {
this._super('StudentT');
this._dof = parseFloat(degreesOfFreedom);
if(mu != null) {
this._mu = parseFloat(mu);
this._string = "StudentT ("+this._dof.toFixed(2)+", " + this._mu.toFixed(2)+ ")";
} else {
this._mu = 0.0;
this._string = "StudentT ("+this._dof.toFixed(2)+")";
}
},
_pdf: function(x, give_log) {
if(give_log == null) give_log = false;
if(this._mu == null) {
return this._dt(x, give_log);
} else {
var y = this._dnt(x, give_log);
if(y > 1){
console.warn('x:' + x + ', y: ' + y);
}
return y;
}
},
_cdf: function(x, lower_tail, give_log) {
if(lower_tail == null) lower_tail = true;
if(give_log == null) give_log = false;
if(this._mu == null) {
return this._pt(x, lower_tail, give_log);
} else {
return this._pnt(x, lower_tail, give_log);
}
},
_dt: function(x, give_log) {
var t,u;
var n = this._dof;
if (n <= 0){
console.warn('Invalid parameters in _dt');
return Number.NaN;
}
if(!jstat.isFinite(x)) {
return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0;
}
if(!jstat.isFinite(n)) {
var norm = new NormalDistribution(0.0, 1.0);
return norm.density(x, give_log);
}
t = -jstat.bd0(n/2.0,(n+1)/2.0) + jstat.stirlerr((n+1)/2.0) - jstat.stirlerr(n/2.0);
if ( x*x > 0.2*n )
u = Math.log( 1+ x*x/n ) * n/2;
else
u = -jstat.bd0(n/2.0,(n+x*x)/2.0) + x*x/2.0;
var p1 = jstat.TWO_PI *(1+x*x/n);
var p2 = t-u;
return (give_log) ? -0.5*Math.log(p1) + p2 : Math.exp(p2)/Math.sqrt(p1); // R_D_fexp(M_2PI*(1+x*x/n), t-u);
},
_dnt: function(x, give_log) {
if(give_log == null) give_log = false;
var df = this._dof;
var ncp = this._mu;
var u;
if(df <= 0.0) {
console.warn("Illegal arguments _dnf");
return Number.NaN;
}
if(ncp == 0.0) {
return this._dt(x, give_log);
}
if(!jstat.isFinite(x)) {
// R_D__0
if(give_log) {
return Number.NEGATIVE_INFINITY;
} else {
return 0.0;
}
}
/* If infinite df then the density is identical to a
normal distribution with mean = ncp. However, the formula
loses a lot of accuracy around df=1e9
*/
if(!isFinite(df) || df > 1e8) {
var dist = new NormalDistribution(ncp, 1.);
return dist.density(x, give_log);
}
/* Do calculations on log scale to stabilize */
/* Consider two cases: x ~= 0 or not */
if (Math.abs(x) > Math.sqrt(df * jstat.DBL_EPSILON)) {
var newT = new StudentTDistribution(df+2, ncp);
u = Math.log(df) - Math.log(Math.abs(x)) +
Math.log(Math.abs(newT._pnt(x*Math.sqrt((df+2)/df), true, false) -
this._pnt(x, true, false)));
/* FIXME: the above still suffers from cancellation (but not horribly) */
}
else { /* x ~= 0 : -> same value as for x = 0 */
u = jstat.lgamma((df+1)/2) - jstat.lgamma(df/2)
- .5*(Math.log(Math.PI) + Math.log(df) + ncp*ncp);
}
return (give_log ? u : Math.exp(u));
},
_pt: function(x, lower_tail, log_p) {
if(lower_tail == null) lower_tail = true;
if(log_p == null) log_p = false;
var val, nx;
var n = this._dof;
var DT_0, DT_1;
if(lower_tail) {
if(log_p) {
DT_0 = Number.NEGATIVE_INFINITY;
DT_1 = 1.;
} else {
DT_0 = 0.;
DT_1 = 1.;
}
} else {
if(log_p) {
// not lower_tail but log_p
DT_0 = 0.;
DT_1 = Number.NEGATIVE_INFINITY;
} else {
// not lower_tail and not log_p
DT_0 = 1.;
DT_1 = 0.;
}
}
if(n <= 0.0) {
console.warn("Invalid T distribution _pt");
return Number.NaN;
}
var norm = new NormalDistribution(0,1);
if(!jstat.isFinite(x)) {
return (x < 0) ? DT_0 : DT_1;
}
if(!jstat.isFinite(n)) {
return norm._cdf(x, lower_tail, log_p);
}
if (n > 4e5) { /*-- Fixme(?): test should depend on `n' AND `x' ! */
/* Approx. from Abramowitz & Stegun 26.7.8 (p.949) */
val = 1./(4.*n);
return norm._cdf(x*(1. - val)/sqrt(1. + x*x*2.*val), lower_tail, log_p);
}
nx = 1 + (x/n)*x;
/* FIXME: This test is probably losing rather than gaining precision,
* now that pbeta(*, log_p = TRUE) is much better.
* Note however that a version of this test *is* needed for x*x > D_MAX */
if(nx > 1e100) { /* <==> x*x > 1e100 * n */
/* Danger of underflow. So use Abramowitz & Stegun 26.5.4
pbeta(z, a, b) ~ z^a(1-z)^b / aB(a,b) ~ z^a / aB(a,b),
with z = 1/nx, a = n/2, b= 1/2 :
*/
var lval;
lval = -0.5*n*(2*Math.log(Math.abs(x)) - Math.log(n))
- jstat.logBeta(0.5*n, 0.5) - Math.log(0.5*n);
val = log_p ? lval : Math.exp(lval);
} else {
/*
val = (n > x * x)
// ? pbeta (x * x / (n + x * x), 0.5, n / 2., 0, log_p)
// : pbeta (1. / nx, n / 2., 0.5, 1, log_p);
*/
if(n > x * x) {
var beta = new BetaDistribution(0.5, n/2.);
return beta._cdf(x*x/ (n + x * x), false, log_p);
} else {
beta = new BetaDistribution(n / 2., 0.5);
return beta._cdf(1. / nx, true, log_p);
}
}
/* Use "1 - v" if lower_tail and x > 0 (but not both):*/
if(x <= 0.)
lower_tail = !lower_tail;
if(log_p) {
if(lower_tail) return jstat.log1p(-0.5*Math.exp(val));
else return val - M_LN2; /* = log(.5* pbeta(....)) */
}
else {
val /= 2.;
if(lower_tail) {
return (0.5 - val + 0.5);
} else {
return val;
}
}
},
_pnt: function(t, lower_tail, log_p) {
var dof = this._dof;
var ncp = this._mu;
var DT_0, DT_1;
if(lower_tail) {
if(log_p) {
DT_0 = Number.NEGATIVE_INFINITY;
DT_1 = 1.;
} else {
DT_0 = 0.;
DT_1 = 1.;
}
} else {
if(log_p) {
// not lower_tail but log_p
DT_0 = 0.;
DT_1 = Number.NEGATIVE_INFINITY;
} else {
// not lower_tail and not log_p
DT_0 = 1.;
DT_1 = 0.;
}
}
var albeta, a, b, del, errbd, lambda, rxb, tt, x;
var geven, godd, p, q, s, tnc, xeven, xodd;
var it, negdel;
/* note - itrmax and errmax may be changed to suit one's needs. */
var ITRMAX = 1000;
var ERRMAX = 1.e-7;
if(dof <= 0.0) {
return Number.NaN;
} else if (dof == 0.0) {
return this._pt(t);
}
if(!jstat.isFinite(t)) {
return (t < 0) ? DT_0 : DT_1;
}
if(t >= 0.) {
negdel = false;
tt = t;
del = ncp;
} else {
/* We deal quickly with left tail if extreme,
since pt(q, df, ncp) <= pt(0, df, ncp) = \Phi(-ncp) */
if(ncp >= 40 && (!log_p || !lower_tail)) {
return DT_0;
}
negdel = true;
tt = -t;
del = -ncp;
}
if(dof > 4e5 || del*del > 2* Math.LN2 * (-(jstat.DBL_MIN_EXP))) {
/*-- 2nd part: if del > 37.62, then p=0 below
FIXME: test should depend on `df', `tt' AND `del' ! */
/* Approx. from Abramowitz & Stegun 26.7.10 (p.949) */
s=1./(4.*dof);
var norm = new NormalDistribution(del, Math.sqrt(1. + tt*tt*2.*s));
var result = norm._cdf(tt*(1.-s), lower_tail != negdel, log_p);
return result;
}
/* initialize twin series */
/* Guenther, J. (1978). Statist. Computn. Simuln. vol.6, 199. */
x = t * t;
rxb = dof/(x + dof);/* := (1 - x) {x below} -- but more accurately */
x = x / (x + dof);/* in [0,1) */
if (x > 0.) {/* <==> t != 0 */
lambda = del * del;
p = .5 * Math.exp(-.5 * lambda);
if(p == 0.) { // underflow!
console.warn("underflow in _pnt");
return DT_0;
}
q = jstat.SQRT_2dPI * p * del;
s = .5 - p;
if(s < 1e-7) {
s = -0.5 * jstat.expm1(-0.5 * lambda);
}
a = .5;
b = .5 * dof;
/* rxb = (1 - x) ^ b [ ~= 1 - b*x for tiny x --> see 'xeven' below]
* where '(1 - x)' =: rxb {accurately!} above */
rxb = Math.pow(rxb, b);
albeta = jstat.LN_SQRT_PI + jstat.lgamma(b) - jstat.lgamma(.5 + b);
/* TODO: change incompleteBeta function to accept lower_tail and p_log */
xodd = jstat.incompleteBeta(a, b, x);
godd = 2. * rxb * Math.exp(a * Math.log(x) - albeta);
tnc = b * x;
xeven = (tnc < jstat.DBL_EPSILON) ? tnc : 1. - rxb;
geven = tnc * rxb;
tnc = p * xodd + q * xeven;
/* repeat until convergence or iteration limit */
for(it = 1; it <= ITRMAX; it++) {
a += 1.;
xodd -= godd;
xeven -= geven;
godd *= x * (a + b - 1.) / a;
geven *= x * (a + b - .5) / (a + .5);
p *= lambda / (2 * it);
q *= lambda / (2 * it + 1);
tnc += p * xodd + q * xeven;
s -= p;
/* R 2.4.0 added test for rounding error here. */
if(s < -1.e-10) { /* happens e.g. for (t,df,ncp)=(40,10,38.5), after 799 it.*/
//console.write("precision error _pnt");
break;
}
if(s <= 0 && it > 1) break;
errbd = 2. * s * (xodd - godd);
if(Math.abs(errbd) < ERRMAX) break;/*convergence*/
}
if(it == ITRMAX) {
throw "Non-convergence _pnt";
}
} else {
tnc = 0.;
}
norm = new NormalDistribution(0,1);
tnc += norm._cdf(-del, /*lower*/true, /*log_p*/ false);
lower_tail = lower_tail != negdel; /* xor */
if(tnc > 1 - 1e-10 && lower_tail) {
//console.warn("precision error _pnt");
}
var res = jstat.fmin2(tnc, 1.);
if(lower_tail) {
if(log_p) {
return Math.log(res);
} else {
return res;
}
} else {
if(log_p) {
return jstat.log1p(-(res));
} else {
return (0.5 - (res) + 0.5);
}
}
},
getDegreesOfFreedom: function() {
return this._dof;
},
getNonCentralityParameter: function() {
return this._mu;
},
getMean: function() {
if(this._dof > 1) {
var ans = (1/2)*Math.log(this._dof/2) + jstat.lgamma((this._dof-1)/2) - jstat.lgamma(this._dof/2)
return Math.exp(ans) * this._mu;
} else {
return Number.NaN;
}
},
getVariance: function() {
if(this._dof > 2) {
var ans = this._dof * (1 + this._mu*this._mu)/(this._dof-2) - (((this._mu*this._mu * this._dof) / 2) * Math.pow(Math.exp(jstat.lgamma((this._dof - 1)/2)-jstat.lgamma(this._dof/2)), 2));
return ans;
} else {
return Number.NaN;
}
}
});
/******************************************************************************/
/* jQuery Flot graph objects */
/******************************************************************************/
var Plot = Class.extend({
init: function(id, options) {
this._container = '#' + String(id);
this._plots = [];
this._flotObj = null;
this._locked = false;
if(options != null) {
this._options = options;
} else {
this._options = {
};
}
},
getContainer: function() {
return this._container;
},
getGraph: function() {
return this._flotObj;
},
setData: function(data) {
this._plots = data;
},
clear: function() {
this._plots = [];
//this.render();
},
showLegend: function() {
this._options.legend = {
show: true
}
this.render();
},
hideLegend: function() {
this._options.legend = {
show: false
}
this.render();
},
render: function() {
this._flotObj = null;
this._flotObj = $.plot($(this._container), this._plots, this._options);
}
});
var DistributionPlot = Plot.extend({
init: function(id, distribution, range, options) {
this._super(id, options);
this._showPDF = true;
this._showCDF = false;
this._pdfValues = []; // raw values for pdf
this._cdfValues = []; // raw values for cdf
this._maxY = 1;
this._plotType = 'line'; // line, point, both
this._fill = false;
this._distribution = distribution; // underlying PDF
// Range object for the plot
if(range != null && Range.validate(range)) {
this._range = range;
} else {
this._range = this._distribution.getRange(); // no range supplied, use distribution default
}
// render
if(this._distribution != null) {
this._maxY = this._generateValues(); // create the pdf/cdf values in the ctor
} else {
this._options.xaxis = {
min: range.getMinimum(),
max: range.getMaximum()
}
this._options.yaxis = {
max: 1
}
}
this.render();
},
setHover: function(bool) {
if(bool) {
if(this._options.grid == null) {
this._options.grid = {
hoverable: true,
mouseActiveRadius: 25
}
} else {
this._options.grid.hoverable = true,
this._options.grid.mouseActiveRadius = 25
}
function showTooltip(x, y, contents, color) {
$('' + contents + '
').css( {
position: 'absolute',
display: 'none',
top: y + 15,
'font-size': 'small',
left: x + 5,
border: '1px solid ' + color[1],
color: color[2],
padding: '5px',
'background-color': color[0],
opacity: 0.80
}).appendTo("body").show();
}
var previousPoint = null;
$(this._container).bind("plothover", function(event, pos, item) {
$("#x").text(pos.x.toFixed(2));
$("#y").text(pos.y.toFixed(2));
if (item) {
if (previousPoint != item.datapoint) {
previousPoint = item.datapoint;
$("#jstat_tooltip").remove();
var x = jstat.toSigFig(item.datapoint[0],2), y = jstat.toSigFig(item.datapoint[1], 2);
var text = null;
var color = item.series.color;
if(item.series.label == 'PDF') {
text = "P(" + x + ") = " + y;
color = ["#fee", "#fdd", "#C05F5F"];
} else {
// cdf
text = "F(" + x + ") = " + y;
color = ["#eef", "#ddf", "#4A4AC0"];
}
showTooltip(item.pageX, item.pageY, text, color);
}
}
else {
$("#jstat_tooltip").remove();
previousPoint = null;
}
});
$(this._container).bind("mouseleave", function() {
if($('#jstat_tooltip').is(':visible')) {
$('#jstat_tooltip').remove();
previousPoint = null;
}
});
} else {
// unbind
if(this._options.grid == null) {
this._options.grid = {
hoverable: false
}
} else {
this._options.grid.hoverable = false
}
$(this._container).unbind("plothover");
}
this.render();
},
setType: function(type) {
this._plotType = type;
var lines = {};
var points = {};
if(this._plotType == 'line') {
lines.show = true;
points.show = false;
} else if(this._plotType == 'points') {
lines.show = false;
points.show = true;
} else if(this._plotType == 'both') {
lines.show = true;
points.show = true;
}
if(this._options.series == null) {
this._options.series = {
lines: lines,
points: points
}
} else {
if(this._options.series.lines == null) {
this._options.series.lines = lines;
} else {
this._options.series.lines.show = lines.show;
}
if(this._options.series.points == null) {
this._options.series.points = points;
} else {
this._options.series.points.show = points.show;
}
}
this.render();
},
setFill: function(bool) {
this._fill = bool;
if(this._options.series == null) {
this._options.series = {
lines: {
fill: bool
}
}
} else {
if(this._options.series.lines == null) {
this._options.series.lines = {
fill: bool
}
} else {
this._options.series.lines.fill = bool;
}
}
this.render();
},
clear: function() {
this._super();
this._distribution = null;
this._pdfValues = [];
this._cdfValues = [];
this.render();
},
_generateValues: function() {
this._cdfValues = []; // reinitialize the arrays.
this._pdfValues = [];
var xs = this._range.getPoints();
this._options.xaxis = {
min: xs[0],
max: xs[xs.length-1]
}
var pdfs = this._distribution.density(this._range);
var cdfs = this._distribution.cumulativeDensity(this._range);
for(var i = 0; i < xs.length; i++) {
if(pdfs[i] == Number.POSITIVE_INFINITY || pdfs[i] == Number.NEGATIVE_INFINITY) {
pdfs[i] = null;
}
if(cdfs[i] == Number.POSITIVE_INFINITY || cdfs[i] == Number.NEGATIVE_INFINITY) {
cdfs[i] = null;
}
this._pdfValues.push([xs[i], pdfs[i]]);
this._cdfValues.push([xs[i], cdfs[i]]);
}
return jstat.max(pdfs);
},
showPDF: function() {
this._showPDF = true;
this.render();
},
hidePDF: function() {
this._showPDF = false;
this.render();
},
showCDF: function() {
this._showCDF = true;
this.render();
},
hideCDF: function() {
this._showCDF = false;
this.render();
},
setDistribution: function(distribution, range) {
this._distribution = distribution;
if(range != null) {
this._range = range;
} else {
this._range = distribution.getRange();
}
this._maxY = this._generateValues();
this._options.yaxis = {
max: this._maxY*1.1
}
this.render();
},
getDistribution: function() {
return this._distribution;
},
getRange: function() {
return this._range;
},
setRange: function(range) {
this._range = range;
this._generateValues();
this.render();
},
render: function() {
if(this._distribution != null) {
if(this._showPDF && this._showCDF) {
this.setData([{
yaxis: 1,
data:this._pdfValues,
color: 'rgb(237,194,64)',
clickable: false,
hoverable: true,
label: "PDF"
}, {
yaxis: 2,
data:this._cdfValues,
clickable: false,
color: 'rgb(175,216,248)',
hoverable: true,
label: "CDF"
}]);
this._options.yaxis = {
max: this._maxY*1.1
}
} else if(this._showPDF) {
this.setData([{
data:this._pdfValues,
hoverable: true,
color: 'rgb(237,194,64)',
clickable: false,
label: "PDF"
}]);
this._options.yaxis = {
max: this._maxY*1.1
}
} else if(this._showCDF) {
this.setData([{
data:this._cdfValues,
hoverable: true,
color: 'rgb(175,216,248)',
clickable: false,
label: "CDF"
}]);
this._options.yaxis = {
max: 1.1
}
}
} else {
this.setData([]);
}
this._super(); // Call the parent plot method
}
});
var DistributionFactory = {};
DistributionFactory.build = function(json) {
/*
if(json.name == null) {
try{
json = JSON.parse(json);
}
catch(err) {
throw "invalid JSON";
}
// try to parse it
}*/
/*
if(json.name != null) {
var name = json.name;
} else {
throw "Malformed JSON provided to DistributionBuilder " + json;
}
*/
if(json.NormalDistribution) {
if(json.NormalDistribution.mean != null && json.NormalDistribution.standardDeviation != null) {
return new NormalDistribution(json.NormalDistribution.mean[0], json.NormalDistribution.standardDeviation[0]);
} else {
throw "Malformed JSON provided to DistributionBuilder " + json;
}
} else if (json.LogNormalDistribution) {
if(json.LogNormalDistribution.location != null && json.LogNormalDistribution.scale != null) {
return new LogNormalDistribution(json.LogNormalDistribution.location[0], json.LogNormalDistribution.scale[0]);
} else {
throw "Malformed JSON provided to DistributionBuilder " + json;
}
} else if (json.BetaDistribution) {
if(json.BetaDistribution.alpha != null && json.BetaDistribution.beta != null) {
return new BetaDistribution(json.BetaDistribution.alpha[0], json.BetaDistribution.beta[0]);
} else {
throw "Malformed JSON provided to DistributionBuilder " + json;
}
} else if (json.GammaDistribution) {
if(json.GammaDistribution.shape != null && json.GammaDistribution.scale != null) {
return new GammaDistribution(json.GammaDistribution.shape[0], json.GammaDistribution.scale[0]);
} else {
throw "Malformed JSON provided to DistributionBuilder " + json;
}
} else if (json.StudentTDistribution) {
if(json.StudentTDistribution.degreesOfFreedom != null && json.StudentTDistribution.nonCentralityParameter != null) {
return new StudentTDistribution(json.StudentTDistribution.degreesOfFreedom[0], json.StudentTDistribution.nonCentralityParameter[0]);
} else if(json.StudentTDistribution.degreesOfFreedom != null) {
return new StudentTDistribution(json.StudentTDistribution.degreesOfFreedom[0]);
} else {
throw "Malformed JSON provided to DistributionBuilder " + json;
}
} else {
throw "Malformed JSON provided to DistributionBuilder " + json;
}
}