1597 lines
40 KiB
JavaScript
1597 lines
40 KiB
JavaScript
(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 f(a, b, c) {
|
|
if (!(this instanceof f))
|
|
return new f(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 tukey arcsine'
|
|
).split(' '));
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
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));
|
|
}
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
mean: function mean(alpha, beta) {
|
|
return alpha / (alpha + beta);
|
|
},
|
|
|
|
median: function median(alpha, beta) {
|
|
return jStat.ibetainv(0.5, alpha, beta);
|
|
},
|
|
|
|
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));
|
|
},
|
|
|
|
variance: function variance(alpha, beta) {
|
|
return (alpha * beta) / (Math.pow(alpha + beta, 2) * (alpha + beta + 1));
|
|
}
|
|
});
|
|
|
|
// 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;
|
|
|
|
if (x < 0)
|
|
return 0;
|
|
|
|
if (df1 <= 2) {
|
|
if (x === 0 && df1 < 2) {
|
|
return Infinity;
|
|
}
|
|
if (x === 0 && df1 === 2) {
|
|
return 1;
|
|
}
|
|
return (1 / jStat.betafn(df1 / 2, df2 / 2)) *
|
|
Math.pow(df1 / df2, df1 / 2) *
|
|
Math.pow(x, (df1/2) - 1) *
|
|
Math.pow((1 + (df1 / df2) * x), -(df1 + 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);
|
|
},
|
|
|
|
cdf: function cdf(x, df1, df2) {
|
|
if (x < 0)
|
|
return 0;
|
|
return jStat.ibeta((df1 * x) / (df1 * x + df2), df1 / 2, df2 / 2);
|
|
},
|
|
|
|
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;
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
variance: function variance(df1, df2) {
|
|
if (df2 <= 4)
|
|
return undefined;
|
|
return 2 * df2 * df2 * (df1 + df2 - 2) /
|
|
(df1 * (df2 - 2) * (df2 - 2) * (df2 - 4));
|
|
}
|
|
});
|
|
|
|
|
|
// 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;
|
|
},
|
|
|
|
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: function median(local/*, scale*/) {
|
|
return local;
|
|
},
|
|
|
|
mode: function mode(local/*, scale*/) {
|
|
return local;
|
|
},
|
|
|
|
sample: function sample(local, scale) {
|
|
return jStat.randn() *
|
|
Math.sqrt(1 / (2 * jStat.randg(0.5))) * scale + local;
|
|
}
|
|
});
|
|
|
|
|
|
|
|
// 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));
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
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: function mode(dof) {
|
|
return (dof - 2 > 0) ? dof - 2 : 0;
|
|
},
|
|
|
|
sample: function sample(dof) {
|
|
return jStat.randg(dof / 2) * 2;
|
|
},
|
|
|
|
variance: function variance(dof) {
|
|
return 2 * dof;
|
|
}
|
|
});
|
|
|
|
|
|
|
|
// 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;
|
|
},
|
|
|
|
mean : function(rate) {
|
|
return 1 / rate;
|
|
},
|
|
|
|
median: function (rate) {
|
|
return (1 / rate) * Math.log(2);
|
|
},
|
|
|
|
mode: function mode(/*rate*/) {
|
|
return 0;
|
|
},
|
|
|
|
sample: function sample(rate) {
|
|
return -1 / rate * Math.log(jStat._random_fn());
|
|
},
|
|
|
|
variance : function(rate) {
|
|
return Math.pow(rate, -2);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
// 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));
|
|
},
|
|
|
|
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;
|
|
},
|
|
|
|
mean : function(shape, scale) {
|
|
return shape * scale;
|
|
},
|
|
|
|
mode: function mode(shape, scale) {
|
|
if(shape > 1) return (shape - 1) * scale;
|
|
return undefined;
|
|
},
|
|
|
|
sample: function sample(shape, scale) {
|
|
return jStat.randg(shape) * scale;
|
|
},
|
|
|
|
variance: function variance(shape, scale) {
|
|
return shape * scale * scale;
|
|
}
|
|
});
|
|
|
|
// 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));
|
|
},
|
|
|
|
cdf: function cdf(x, shape, scale) {
|
|
if (x <= 0)
|
|
return 0;
|
|
return 1 - jStat.lowRegGamma(shape, scale / x);
|
|
},
|
|
|
|
inv: function(p, shape, scale) {
|
|
return scale / jStat.gammapinv(1 - p, shape);
|
|
},
|
|
|
|
mean : function(shape, scale) {
|
|
return (shape > 1) ? scale / (shape - 1) : undefined;
|
|
},
|
|
|
|
mode: function mode(shape, scale) {
|
|
return scale / (shape + 1);
|
|
},
|
|
|
|
sample: function sample(shape, scale) {
|
|
return scale / jStat.randg(shape);
|
|
},
|
|
|
|
variance: function variance(shape, scale) {
|
|
if (shape <= 2)
|
|
return undefined;
|
|
return scale * scale / ((shape - 1) * (shape - 1) * (shape - 2));
|
|
}
|
|
});
|
|
|
|
|
|
// 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)));
|
|
},
|
|
|
|
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));
|
|
},
|
|
|
|
inv: function inv(p, alpha, beta) {
|
|
return Math.pow(1 - Math.pow(1 - p, 1 / beta), 1 / alpha);
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
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));
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
mean: function mean(mu, sigma) {
|
|
return Math.exp(mu + sigma * sigma / 2);
|
|
},
|
|
|
|
median: function median(mu/*, sigma*/) {
|
|
return Math.exp(mu);
|
|
},
|
|
|
|
mode: function mode(mu, sigma) {
|
|
return Math.exp(mu - sigma * sigma);
|
|
},
|
|
|
|
sample: function sample(mu, sigma) {
|
|
return Math.exp(jStat.randn() * sigma + mu);
|
|
},
|
|
|
|
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));
|
|
}
|
|
|
|
// 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));
|
|
},
|
|
|
|
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);
|
|
|
|
// turn negative x into positive and flip result afterwards
|
|
var flip = false;
|
|
if (x < 0) {
|
|
flip = true;
|
|
ncp = -ncp;
|
|
}
|
|
|
|
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++;
|
|
}
|
|
|
|
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));
|
|
},
|
|
|
|
cdf: function cdf(x, mean, std) {
|
|
return 0.5 * (1 + jStat.erf((x - mean) / Math.sqrt(2 * std * std)));
|
|
},
|
|
|
|
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;
|
|
},
|
|
|
|
mode: function (mean/*, std*/) {
|
|
return mean;
|
|
},
|
|
|
|
sample: function sample(mean, std) {
|
|
return jStat.randn() * std + mean;
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
cdf: function cdf(x, scale, shape) {
|
|
if (x < scale)
|
|
return 0;
|
|
return 1 - Math.pow(scale / x, shape);
|
|
},
|
|
|
|
inv: function inv(p, scale, shape) {
|
|
return scale / Math.pow(1 - p, 1 / shape);
|
|
},
|
|
|
|
mean: function mean(scale, shape) {
|
|
if (shape <= 1)
|
|
return undefined;
|
|
return (shape * Math.pow(scale, shape)) / (shape - 1);
|
|
},
|
|
|
|
median: function median(scale, shape) {
|
|
return scale * (shape * Math.SQRT2);
|
|
},
|
|
|
|
mode: function mode(scale/*, shape*/) {
|
|
return scale;
|
|
},
|
|
|
|
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));
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
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;
|
|
},
|
|
|
|
median: function median(/*dof*/) {
|
|
return 0;
|
|
},
|
|
|
|
mode: function mode(/*dof*/) {
|
|
return 0;
|
|
},
|
|
|
|
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;
|
|
}
|
|
});
|
|
|
|
|
|
|
|
// 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);
|
|
},
|
|
|
|
mode: function mode(scale, shape) {
|
|
if (shape <= 1)
|
|
return 0;
|
|
return scale * Math.pow((shape - 1) / shape, 1 / shape);
|
|
},
|
|
|
|
sample: function sample(scale, shape) {
|
|
return scale * Math.pow(-Math.log(jStat._random_fn()), 1 / shape);
|
|
},
|
|
|
|
variance: function variance(scale, shape) {
|
|
return scale * scale * jStat.gammafn(1 + 2 / shape) -
|
|
Math.pow(jStat.weibull.mean(scale, shape), 2);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
// 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');
|
|
},
|
|
|
|
sample: function sample(a, b) {
|
|
return (a / 2 + b / 2) + (b / 2 - a / 2) * (2 * jStat._random_fn() - 1);
|
|
},
|
|
|
|
variance: function variance(a, b) {
|
|
return Math.pow(b - a, 2) / 12;
|
|
}
|
|
});
|
|
|
|
|
|
// Got this from http://www.math.ucla.edu/~tom/distributions/binomial.html
|
|
function betinc(x, a, b, eps) {
|
|
var a0 = 0;
|
|
var b0 = 1;
|
|
var a1 = 1;
|
|
var b1 = 1;
|
|
var m9 = 0;
|
|
var a2 = 0;
|
|
var c9;
|
|
|
|
while (Math.abs((a1 - a2) / a1) > eps) {
|
|
a2 = a1;
|
|
c9 = -(a + m9) * (a + b + m9) * x / (a + 2 * m9) / (a + 2 * m9 + 1);
|
|
a0 = a1 + c9 * a0;
|
|
b0 = b1 + c9 * b0;
|
|
m9 = m9 + 1;
|
|
c9 = m9 * (b - m9) * x / (a + 2 * m9 - 1) / (a + 2 * m9);
|
|
a1 = a0 + c9 * a1;
|
|
b1 = b0 + c9 * b1;
|
|
a0 = a0 / b1;
|
|
b0 = b0 / b1;
|
|
a1 = a1 / b1;
|
|
b1 = 1;
|
|
}
|
|
|
|
return a1 / a;
|
|
}
|
|
|
|
|
|
// 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);
|
|
},
|
|
|
|
cdf: function cdf(x, n, p) {
|
|
var betacdf;
|
|
var eps = 1e-10;
|
|
|
|
if (x < 0)
|
|
return 0;
|
|
if (x >= n)
|
|
return 1;
|
|
if (p < 0 || p > 1 || n <= 0)
|
|
return NaN;
|
|
|
|
x = Math.floor(x);
|
|
var z = p;
|
|
var a = x + 1;
|
|
var b = n - x;
|
|
var s = a + b;
|
|
var bt = Math.exp(jStat.gammaln(s) - jStat.gammaln(b) -
|
|
jStat.gammaln(a) + a * Math.log(z) + b * Math.log(1 - z));
|
|
|
|
if (z < (a + 1) / (s + 2))
|
|
betacdf = bt * betinc(z, a, b, eps);
|
|
else
|
|
betacdf = 1 - bt * betinc(1 - z, b, a, eps);
|
|
|
|
return Math.round((1 - betacdf) * (1 / eps)) / (1 / eps);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
// 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);
|
|
},
|
|
|
|
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;
|
|
}
|
|
});
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
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.
|
|
|
|
return jStat.hypgeom.pdf(n - k, N, N - m, n);
|
|
}
|
|
|
|
} else if(n * 2 > N) {
|
|
// Half or less is successes.
|
|
|
|
return jStat.hypgeom.pdf(m - k, N, m, N - n);
|
|
|
|
} 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.
|
|
|
|
// 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.
|
|
|
|
// 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 < k; i++) {
|
|
// For every possible number of successes up to that observed...
|
|
|
|
while(scaledPDF > 1 && samplesDone < n) {
|
|
// Intermediate result is growing too big. Apply some of the
|
|
// normalization to shrink everything.
|
|
|
|
scaledPDF *= 1 - (m / (N - samplesDone));
|
|
|
|
// 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));
|
|
}
|
|
|
|
for(; samplesDone < n; samplesDone++) {
|
|
// Apply all the rest of the normalization
|
|
scaledPDF *= 1 - (m / (N - samplesDone));
|
|
}
|
|
|
|
// Bound answer sanely before returning.
|
|
return Math.min(1, Math.max(0, scaledPDF));
|
|
}
|
|
},
|
|
|
|
cdf: function cdf(x, N, m, n) {
|
|
// Hypergeometric CDF.
|
|
|
|
// This algorithm is due to Prof. Thomas S. Ferguson, <tom@math.ucla.edu>,
|
|
// and comes from his hypergeometric test calculator at
|
|
// <http://www.math.ucla.edu/~tom/distributions/Hypergeometric.html>.
|
|
|
|
// x = number of successes drawn
|
|
// N = population size
|
|
// m = number of successes in population
|
|
// n = number of items drawn from population
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
return 1 - jStat.hypgeom.cdf(n - x - 1, N, N - m, n);
|
|
}
|
|
|
|
} else if(n * 2 > N) {
|
|
// Half or less is successes.
|
|
|
|
return 1 - jStat.hypgeom.cdf(m - x - 1, N, m, N - n);
|
|
|
|
} 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.
|
|
|
|
// 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.
|
|
|
|
// Holds the intermediate, scaled total CDF.
|
|
var scaledCDF = 1;
|
|
|
|
// 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...
|
|
|
|
while(scaledCDF > 1 && samplesDone < n) {
|
|
// Intermediate result is growing too big. Apply some of the
|
|
// normalization to shrink everything.
|
|
|
|
var factor = 1 - (m / (N - samplesDone));
|
|
|
|
scaledPDF *= factor;
|
|
scaledCDF *= factor;
|
|
|
|
// 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));
|
|
|
|
// Add to the CDF answer.
|
|
scaledCDF += scaledPDF;
|
|
}
|
|
|
|
for(; samplesDone < n; samplesDone++) {
|
|
// Apply all the rest of the normalization
|
|
scaledCDF *= 1 - (m / (N - samplesDone));
|
|
}
|
|
|
|
// Bound answer sanely before returning.
|
|
return Math.min(1, Math.max(0, scaledCDF));
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
|
|
// 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;
|
|
}
|
|
|
|
return Math.pow(l, k) * Math.exp(-l) / jStat.factorial(k);
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
mean : function(l) {
|
|
return l;
|
|
},
|
|
|
|
variance : function(l) {
|
|
return l;
|
|
},
|
|
|
|
sampleSmall: function sampleSmall(l) {
|
|
var p = 1, k = 0, L = Math.exp(-l);
|
|
do {
|
|
k++;
|
|
p *= jStat._random_fn();
|
|
} while (p > L);
|
|
return k - 1;
|
|
},
|
|
|
|
sampleLarge: function sampleLarge(l) {
|
|
var lam = l;
|
|
var k;
|
|
var U, V, slam, loglam, a, b, invalpha, vr, us;
|
|
|
|
slam = Math.sqrt(lam);
|
|
loglam = Math.log(lam);
|
|
b = 0.931 + 2.53 * slam;
|
|
a = -0.059 + 0.02483 * b;
|
|
invalpha = 1.1239 + 1.1328 / (b - 3.4);
|
|
vr = 0.9277 - 3.6224 / (b - 2);
|
|
|
|
while (1) {
|
|
U = Math.random() - 0.5;
|
|
V = Math.random();
|
|
us = 0.5 - Math.abs(U);
|
|
k = Math.floor((2 * a / us + b) * U + lam + 0.43);
|
|
if ((us >= 0.07) && (V <= vr)) {
|
|
return k;
|
|
}
|
|
if ((k < 0) || ((us < 0.013) && (V > us))) {
|
|
continue;
|
|
}
|
|
/* log(V) == log(0.0) ok here */
|
|
/* if U==0.0 so that us==0.0, log is ok since always returns */
|
|
if ((Math.log(V) + Math.log(invalpha) - Math.log(a / (us * us) + b)) <= (-lam + k * loglam - jStat.loggam(k + 1))) {
|
|
return k;
|
|
}
|
|
}
|
|
},
|
|
|
|
sample: function sample(l) {
|
|
if (l < 10)
|
|
return this.sampleSmall(l);
|
|
else
|
|
return this.sampleLarge(l);
|
|
}
|
|
});
|
|
|
|
// 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));
|
|
}
|
|
}
|
|
},
|
|
|
|
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));
|
|
},
|
|
|
|
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)))));
|
|
}
|
|
}
|
|
},
|
|
|
|
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);
|
|
}
|
|
},
|
|
|
|
mode: function mode(a, b, c) {
|
|
return c;
|
|
},
|
|
|
|
sample: function sample(a, b, c) {
|
|
var u = jStat._random_fn();
|
|
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(a, b, c) {
|
|
return (a * a + b * b + c * c - a * b - a * c - b * c) / 18;
|
|
}
|
|
});
|
|
|
|
|
|
// extend arcsine function with static methods
|
|
jStat.extend(jStat.arcsine, {
|
|
pdf: function pdf(x, a, b) {
|
|
if (b <= a) return NaN;
|
|
|
|
return (x <= a || x >= b) ? 0 :
|
|
(2 / Math.PI) *
|
|
Math.pow(Math.pow(b - a, 2) -
|
|
Math.pow(2 * x - a - b, 2), -0.5);
|
|
},
|
|
|
|
cdf: function cdf(x, a, b) {
|
|
if (x < a)
|
|
return 0;
|
|
else if (x < b)
|
|
return (2 / Math.PI) * Math.asin(Math.sqrt((x - a)/(b - a)));
|
|
return 1;
|
|
},
|
|
|
|
inv: function(p, a, b) {
|
|
return a + (0.5 - 0.5 * Math.cos(Math.PI * p)) * (b - a);
|
|
},
|
|
|
|
mean: function mean(a, b) {
|
|
if (b <= a) return NaN;
|
|
return (a + b) / 2;
|
|
},
|
|
|
|
median: function median(a, b) {
|
|
if (b <= a) return NaN;
|
|
return (a + b) / 2;
|
|
},
|
|
|
|
mode: function mode(/*a, b*/) {
|
|
throw new Error('mode is not yet implemented');
|
|
},
|
|
|
|
sample: function sample(a, b) {
|
|
return ((a + b) / 2) + ((b - a) / 2) *
|
|
Math.sin(2 * Math.PI * jStat.uniform.sample(0, 1));
|
|
},
|
|
|
|
variance: function variance(a, b) {
|
|
if (b <= a) return NaN;
|
|
return Math.pow(b - a, 2) / 8;
|
|
}
|
|
});
|
|
|
|
|
|
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, 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);
|
|
}
|
|
},
|
|
|
|
mean: function(mu/*, b*/) {
|
|
return mu;
|
|
},
|
|
|
|
median: function(mu/*, b*/) {
|
|
return mu;
|
|
},
|
|
|
|
mode: function(mu/*, b*/) {
|
|
return mu;
|
|
},
|
|
|
|
variance: function(mu, b) {
|
|
return 2 * b * b;
|
|
},
|
|
|
|
sample: function sample(mu, b) {
|
|
var u = jStat._random_fn() - 0.5;
|
|
|
|
return mu - (b * laplaceSign(u) * Math.log(1 - (2 * Math.abs(u))));
|
|
}
|
|
});
|
|
|
|
function tukeyWprob(w, rr, cc) {
|
|
var nleg = 12;
|
|
var ihalf = 6;
|
|
|
|
var C1 = -30;
|
|
var C2 = -50;
|
|
var C3 = 60;
|
|
var bb = 8;
|
|
var wlar = 3;
|
|
var wincr1 = 2;
|
|
var wincr2 = 3;
|
|
var xleg = [
|
|
0.981560634246719250690549090149,
|
|
0.904117256370474856678465866119,
|
|
0.769902674194304687036893833213,
|
|
0.587317954286617447296702418941,
|
|
0.367831498998180193752691536644,
|
|
0.125233408511468915472441369464
|
|
];
|
|
var aleg = [
|
|
0.047175336386511827194615961485,
|
|
0.106939325995318430960254718194,
|
|
0.160078328543346226334652529543,
|
|
0.203167426723065921749064455810,
|
|
0.233492536538354808760849898925,
|
|
0.249147045813402785000562436043
|
|
];
|
|
|
|
var qsqz = w * 0.5;
|
|
|
|
// if w >= 16 then the integral lower bound (occurs for c=20)
|
|
// is 0.99999999999995 so return a value of 1.
|
|
|
|
if (qsqz >= bb)
|
|
return 1.0;
|
|
|
|
// find (f(w/2) - 1) ^ cc
|
|
// (first term in integral of hartley's form).
|
|
|
|
var pr_w = 2 * jStat.normal.cdf(qsqz, 0, 1, 1, 0) - 1; // erf(qsqz / M_SQRT2)
|
|
// if pr_w ^ cc < 2e-22 then set pr_w = 0
|
|
if (pr_w >= Math.exp(C2 / cc))
|
|
pr_w = Math.pow(pr_w, cc);
|
|
else
|
|
pr_w = 0.0;
|
|
|
|
// if w is large then the second component of the
|
|
// integral is small, so fewer intervals are needed.
|
|
|
|
var wincr;
|
|
if (w > wlar)
|
|
wincr = wincr1;
|
|
else
|
|
wincr = wincr2;
|
|
|
|
// find the integral of second term of hartley's form
|
|
// for the integral of the range for equal-length
|
|
// intervals using legendre quadrature. limits of
|
|
// integration are from (w/2, 8). two or three
|
|
// equal-length intervals are used.
|
|
|
|
// blb and bub are lower and upper limits of integration.
|
|
|
|
var blb = qsqz;
|
|
var binc = (bb - qsqz) / wincr;
|
|
var bub = blb + binc;
|
|
var einsum = 0.0;
|
|
|
|
// integrate over each interval
|
|
|
|
var cc1 = cc - 1.0;
|
|
for (var wi = 1; wi <= wincr; wi++) {
|
|
var elsum = 0.0;
|
|
var a = 0.5 * (bub + blb);
|
|
|
|
// legendre quadrature with order = nleg
|
|
|
|
var b = 0.5 * (bub - blb);
|
|
|
|
for (var jj = 1; jj <= nleg; jj++) {
|
|
var j, xx;
|
|
if (ihalf < jj) {
|
|
j = (nleg - jj) + 1;
|
|
xx = xleg[j-1];
|
|
} else {
|
|
j = jj;
|
|
xx = -xleg[j-1];
|
|
}
|
|
var c = b * xx;
|
|
var ac = a + c;
|
|
|
|
// if exp(-qexpo/2) < 9e-14,
|
|
// then doesn't contribute to integral
|
|
|
|
var qexpo = ac * ac;
|
|
if (qexpo > C3)
|
|
break;
|
|
|
|
var pplus = 2 * jStat.normal.cdf(ac, 0, 1, 1, 0);
|
|
var pminus= 2 * jStat.normal.cdf(ac, w, 1, 1, 0);
|
|
|
|
// if rinsum ^ (cc-1) < 9e-14,
|
|
// then doesn't contribute to integral
|
|
|
|
var rinsum = (pplus * 0.5) - (pminus * 0.5);
|
|
if (rinsum >= Math.exp(C1 / cc1)) {
|
|
rinsum = (aleg[j-1] * Math.exp(-(0.5 * qexpo))) * Math.pow(rinsum, cc1);
|
|
elsum += rinsum;
|
|
}
|
|
}
|
|
elsum *= (((2.0 * b) * cc) / Math.sqrt(2 * Math.PI));
|
|
einsum += elsum;
|
|
blb = bub;
|
|
bub += binc;
|
|
}
|
|
|
|
// if pr_w ^ rr < 9e-14, then return 0
|
|
pr_w += einsum;
|
|
if (pr_w <= Math.exp(C1 / rr))
|
|
return 0;
|
|
|
|
pr_w = Math.pow(pr_w, rr);
|
|
if (pr_w >= 1) // 1 was iMax was eps
|
|
return 1;
|
|
return pr_w;
|
|
}
|
|
|
|
function tukeyQinv(p, c, v) {
|
|
var p0 = 0.322232421088;
|
|
var q0 = 0.993484626060e-01;
|
|
var p1 = -1.0;
|
|
var q1 = 0.588581570495;
|
|
var p2 = -0.342242088547;
|
|
var q2 = 0.531103462366;
|
|
var p3 = -0.204231210125;
|
|
var q3 = 0.103537752850;
|
|
var p4 = -0.453642210148e-04;
|
|
var q4 = 0.38560700634e-02;
|
|
var c1 = 0.8832;
|
|
var c2 = 0.2368;
|
|
var c3 = 1.214;
|
|
var c4 = 1.208;
|
|
var c5 = 1.4142;
|
|
var vmax = 120.0;
|
|
|
|
var ps = 0.5 - 0.5 * p;
|
|
var yi = Math.sqrt(Math.log(1.0 / (ps * ps)));
|
|
var t = yi + (((( yi * p4 + p3) * yi + p2) * yi + p1) * yi + p0)
|
|
/ (((( yi * q4 + q3) * yi + q2) * yi + q1) * yi + q0);
|
|
if (v < vmax) t += (t * t * t + t) / v / 4.0;
|
|
var q = c1 - c2 * t;
|
|
if (v < vmax) q += -c3 / v + c4 * t / v;
|
|
return t * (q * Math.log(c - 1.0) + c5);
|
|
}
|
|
|
|
jStat.extend(jStat.tukey, {
|
|
cdf: function cdf(q, nmeans, df) {
|
|
// Identical implementation as the R ptukey() function as of commit 68947
|
|
var rr = 1;
|
|
var cc = nmeans;
|
|
|
|
var nlegq = 16;
|
|
var ihalfq = 8;
|
|
|
|
var eps1 = -30.0;
|
|
var eps2 = 1.0e-14;
|
|
var dhaf = 100.0;
|
|
var dquar = 800.0;
|
|
var deigh = 5000.0;
|
|
var dlarg = 25000.0;
|
|
var ulen1 = 1.0;
|
|
var ulen2 = 0.5;
|
|
var ulen3 = 0.25;
|
|
var ulen4 = 0.125;
|
|
var xlegq = [
|
|
0.989400934991649932596154173450,
|
|
0.944575023073232576077988415535,
|
|
0.865631202387831743880467897712,
|
|
0.755404408355003033895101194847,
|
|
0.617876244402643748446671764049,
|
|
0.458016777657227386342419442984,
|
|
0.281603550779258913230460501460,
|
|
0.950125098376374401853193354250e-1
|
|
];
|
|
var alegq = [
|
|
0.271524594117540948517805724560e-1,
|
|
0.622535239386478928628438369944e-1,
|
|
0.951585116824927848099251076022e-1,
|
|
0.124628971255533872052476282192,
|
|
0.149595988816576732081501730547,
|
|
0.169156519395002538189312079030,
|
|
0.182603415044923588866763667969,
|
|
0.189450610455068496285396723208
|
|
];
|
|
|
|
if (q <= 0)
|
|
return 0;
|
|
|
|
// df must be > 1
|
|
// there must be at least two values
|
|
|
|
if (df < 2 || rr < 1 || cc < 2) return NaN;
|
|
|
|
if (!Number.isFinite(q))
|
|
return 1;
|
|
|
|
if (df > dlarg)
|
|
return tukeyWprob(q, rr, cc);
|
|
|
|
// calculate leading constant
|
|
|
|
var f2 = df * 0.5;
|
|
var f2lf = ((f2 * Math.log(df)) - (df * Math.log(2))) - jStat.gammaln(f2);
|
|
var f21 = f2 - 1.0;
|
|
|
|
// integral is divided into unit, half-unit, quarter-unit, or
|
|
// eighth-unit length intervals depending on the value of the
|
|
// degrees of freedom.
|
|
|
|
var ff4 = df * 0.25;
|
|
var ulen;
|
|
if (df <= dhaf) ulen = ulen1;
|
|
else if (df <= dquar) ulen = ulen2;
|
|
else if (df <= deigh) ulen = ulen3;
|
|
else ulen = ulen4;
|
|
|
|
f2lf += Math.log(ulen);
|
|
|
|
// integrate over each subinterval
|
|
|
|
var ans = 0.0;
|
|
|
|
for (var i = 1; i <= 50; i++) {
|
|
var otsum = 0.0;
|
|
|
|
// legendre quadrature with order = nlegq
|
|
// nodes (stored in xlegq) are symmetric around zero.
|
|
|
|
var twa1 = (2 * i - 1) * ulen;
|
|
|
|
for (var jj = 1; jj <= nlegq; jj++) {
|
|
var j, t1;
|
|
if (ihalfq < jj) {
|
|
j = jj - ihalfq - 1;
|
|
t1 = (f2lf + (f21 * Math.log(twa1 + (xlegq[j] * ulen))))
|
|
- (((xlegq[j] * ulen) + twa1) * ff4);
|
|
} else {
|
|
j = jj - 1;
|
|
t1 = (f2lf + (f21 * Math.log(twa1 - (xlegq[j] * ulen))))
|
|
+ (((xlegq[j] * ulen) - twa1) * ff4);
|
|
}
|
|
|
|
// if exp(t1) < 9e-14, then doesn't contribute to integral
|
|
var qsqz;
|
|
if (t1 >= eps1) {
|
|
if (ihalfq < jj) {
|
|
qsqz = q * Math.sqrt(((xlegq[j] * ulen) + twa1) * 0.5);
|
|
} else {
|
|
qsqz = q * Math.sqrt(((-(xlegq[j] * ulen)) + twa1) * 0.5);
|
|
}
|
|
|
|
// call wprob to find integral of range portion
|
|
|
|
var wprb = tukeyWprob(qsqz, rr, cc);
|
|
var rotsum = (wprb * alegq[j]) * Math.exp(t1);
|
|
otsum += rotsum;
|
|
}
|
|
// end legendre integral for interval i
|
|
// L200:
|
|
}
|
|
|
|
// if integral for interval i < 1e-14, then stop.
|
|
// However, in order to avoid small area under left tail,
|
|
// at least 1 / ulen intervals are calculated.
|
|
if (i * ulen >= 1.0 && otsum <= eps2)
|
|
break;
|
|
|
|
// end of interval i
|
|
// L330:
|
|
|
|
ans += otsum;
|
|
}
|
|
|
|
if (otsum > eps2) { // not converged
|
|
throw new Error('tukey.cdf failed to converge');
|
|
}
|
|
if (ans > 1)
|
|
ans = 1;
|
|
return ans;
|
|
},
|
|
|
|
inv: function(p, nmeans, df) {
|
|
// Identical implementation as the R qtukey() function as of commit 68947
|
|
var rr = 1;
|
|
var cc = nmeans;
|
|
|
|
var eps = 0.0001;
|
|
var maxiter = 50;
|
|
|
|
// df must be > 1 ; there must be at least two values
|
|
if (df < 2 || rr < 1 || cc < 2) return NaN;
|
|
|
|
if (p < 0 || p > 1) return NaN;
|
|
if (p === 0) return 0;
|
|
if (p === 1) return Infinity;
|
|
|
|
// Initial value
|
|
|
|
var x0 = tukeyQinv(p, cc, df);
|
|
|
|
// Find prob(value < x0)
|
|
|
|
var valx0 = jStat.tukey.cdf(x0, nmeans, df) - p;
|
|
|
|
// Find the second iterate and prob(value < x1).
|
|
// If the first iterate has probability value
|
|
// exceeding p then second iterate is 1 less than
|
|
// first iterate; otherwise it is 1 greater.
|
|
|
|
var x1;
|
|
if (valx0 > 0.0)
|
|
x1 = Math.max(0.0, x0 - 1.0);
|
|
else
|
|
x1 = x0 + 1.0;
|
|
var valx1 = jStat.tukey.cdf(x1, nmeans, df) - p;
|
|
|
|
// Find new iterate
|
|
|
|
var ans;
|
|
for(var iter = 1; iter < maxiter; iter++) {
|
|
ans = x1 - ((valx1 * (x1 - x0)) / (valx1 - valx0));
|
|
valx0 = valx1;
|
|
|
|
// New iterate must be >= 0
|
|
|
|
x0 = x1;
|
|
if (ans < 0.0) {
|
|
ans = 0.0;
|
|
valx1 = -p;
|
|
}
|
|
// Find prob(value < new iterate)
|
|
|
|
valx1 = jStat.tukey.cdf(ans, nmeans, df) - p;
|
|
x1 = ans;
|
|
|
|
// If the difference between two successive
|
|
// iterates is less than eps, stop
|
|
|
|
var xabs = Math.abs(x1 - x0);
|
|
if (xabs < eps)
|
|
return ans;
|
|
}
|
|
|
|
throw new Error('tukey.inv failed to converge');
|
|
}
|
|
});
|
|
|
|
}(jStat, Math));
|