md`
/**
* https://script.google.com/a/even.com/d/1OKx4Ga3-16e9aSHBZnuNF4jSemTYxwO8CvVmESQaW3J2ENJDuz47iSen/edit
*/
/**
* GENERAL NOTES
*
* 1. The 'seed' parameter exists solely to bust the cache and force Google
* Sheets to recalculate the function's return value.
*/
function helpseed() {
return `seed parameter
Several of the functions accept a 'seed' parameter. This parameter is used to invalidate cached return values from previous invocations of the function.`
}
function gqpdversion() {
return 20200213
}
function gqpdproject() {
return "https://script.google.com/a/even.com/d/1OKx4Ga3-16e9aSHBZnuNF4jSemTYxwO8CvVmESQaW3J2ENJDuz47iSen/edit"
}
function gnqpdinv(quantiles, x, alpha, tail, seed) {
var d = gnqpd(dropna(unwrap(x)), alpha, tail, seed);
var quantiles = unwrap(quantiles);
var y = [];
for (var i = 0; i < quantiles.length; i++) {
var p = quantiles[i];
y.push(d.qqf(p))
}
return y
}
function helpgnpqdinv() {
return `GNQPDINV(quantiles, x, [alpha], [tail], [seed])
Return an array of values from a random variable parameterized by symmetric quantiles.
Parameters:
quantiles: An array of the desired quantiles (like {.2, .1, .5, ...}).
x: An array of specified lower bound and three quantiles that defines the distribution shape (ordered from smallest to greatest).
alpha: The distance from 0 (or 1) for the lower (or upper) symmetric quantile parameter (default: 0.1).
tail: The heaviness of the tail of the distribution (default: 0.5, number between 0 and 1).
GNQPDINV returns values from a random variable defined by a lower bound and values associated with three symmetric quantiles from the distribution. This generalized, normal quantile-parameterized distribution (GNQPD) can emulate a variety of more typical distributions, including the Beta, Normal, and Lognormal.
The x parameter is an array with four values. The first is the lower bound of values in the distribution. The remaining values are the alpha, 0.5, and 1-alpha quantiles from the distribution; alpha defauts to 0.1.
See also: https:\/\/doi.org/10.1287/deca.2018.0376`
}
/* https://doi.org/10.1287/deca.2018.0376 */
function gnqpd(x, alpha, tail, seed) {
// Use lower and upper bounds if present (fully bounded), semi-bounded if not.
var f = (x.length == 4) ? gnqpds : gnqpdb
return f(x, alpha, tail, seed);
}
// bounded
function gnqpdb(x, alpha, tail, seed) {
var d = {
x: x,
seed: seed,
}
var alpha = d.alpha = parseInt(alpha) ? parseInt(alpha) : 0.1;
var normsinv = function (a) { return normal.inv(a, 0, 1) }
var normsdist = function (a) { return normal.cdf(a, 0, 1) }
var l = d.l = x[0]
var u = d.u = x[4]
var L = d.L = normsinv((x[1]-l)/(u-l))
var B = d.B = normsinv((x[2]-l)/(u-l))
var H = d.H = normsinv((x[3]-l)/(u-l))
var n = d.n = Math.sign(L + H - 2*B)
var c = d.c = normsinv(1-alpha)
var delta = d.delta = (1/c)*Math.acosh((H-L)/(2*Math.min(B-L, H-B)))
var lambda = d.lambda = (H-L)/Math.sinh(2*delta*c)
var theta;
var theta = d.theta = {"1": L, "0": B, "-1": H}[n+""]
if (n == 0) {
d.qqf = function (p) {
return (l + (u-l) * normsdist(B + ((H-L)/(2*c))*normsinv(p)))
}
} else {
d.qqf = function (p) {
return (l + (u-l) * normsdist(theta + lambda * Math.sinh(delta*(normsinv(p) + n * c))))
}
}
return d
}
// semi-bounded
function gnqpds(x, alpha, tail, seed) {
var d = {
x: x,
seed: seed,
};
var alpha = d.alpha = parseInt(alpha) ? parseInt(alpha) : 0.1;
var tail = d.tail = parseInt(tail) ? parseInt(tail) : 0.5;
var normsinv = function (x) { return normal.inv(x, 0, 1) }
var l = d.l = x[0];
var L = d.L = Math.log(x[1] - l);
var B = d.B = Math.log(x[2] - l);
var H = d.H = Math.log(x[3] - l);
var c_alpha = d.c_alpha = normsinv(1-alpha);
var r_alpha = d.r_alpha = (L + H - (2 * B))/(H - L);
var R = d.R = (H-L)/(2 * c_alpha);
var t = (r_alpha > 0) ? (R*(1+r_alpha))+(tail*((2*R)-(R*(1+r_alpha)))) : tail*R*(1+r_alpha);
var C = d.C = t - R;
var delta = d.delta = (1/c_alpha)*Math.sinh(2 * Math.atanh((r_alpha*R)/C));
var A = d.A = B - (C/delta);
var a = d.a = delta/(Math.sqrt(Math.pow(R, 2) - Math.pow(C, 2)))
d.qqf = function (p) {
return (l + Math.exp(A + R * normsinv(p) + (C/delta) * Math.sqrt(1 + Math.pow(delta * normsinv(p), 2))))
};
return d;
}
function sample(k, seed) {
var y = [];
for (var i = 0; i < k; i++) {
y.push(Math.random());
}
return y;
}
function helpsample() {
return `SAMPLE(k, [seed])
Return an array of samples from a uniformly distributed random variable.
Parameters:
k: The number of values to sample.`
}
function poissoninv(quantiles, mu, seed) {
var y = [];
var mu = unwrap(mu);
for (var i = 0; i < quantiles.length; i++) {
y.push(poisson.sample(choice(mu, seed)))
}
return y;
}
function helppoissoninv() {
return `POISSONINV(quantiles, mu, [seed])
Return an array of values from a Poisson-distributed random variable.
Parameters:
quantiles: An array of the desired quantiles (like {.2, .01, .6, ...}).
mu: A number representing the average rate of arrival of events modeled by the distribution.`
}
function choice(x, seed) {
var i = Math.floor(Math.random() * x.length);
return x[i]
}
function helpchoice() {
return `CHOICE(x, [seed])
Return a single value from a random position in the array of values x.
Parameters:
x: An array of values (like {.1, 40, 3, 1.5, ...}).`
}
function dropna(x) {
if (!Array.isArray(x))
return x;
var x_ = [];
for (var i = 0; i < x.length; i++) {
var y = x[i];
if (y !== "") x_.push(y);
}
return x_
}
function unwrap(x) {
if (!Array.isArray(x))
return x;
// unwrap({1,2,3}) -> [[1,2,3]] -> [1,2,3]
if (x.length == 1)
return x[0];
// unwrap({1;2;3}) -> [[1],[2],[3]] -> [1,2,3]
var x_ = []
for (var i = 0; i < x.length; i++) {
x_.push(x[i][0])
}
return x_
}
function helpunwrap() {
return `UNWRAP(x)
Return the array x arranged on a single dimension.
Parameters:
x: A multi-dimensional array of arrays.`
}
`