@numero-uno-jpn/rstats 0.0.0-stage → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/LICENSE +21 -0
- package/README.ja.md +181 -0
- package/README.md +172 -2
- package/THIRD_PARTY_NOTICES.md +297 -0
- package/dist/distributions/beta.d.ts +62 -0
- package/dist/distributions/beta.js +246 -0
- package/dist/distributions/binomial.d.ts +41 -0
- package/dist/distributions/binomial.js +136 -0
- package/dist/distributions/cauchy.d.ts +34 -0
- package/dist/distributions/cauchy.js +139 -0
- package/dist/distributions/chisq.d.ts +42 -0
- package/dist/distributions/chisq.js +60 -0
- package/dist/distributions/exponential.d.ts +34 -0
- package/dist/distributions/exponential.js +93 -0
- package/dist/distributions/f.d.ts +47 -0
- package/dist/distributions/f.js +214 -0
- package/dist/distributions/gamma.d.ts +61 -0
- package/dist/distributions/gamma.js +188 -0
- package/dist/distributions/geometric.d.ts +38 -0
- package/dist/distributions/geometric.js +116 -0
- package/dist/distributions/hypergeometric.d.ts +38 -0
- package/dist/distributions/hypergeometric.js +234 -0
- package/dist/distributions/logistic.d.ts +36 -0
- package/dist/distributions/logistic.js +94 -0
- package/dist/distributions/lognormal.d.ts +39 -0
- package/dist/distributions/lognormal.js +158 -0
- package/dist/distributions/nbeta.d.ts +7 -0
- package/dist/distributions/nbeta.js +312 -0
- package/dist/distributions/nchisq.d.ts +3 -0
- package/dist/distributions/nchisq.js +217 -0
- package/dist/distributions/negbinomial.d.ts +54 -0
- package/dist/distributions/negbinomial.js +325 -0
- package/dist/distributions/normal.d.ts +66 -0
- package/dist/distributions/normal.js +184 -0
- package/dist/distributions/nt.d.ts +3 -0
- package/dist/distributions/nt.js +491 -0
- package/dist/distributions/poisson.d.ts +41 -0
- package/dist/distributions/poisson.js +104 -0
- package/dist/distributions/signrank.d.ts +38 -0
- package/dist/distributions/signrank.js +216 -0
- package/dist/distributions/t.d.ts +49 -0
- package/dist/distributions/t.js +235 -0
- package/dist/distributions/tukey.d.ts +43 -0
- package/dist/distributions/tukey.js +985 -0
- package/dist/distributions/uniform.d.ts +36 -0
- package/dist/distributions/uniform.js +90 -0
- package/dist/distributions/weibull.d.ts +37 -0
- package/dist/distributions/weibull.js +125 -0
- package/dist/distributions/wilcox.d.ts +47 -0
- package/dist/distributions/wilcox.js +270 -0
- package/dist/htest/chisqTest.d.ts +55 -0
- package/dist/htest/chisqTest.js +160 -0
- package/dist/htest/cor.d.ts +81 -0
- package/dist/htest/cor.js +436 -0
- package/dist/htest/fisherTest.d.ts +48 -0
- package/dist/htest/fisherTest.js +468 -0
- package/dist/htest/pAdjust.d.ts +34 -0
- package/dist/htest/pAdjust.js +147 -0
- package/dist/htest/tTest.d.ts +57 -0
- package/dist/htest/tTest.js +153 -0
- package/dist/htest/types.d.ts +49 -0
- package/dist/htest/types.js +20 -0
- package/dist/htest/wilcoxTest.d.ts +61 -0
- package/dist/htest/wilcoxTest.js +471 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +50 -0
- package/dist/internal/ddexp.d.ts +19 -0
- package/dist/internal/ddexp.js +94 -0
- package/dist/internal/discrete.d.ts +14 -0
- package/dist/internal/discrete.js +85 -0
- package/dist/internal/dpq.d.ts +43 -0
- package/dist/internal/dpq.js +54 -0
- package/dist/internal/integer.d.ts +4 -0
- package/dist/internal/integer.js +17 -0
- package/dist/internal/invert.d.ts +36 -0
- package/dist/internal/invert.js +124 -0
- package/dist/internal/noncentral.d.ts +12 -0
- package/dist/internal/noncentral.js +2 -0
- package/dist/internal/poissonMixture.d.ts +31 -0
- package/dist/internal/poissonMixture.js +401 -0
- package/dist/internal/summary.d.ts +21 -0
- package/dist/internal/summary.js +125 -0
- package/dist/internal/uniroot.d.ts +11 -0
- package/dist/internal/uniroot.js +75 -0
- package/dist/internal/vectorize.d.ts +12 -0
- package/dist/internal/vectorize.js +7 -0
- package/dist/linalg/basic.d.ts +95 -0
- package/dist/linalg/basic.js +152 -0
- package/dist/linalg/chol.d.ts +83 -0
- package/dist/linalg/chol.js +200 -0
- package/dist/linalg/eigen.d.ts +52 -0
- package/dist/linalg/eigen.js +753 -0
- package/dist/linalg/kappa.d.ts +51 -0
- package/dist/linalg/kappa.js +199 -0
- package/dist/linalg/matrix.d.ts +29 -0
- package/dist/linalg/matrix.js +65 -0
- package/dist/linalg/norm.d.ts +32 -0
- package/dist/linalg/norm.js +92 -0
- package/dist/linalg/qr.d.ts +264 -0
- package/dist/linalg/qr.js +494 -0
- package/dist/linalg/solve.d.ts +123 -0
- package/dist/linalg/solve.js +380 -0
- package/dist/linalg/svd.d.ts +38 -0
- package/dist/linalg/svd.js +275 -0
- package/dist/random/as241.d.ts +2 -0
- package/dist/random/as241.js +58 -0
- package/dist/random/beta.d.ts +14 -0
- package/dist/random/beta.js +117 -0
- package/dist/random/binomial.d.ts +25 -0
- package/dist/random/binomial.js +224 -0
- package/dist/random/common.d.ts +4 -0
- package/dist/random/common.js +35 -0
- package/dist/random/continuous.d.ts +91 -0
- package/dist/random/continuous.js +138 -0
- package/dist/random/gamma.d.ts +52 -0
- package/dist/random/gamma.js +217 -0
- package/dist/random/hyper.d.ts +12 -0
- package/dist/random/hyper.js +240 -0
- package/dist/random/mt.d.ts +16 -0
- package/dist/random/mt.js +97 -0
- package/dist/random/poisson.d.ts +33 -0
- package/dist/random/poisson.js +204 -0
- package/dist/random/rank.d.ts +19 -0
- package/dist/random/rank.js +62 -0
- package/dist/random/rng.d.ts +46 -0
- package/dist/random/rng.js +148 -0
- package/dist/random/sample.d.ts +45 -0
- package/dist/random/sample.js +255 -0
- package/dist/rstats.js +2 -0
- package/dist/special/beta.d.ts +30 -0
- package/dist/special/beta.js +152 -0
- package/dist/special/constants.d.ts +16 -0
- package/dist/special/constants.js +19 -0
- package/dist/special/dd.d.ts +29 -0
- package/dist/special/dd.js +693 -0
- package/dist/special/erf.d.ts +9 -0
- package/dist/special/erf.js +85 -0
- package/dist/special/gamma.d.ts +32 -0
- package/dist/special/gamma.js +270 -0
- package/dist/special/igam.d.ts +11 -0
- package/dist/special/igam.js +291 -0
- package/dist/special/igamAsympCoeff.d.ts +4 -0
- package/dist/special/igamAsympCoeff.js +233 -0
- package/dist/special/igami.d.ts +2 -0
- package/dist/special/igami.js +135 -0
- package/dist/special/incbet.d.ts +25 -0
- package/dist/special/incbet.js +326 -0
- package/dist/special/lgammaDiff.d.ts +2 -0
- package/dist/special/lgammaDiff.js +34 -0
- package/dist/special/loader.d.ts +43 -0
- package/dist/special/loader.js +456 -0
- package/dist/special/log1pmx.d.ts +2 -0
- package/dist/special/log1pmx.js +20 -0
- package/dist/special/ndtri.d.ts +4 -0
- package/dist/special/ndtri.js +82 -0
- package/dist/special/polevl.d.ts +4 -0
- package/dist/special/polevl.js +17 -0
- package/package.json +68 -4
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
// Negative binomial distribution dnbinom / pnbinom / qnbinom (size and prob, or size and mu).
|
|
2
|
+
//
|
|
3
|
+
// Probabilities use the relation to the binomial probability (Loader 2000)
|
|
4
|
+
// P[X = x] = Γ(x + n) / (Γ(n) x!) p^n q^x = n / (n + x) · b(n; n + x, p)
|
|
5
|
+
// where b is dbinomRawDD from src/special/loader.ts (n need not be an integer).
|
|
6
|
+
// The distribution function is P[X <= x] = I_p(n, x + 1) (regularized incomplete beta function).
|
|
7
|
+
// When mu is given, p = n / (n + mu) and q = mu / (n + mu) are formed in double-double
|
|
8
|
+
// (for n ≫ mu, rounding 1 - p gives q a large relative error). size = Inf is the Poisson distribution.
|
|
9
|
+
// Quantiles use an integer search from a Cornish-Fisher starting value.
|
|
10
|
+
import { vectorize } from "../internal/vectorize.js";
|
|
11
|
+
import { d0, dt0, dt1, quantileBoundaries } from "../internal/dpq.js";
|
|
12
|
+
import { discreteQuantile } from "../internal/discrete.js";
|
|
13
|
+
import { nonInt } from "../internal/integer.js";
|
|
14
|
+
import { betaIncDD } from "../special/incbet.js";
|
|
15
|
+
import { dbinomRawDD, dpoisRaw } from "../special/loader.js";
|
|
16
|
+
import { ddAdd, ddDiv, ddMul, twoSum } from "../special/dd.js";
|
|
17
|
+
import { qnorm } from "./normal.js";
|
|
18
|
+
import { qgamma1 } from "./gamma.js";
|
|
19
|
+
import { ppois1, qpois1 } from "./poisson.js";
|
|
20
|
+
import { probPair } from "./geometric.js";
|
|
21
|
+
function fromMu(size, mu) {
|
|
22
|
+
const s = twoSum(size, mu);
|
|
23
|
+
return { size, pd: ddDiv([size, 0], s), qd: ddDiv([mu, 0], s) };
|
|
24
|
+
}
|
|
25
|
+
function fromProb(size, prob) {
|
|
26
|
+
return { size, ...probPair(prob) };
|
|
27
|
+
}
|
|
28
|
+
/** P[X = x] (x a nonnegative integer, 0 < size < ∞, 0 < p < 1) */
|
|
29
|
+
function dnbinomRaw(x, par, log) {
|
|
30
|
+
const { size, pd, qd } = par;
|
|
31
|
+
const b = dbinomRawDD(size, x, pd, qd, log);
|
|
32
|
+
if (x === 0)
|
|
33
|
+
return b;
|
|
34
|
+
const r = size / (size + x);
|
|
35
|
+
return log ? b + Math.log(r) : b * r;
|
|
36
|
+
}
|
|
37
|
+
/** P[X <= x] (lower) / P[X > x] (!lower) (x a nonnegative integer) */
|
|
38
|
+
function pnbinomRaw(x, par, lower, log) {
|
|
39
|
+
const up = upperSeries(x, par);
|
|
40
|
+
if (up !== undefined) {
|
|
41
|
+
// The upper tail (the smaller side) was computed directly by the series
|
|
42
|
+
if (!lower)
|
|
43
|
+
return log ? Math.log(up) : up;
|
|
44
|
+
return log ? Math.log1p(-up) : 1 - up;
|
|
45
|
+
}
|
|
46
|
+
return betaIncDD(par.size, x + 1, par.pd, par.qd, lower, log);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* For size <= 1 with q close to 1, compute the upper probability P[X > x] = I_q(x + 1, size) directly by the
|
|
50
|
+
* positive-term series
|
|
51
|
+
* I_z(A, B) = z^A (1 - z)^B / (A B(A, B)) [1 + Σ_{k>=1} Π_{j<k} (A + B + j) / (A + 1 + j) z^k]
|
|
52
|
+
* (z = q, A = x + 1, B = size). In this region the incomplete beta continued fraction is off by about 3e-14
|
|
53
|
+
* relative near the mean. All terms are positive, so there is no cancellation. The number of terms is about
|
|
54
|
+
* -log(1e-17) / -log z. Returns undefined when not usable (too many terms, or the value exceeds 1/2)
|
|
55
|
+
*/
|
|
56
|
+
function upperSeries(x, par) {
|
|
57
|
+
const { size, pd, qd } = par;
|
|
58
|
+
const z = qd[0] + qd[1];
|
|
59
|
+
const p = pd[0] + pd[1];
|
|
60
|
+
if (!(size <= 1) || !(z >= 0.95) || p < 1e-4)
|
|
61
|
+
return undefined;
|
|
62
|
+
const A = x + 1;
|
|
63
|
+
const B = size;
|
|
64
|
+
// Prefactor z^A (1 - z)^B / (A B(A, B)) = b(A; A + B, z) B / (A + B)
|
|
65
|
+
const pre = (dbinomRawDD(A, B, qd, pd, false) * B) / (A + B);
|
|
66
|
+
if (!(pre > 1e-290))
|
|
67
|
+
return undefined;
|
|
68
|
+
// Rounding of the term ratios accumulates with the number of terms (1e-12 over tens of thousands), so keep terms and sum in double-double
|
|
69
|
+
const ab = twoSum(A, B);
|
|
70
|
+
let term = [1, 0];
|
|
71
|
+
let sum = [1, 0];
|
|
72
|
+
for (let k = 0; k < 1e6; k++) {
|
|
73
|
+
const ratio = ddDiv(ddAdd(ab, [k, 0]), [A + 1 + k, 0]);
|
|
74
|
+
term = ddMul(ddMul(term, ratio), qd);
|
|
75
|
+
sum = ddAdd(sum, term);
|
|
76
|
+
if (term[0] < 1e-18 * sum[0]) {
|
|
77
|
+
const v = pre * (sum[0] + sum[1]);
|
|
78
|
+
return v <= 0.5 ? v : undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
function quantileRaw(p, par, lowerTail, logP) {
|
|
84
|
+
// Starting value from the Cornish-Fisher expansion
|
|
85
|
+
const prob = par.pd[0] + par.pd[1];
|
|
86
|
+
const Q = 1 / prob;
|
|
87
|
+
const P = (par.qd[0] + par.qd[1]) * Q;
|
|
88
|
+
const mu = par.size * P;
|
|
89
|
+
const sigma = Math.sqrt(par.size * P * Q);
|
|
90
|
+
const gamma = (Q + P) / sigma;
|
|
91
|
+
let y0;
|
|
92
|
+
if (prob < 0.05) {
|
|
93
|
+
// For small p, X (-log q) is nearly gamma (shape = size). Even for highly skewed distributions (small size)
|
|
94
|
+
// this is a much closer starting value than Cornish-Fisher, reducing distribution-function evaluations
|
|
95
|
+
// -log q = log(1 + p/q) (taking log of a rounded q loses digits for small p)
|
|
96
|
+
y0 = qgamma1(p, par.size, 1, lowerTail, logP) / Math.log1p(prob / (par.qd[0] + par.qd[1]));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const z = qnorm(p, { lowerTail, logP });
|
|
100
|
+
y0 = mu + sigma * (z + (gamma * (z * z - 1)) / 6);
|
|
101
|
+
}
|
|
102
|
+
let start = Number.isFinite(y0) ? Math.max(y0, 0) : Number.isFinite(mu) ? mu : 0;
|
|
103
|
+
if (start > 1e4)
|
|
104
|
+
start = refineContinuous(start, p, par, lowerTail, logP);
|
|
105
|
+
return discreteQuantile({
|
|
106
|
+
p,
|
|
107
|
+
lowerTail,
|
|
108
|
+
logP,
|
|
109
|
+
cdf: (y, l, lg) => pnbinomRaw(y, par, l, lg),
|
|
110
|
+
y0: start,
|
|
111
|
+
lo: 0,
|
|
112
|
+
hi: Infinity,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Before the integer search, refine the starting value by Newton's method treating y as real.
|
|
117
|
+
* The discrete search widens from step 1 by doubling, so a starting value off by 1e15 units would need more than
|
|
118
|
+
* 100 distribution-function evaluations. Solves for the log probability of the smaller tail. The final integer
|
|
119
|
+
* search determines the answer, so this only needs to be close
|
|
120
|
+
*/
|
|
121
|
+
function refineContinuous(y0, p, par, lowerTail, logP) {
|
|
122
|
+
// Use the same slack-adjusted target as the integer search (src/internal/discrete.ts)
|
|
123
|
+
// (when p is close to 1, the slack changes the opposite-tail probability considerably)
|
|
124
|
+
const E = Number.EPSILON;
|
|
125
|
+
let t = p;
|
|
126
|
+
if (logP)
|
|
127
|
+
t = lowerTail ? p * (1 + 2 * E) : p * (1 - 2 * E);
|
|
128
|
+
else if (lowerTail)
|
|
129
|
+
t = p * (1 - 8 * E);
|
|
130
|
+
else if (1 - p > 32 * E)
|
|
131
|
+
t = p * (1 + 8 * E);
|
|
132
|
+
const lp = logP ? t : Math.log(t);
|
|
133
|
+
// Switch to the smaller tail
|
|
134
|
+
const useLower = lp <= -Math.LN2 ? lowerTail : !lowerTail;
|
|
135
|
+
const target = lp <= -Math.LN2 ? lp : logP ? Math.log(-Math.expm1(t)) : Math.log1p(-t);
|
|
136
|
+
// First step: Newton's method with the slope approximated by the probability mass function; then the secant method
|
|
137
|
+
let y = y0;
|
|
138
|
+
let r = pnbinomRaw(y, par, useLower, true) - target;
|
|
139
|
+
let yPrev = NaN;
|
|
140
|
+
let rPrev = NaN;
|
|
141
|
+
for (let i = 0; i < 8 && r !== 0; i++) {
|
|
142
|
+
let next;
|
|
143
|
+
if (Number.isNaN(yPrev) || r === rPrev) {
|
|
144
|
+
const slope = (useLower ? 1 : -1) * Math.exp(dnbinomRaw(y, par, true) - (r + target));
|
|
145
|
+
next = y - r / slope;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
next = y - (r * (y - yPrev)) / (r - rPrev);
|
|
149
|
+
}
|
|
150
|
+
if (!Number.isFinite(next) || next <= 0)
|
|
151
|
+
break;
|
|
152
|
+
if (Math.abs(next - y) < 0.5)
|
|
153
|
+
return next;
|
|
154
|
+
yPrev = y;
|
|
155
|
+
rPrev = r;
|
|
156
|
+
y = next;
|
|
157
|
+
r = pnbinomRaw(y, par, useLower, true) - target;
|
|
158
|
+
}
|
|
159
|
+
return y;
|
|
160
|
+
}
|
|
161
|
+
// ---- Parameterized by prob ----
|
|
162
|
+
export function dnbinom1(x, size, prob, log) {
|
|
163
|
+
if (Number.isNaN(x) || Number.isNaN(size) || Number.isNaN(prob))
|
|
164
|
+
return NaN;
|
|
165
|
+
if (size < 0 || !(prob > 0) || prob > 1)
|
|
166
|
+
return NaN;
|
|
167
|
+
if (nonInt(x) || x < 0 || !Number.isFinite(x))
|
|
168
|
+
return d0(log);
|
|
169
|
+
const n = Math.round(x);
|
|
170
|
+
if (size === 0 || prob === 1)
|
|
171
|
+
return n === 0 ? (log ? 0 : 1) : d0(log);
|
|
172
|
+
if (size === Infinity)
|
|
173
|
+
return d0(log);
|
|
174
|
+
return dnbinomRaw(n, fromProb(size, prob), log);
|
|
175
|
+
}
|
|
176
|
+
export function pnbinom1(q, size, prob, lowerTail, logP) {
|
|
177
|
+
if (Number.isNaN(q) || Number.isNaN(size) || Number.isNaN(prob))
|
|
178
|
+
return NaN;
|
|
179
|
+
if (size < 0 || !(prob > 0) || prob > 1 || size === Infinity)
|
|
180
|
+
return NaN;
|
|
181
|
+
if (q < 0)
|
|
182
|
+
return dt0(lowerTail, logP);
|
|
183
|
+
if (size === 0 || prob === 1 || !Number.isFinite(q))
|
|
184
|
+
return dt1(lowerTail, logP);
|
|
185
|
+
return pnbinomRaw(Math.floor(q + 1e-7), fromProb(size, prob), lowerTail, logP);
|
|
186
|
+
}
|
|
187
|
+
export function qnbinom1(p, size, prob, lowerTail, logP) {
|
|
188
|
+
if (Number.isNaN(p) || Number.isNaN(size) || Number.isNaN(prob))
|
|
189
|
+
return NaN;
|
|
190
|
+
// Same order as R: parameter range → point mass (prob = 1 or size = 0 gives 0 regardless of p) → prob = 0 → range of p
|
|
191
|
+
if (size < 0 || prob < 0 || prob > 1)
|
|
192
|
+
return NaN;
|
|
193
|
+
if (prob === 1 || size === 0)
|
|
194
|
+
return 0;
|
|
195
|
+
if (prob === 0)
|
|
196
|
+
return NaN;
|
|
197
|
+
const b = quantileBoundaries(p, 0, Infinity, lowerTail, logP);
|
|
198
|
+
if (b !== undefined)
|
|
199
|
+
return b;
|
|
200
|
+
// size = Inf (prob < 1) has its mass at X = ∞ (R gives Inf or NaN depending on p)
|
|
201
|
+
if (size === Infinity)
|
|
202
|
+
return Infinity;
|
|
203
|
+
return quantileRaw(p, fromProb(size, prob), lowerTail, logP);
|
|
204
|
+
}
|
|
205
|
+
// ---- Parameterized by mu ----
|
|
206
|
+
export function dnbinomMu1(x, size, mu, log) {
|
|
207
|
+
if (Number.isNaN(x) || Number.isNaN(size) || Number.isNaN(mu))
|
|
208
|
+
return NaN;
|
|
209
|
+
if (size < 0 || mu < 0)
|
|
210
|
+
return NaN;
|
|
211
|
+
if (nonInt(x) || x < 0 || !Number.isFinite(x))
|
|
212
|
+
return d0(log);
|
|
213
|
+
const n = Math.round(x);
|
|
214
|
+
if (size === 0) {
|
|
215
|
+
if (n === 0)
|
|
216
|
+
return log ? 0 : 1;
|
|
217
|
+
return mu === 0 ? NaN : d0(log);
|
|
218
|
+
}
|
|
219
|
+
if (mu === Infinity)
|
|
220
|
+
return d0(log);
|
|
221
|
+
if (size === Infinity)
|
|
222
|
+
return dpoisRaw(n, mu, log);
|
|
223
|
+
if (mu === 0)
|
|
224
|
+
return n === 0 ? (log ? 0 : 1) : d0(log);
|
|
225
|
+
return dnbinomRaw(n, fromMu(size, mu), log);
|
|
226
|
+
}
|
|
227
|
+
export function pnbinomMu1(q, size, mu, lowerTail, logP) {
|
|
228
|
+
if (Number.isNaN(q) || Number.isNaN(size) || Number.isNaN(mu))
|
|
229
|
+
return NaN;
|
|
230
|
+
if (size < 0 || mu < 0 || mu === Infinity)
|
|
231
|
+
return NaN;
|
|
232
|
+
if (q < 0)
|
|
233
|
+
return dt0(lowerTail, logP);
|
|
234
|
+
if (size === 0 || mu === 0 || !Number.isFinite(q))
|
|
235
|
+
return dt1(lowerTail, logP);
|
|
236
|
+
if (size === Infinity)
|
|
237
|
+
return ppois1(q, mu, lowerTail, logP);
|
|
238
|
+
return pnbinomRaw(Math.floor(q + 1e-7), fromMu(size, mu), lowerTail, logP);
|
|
239
|
+
}
|
|
240
|
+
export function qnbinomMu1(p, size, mu, lowerTail, logP) {
|
|
241
|
+
if (Number.isNaN(p) || Number.isNaN(size) || Number.isNaN(mu))
|
|
242
|
+
return NaN;
|
|
243
|
+
// As in R, size = Inf is the Poisson distribution, and size = 0 or mu = 0 gives 0 regardless of the other parameter or p
|
|
244
|
+
if (size === Infinity)
|
|
245
|
+
return qpois1(p, mu, lowerTail, logP);
|
|
246
|
+
if (size === 0 || mu === 0)
|
|
247
|
+
return 0;
|
|
248
|
+
if (size < 0 || mu < 0)
|
|
249
|
+
return NaN;
|
|
250
|
+
// mu = Inf answers only at the ends (p = 0, 1) and is NaN otherwise (as in R)
|
|
251
|
+
if (mu === Infinity)
|
|
252
|
+
return quantileBoundaries(p, 0, Infinity, lowerTail, logP) ?? NaN;
|
|
253
|
+
const b = quantileBoundaries(p, 0, Infinity, lowerTail, logP);
|
|
254
|
+
if (b !== undefined)
|
|
255
|
+
return b;
|
|
256
|
+
return quantileRaw(p, fromMu(size, mu), lowerTail, logP);
|
|
257
|
+
}
|
|
258
|
+
/** Accept exactly one of prob and mu (as in R, giving both or neither is an error) */
|
|
259
|
+
function which(options) {
|
|
260
|
+
const { prob, mu } = options;
|
|
261
|
+
if (prob !== undefined && mu !== undefined)
|
|
262
|
+
throw new TypeError("specify exactly one of prob and mu");
|
|
263
|
+
if (prob !== undefined)
|
|
264
|
+
return { kind: "prob", v: prob };
|
|
265
|
+
if (mu !== undefined)
|
|
266
|
+
return { kind: "mu", v: mu };
|
|
267
|
+
throw new TypeError("specify exactly one of prob and mu");
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Probability mass function of the negative binomial distribution (number of failures before the `size`-th success). Equivalent to R's `dnbinom(x, size, prob, mu, log = FALSE)`.
|
|
271
|
+
*
|
|
272
|
+
* The distribution is parameterized by `size` and exactly one of `prob` or `mu`. With `mu`, `size = Infinity` gives the Poisson distribution.
|
|
273
|
+
*
|
|
274
|
+
* @throws TypeError unless exactly one of `prob` and `mu` is given.
|
|
275
|
+
*
|
|
276
|
+
* @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
|
|
277
|
+
* @param size - Target number of successes (need not be an integer).
|
|
278
|
+
* @param options - `prob` or `mu` (see {@link NegBinomialParams}; exactly one is required) and `log` (see {@link DensityOptions}).
|
|
279
|
+
* @returns The probability (or log probability).
|
|
280
|
+
*/
|
|
281
|
+
export function dnbinom(x, size, options) {
|
|
282
|
+
const log = options.log ?? false;
|
|
283
|
+
const w = which(options);
|
|
284
|
+
return w.kind === "prob"
|
|
285
|
+
? vectorize(x, (v) => dnbinom1(v, size, w.v, log))
|
|
286
|
+
: vectorize(x, (v) => dnbinomMu1(v, size, w.v, log));
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Distribution function of the negative binomial distribution (number of failures before the `size`-th success). Equivalent to R's `pnbinom(q, size, prob, mu, lower.tail = TRUE, log.p = FALSE)`.
|
|
290
|
+
*
|
|
291
|
+
* The distribution is parameterized by `size` and exactly one of `prob` or `mu`. With `mu`, `size = Infinity` gives the Poisson distribution.
|
|
292
|
+
*
|
|
293
|
+
* @throws TypeError unless exactly one of `prob` and `mu` is given.
|
|
294
|
+
*
|
|
295
|
+
* @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
|
|
296
|
+
* @param size - Target number of successes (need not be an integer).
|
|
297
|
+
* @param options - `prob` or `mu` (see {@link NegBinomialParams}; exactly one is required), `lowerTail` and `logP` (see {@link TailOptions}).
|
|
298
|
+
* @returns The probability (or log probability).
|
|
299
|
+
*/
|
|
300
|
+
export function pnbinom(q, size, options) {
|
|
301
|
+
const { lowerTail = true, logP = false } = options;
|
|
302
|
+
const w = which(options);
|
|
303
|
+
return w.kind === "prob"
|
|
304
|
+
? vectorize(q, (v) => pnbinom1(v, size, w.v, lowerTail, logP))
|
|
305
|
+
: vectorize(q, (v) => pnbinomMu1(v, size, w.v, lowerTail, logP));
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Quantile function of the negative binomial distribution (number of failures before the `size`-th success). Equivalent to R's `qnbinom(p, size, prob, mu, lower.tail = TRUE, log.p = FALSE)`.
|
|
309
|
+
*
|
|
310
|
+
* The distribution is parameterized by `size` and exactly one of `prob` or `mu`. With `mu`, `size = Infinity` gives the Poisson distribution.
|
|
311
|
+
*
|
|
312
|
+
* @throws TypeError unless exactly one of `prob` and `mu` is given.
|
|
313
|
+
*
|
|
314
|
+
* @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
|
|
315
|
+
* @param size - Target number of successes (need not be an integer).
|
|
316
|
+
* @param options - `prob` or `mu` (see {@link NegBinomialParams}; exactly one is required), `lowerTail` and `logP` (see {@link TailOptions}).
|
|
317
|
+
* @returns The quantile(s).
|
|
318
|
+
*/
|
|
319
|
+
export function qnbinom(p, size, options) {
|
|
320
|
+
const { lowerTail = true, logP = false } = options;
|
|
321
|
+
const w = which(options);
|
|
322
|
+
return w.kind === "prob"
|
|
323
|
+
? vectorize(p, (v) => qnbinom1(v, size, w.v, lowerTail, logP))
|
|
324
|
+
: vectorize(p, (v) => qnbinomMu1(v, size, w.v, lowerTail, logP));
|
|
325
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type Vectorizable, type VectorResult } from "../internal/vectorize.js";
|
|
2
|
+
/** Options for {@link pnorm} and {@link qnorm}. */
|
|
3
|
+
export interface NormalOptions {
|
|
4
|
+
/** Mean. Default `0`. */
|
|
5
|
+
mean?: number;
|
|
6
|
+
/** Standard deviation. Default `1`. */
|
|
7
|
+
sd?: number;
|
|
8
|
+
/** If `true`, probabilities are P[X <= x]; if `false`, P[X > x]. Default `true`. */
|
|
9
|
+
lowerTail?: boolean;
|
|
10
|
+
/** If `true`, probabilities are given (to `qnorm`) and returned (from `pnorm`) as log(p). Default `false`. */
|
|
11
|
+
logP?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Density of the normal distribution. Equivalent to R's `dnorm(x, mean = 0, sd = 1, log = FALSE)`.
|
|
15
|
+
*
|
|
16
|
+
* @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
|
|
17
|
+
* @param options - Distribution parameters and output scale.
|
|
18
|
+
* @param options.mean - Mean. Default `0`.
|
|
19
|
+
* @param options.sd - Standard deviation. Default `1`.
|
|
20
|
+
* @param options.log - If `true`, return the log density. Default `false`.
|
|
21
|
+
* @returns The density (or log density).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* dnorm(0); // 0.3989422804014327
|
|
26
|
+
* dnorm([-1, 0, 1]); // [0.24197072451914337, 0.3989422804014327, 0.24197072451914337]
|
|
27
|
+
* dnorm(1, { mean: 1, sd: 2, log: true }); // -1.612085713764618
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare function dnorm<T extends Vectorizable>(x: T, options?: {
|
|
31
|
+
mean?: number;
|
|
32
|
+
sd?: number;
|
|
33
|
+
log?: boolean;
|
|
34
|
+
}): VectorResult<T>;
|
|
35
|
+
/**
|
|
36
|
+
* Distribution function of the normal distribution.
|
|
37
|
+
* Equivalent to R's `pnorm(q, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)`.
|
|
38
|
+
*
|
|
39
|
+
* @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
|
|
40
|
+
* @param options - `mean`, `sd`, `lowerTail` and `logP` (see {@link NormalOptions}).
|
|
41
|
+
* @returns The probability (or log probability).
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* pnorm(1.96); // 0.9750021048517795
|
|
46
|
+
* pnorm(1.96, { lowerTail: false }); // 0.024997895148220435
|
|
47
|
+
* pnorm(new Float64Array([-1, 0, 1])); // Float64Array [0.15865525393145707, 0.5, 0.8413447460685429]
|
|
48
|
+
* pnorm(-40, { logP: true }); // -804.6084420137538
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function pnorm<T extends Vectorizable>(q: T, options?: NormalOptions): VectorResult<T>;
|
|
52
|
+
/**
|
|
53
|
+
* Quantile function of the normal distribution.
|
|
54
|
+
* Equivalent to R's `qnorm(p, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)`.
|
|
55
|
+
*
|
|
56
|
+
* @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
|
|
57
|
+
* @param options - `mean`, `sd`, `lowerTail` and `logP` (see {@link NormalOptions}).
|
|
58
|
+
* @returns The quantile(s).
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* qnorm(0.975); // 1.959963984540054
|
|
63
|
+
* qnorm(0.05, { mean: 100, sd: 15 }); // 75.32719559572791
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function qnorm<T extends Vectorizable>(p: T, options?: NormalOptions): VectorResult<T>;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// Normal distribution function pnorm and quantile function qnorm.
|
|
2
|
+
//
|
|
3
|
+
// The building blocks are ports of xsf (Cephes) erf / erfc / ndtri / ndtri_exp
|
|
4
|
+
// (src/special/). Argument handling (NaN, ±Inf, sd <= 0, lowerTail, logP) is matched to
|
|
5
|
+
// R's output (test/fixtures/). R's source code was not consulted.
|
|
6
|
+
import { erf, erfcxNonNegative } from "../special/erf.js";
|
|
7
|
+
import { ndtri, ndtriExp } from "../special/ndtri.js";
|
|
8
|
+
import { vectorize } from "../internal/vectorize.js";
|
|
9
|
+
const SQRT1_2 = Math.SQRT1_2;
|
|
10
|
+
/**
|
|
11
|
+
* exp(-a^2 / 2) (a >= 0) multiplied by scale.
|
|
12
|
+
*
|
|
13
|
+
* Computing a^2 directly lets its rounding error be amplified by the exponential, reaching a relative error of
|
|
14
|
+
* 1e-13 around a = 38. Split a = hi + lo (hi on a 1/16 grid so hi^2 is exact) and compute
|
|
15
|
+
* exp(-hi^2/2) * exp(-lo (a + hi) / 2).
|
|
16
|
+
* Where the result is subnormal, rounding happens only in the final multiplication.
|
|
17
|
+
*/
|
|
18
|
+
function scaledExpNegHalfSq(a, scale) {
|
|
19
|
+
const hi = Math.floor(a * 16) / 16;
|
|
20
|
+
const lo = a - hi;
|
|
21
|
+
const halfHiSq = (hi * hi) / 2;
|
|
22
|
+
const m = scale * Math.exp((-lo * (a + hi)) / 2);
|
|
23
|
+
if (halfHiSq <= 700)
|
|
24
|
+
return m * Math.exp(-halfHiSq);
|
|
25
|
+
return m * Math.exp(700 - halfHiSq) * Math.exp(-700);
|
|
26
|
+
}
|
|
27
|
+
/** Lower probability P[Z <= t] of the standard normal (t <= 0) */
|
|
28
|
+
function lowerTailProb(t) {
|
|
29
|
+
const a = -t;
|
|
30
|
+
if (a < Math.SQRT2)
|
|
31
|
+
return 0.5 - 0.5 * erf(a * SQRT1_2);
|
|
32
|
+
return scaledExpNegHalfSq(a, 0.5 * erfcxNonNegative(a * SQRT1_2));
|
|
33
|
+
}
|
|
34
|
+
/** P[Z <= t] of the standard normal (log if logP). t is not NaN */
|
|
35
|
+
function pnormStd(t, logP) {
|
|
36
|
+
if (!logP)
|
|
37
|
+
return t <= 0 ? lowerTailProb(t) : 1 - lowerTailProb(-t);
|
|
38
|
+
if (t > -1)
|
|
39
|
+
return Math.log1p(-lowerTailProb(-t));
|
|
40
|
+
const a = -t;
|
|
41
|
+
return Math.log(0.5 * erfcxNonNegative(a * SQRT1_2)) - (a * a) / 2;
|
|
42
|
+
}
|
|
43
|
+
function pnorm1(q, mean, sd, lowerTail, logP) {
|
|
44
|
+
if (Number.isNaN(q) || Number.isNaN(mean) || Number.isNaN(sd))
|
|
45
|
+
return NaN;
|
|
46
|
+
if (!Number.isFinite(q) && mean === q)
|
|
47
|
+
return NaN; // Inf - Inf
|
|
48
|
+
const p0 = lowerTail ? (logP ? -Infinity : 0) : logP ? 0 : 1;
|
|
49
|
+
const p1 = lowerTail ? (logP ? 0 : 1) : logP ? -Infinity : 0;
|
|
50
|
+
if (sd < 0)
|
|
51
|
+
return NaN;
|
|
52
|
+
if (sd === 0)
|
|
53
|
+
return q < mean ? p0 : p1;
|
|
54
|
+
const x = (q - mean) / sd;
|
|
55
|
+
if (!Number.isFinite(x))
|
|
56
|
+
return q < mean ? p0 : p1;
|
|
57
|
+
return pnormStd(lowerTail ? x : -x, logP);
|
|
58
|
+
}
|
|
59
|
+
function qnorm1(p, mean, sd, lowerTail, logP) {
|
|
60
|
+
if (Number.isNaN(p) || Number.isNaN(mean) || Number.isNaN(sd))
|
|
61
|
+
return NaN;
|
|
62
|
+
// Endpoints corresponding to probabilities 0 and 1
|
|
63
|
+
const left = lowerTail ? -Infinity : Infinity;
|
|
64
|
+
if (logP) {
|
|
65
|
+
if (p > 0)
|
|
66
|
+
return NaN;
|
|
67
|
+
if (p === 0)
|
|
68
|
+
return -left;
|
|
69
|
+
if (p === -Infinity)
|
|
70
|
+
return left;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
if (p < 0 || p > 1)
|
|
74
|
+
return NaN;
|
|
75
|
+
if (p === 0)
|
|
76
|
+
return left;
|
|
77
|
+
if (p === 1)
|
|
78
|
+
return -left;
|
|
79
|
+
}
|
|
80
|
+
if (sd < 0)
|
|
81
|
+
return NaN;
|
|
82
|
+
if (sd === 0)
|
|
83
|
+
return mean;
|
|
84
|
+
const z = logP ? ndtriLogRefined(p) : ndtri(p);
|
|
85
|
+
return mean + sd * (lowerTail ? z : -z);
|
|
86
|
+
}
|
|
87
|
+
const SQRT_2_OVER_PI = 0.797884560802865355879892119869;
|
|
88
|
+
/**
|
|
89
|
+
* ndtriExp with one Newton correction applied only outside the design range of the Cephes approximation.
|
|
90
|
+
*
|
|
91
|
+
* The tail approximation of Cephes' ndtri assumes z = sqrt(-2 log p) up to 64 (log p >= -2048). Beyond that
|
|
92
|
+
* the relative error grows to about 5e-13 (measured at log p = -1e5). So log Φ(x) = y is re-solved using the
|
|
93
|
+
* logP of pnormStd, which is accurate in the tails as well. The derivative φ(x)/Φ(x) is √(2/π) / erfcx(a/√2),
|
|
94
|
+
* since Φ(x) = erfcx(a/√2) φ(x) √(π/2) for x = -a < 0. This avoids dividing exp(-a²/2) by itself.
|
|
95
|
+
*/
|
|
96
|
+
function ndtriLogRefined(y) {
|
|
97
|
+
const x = ndtriExp(y);
|
|
98
|
+
if (!(y < -2048) || !Number.isFinite(x))
|
|
99
|
+
return x;
|
|
100
|
+
const slope = SQRT_2_OVER_PI / erfcxNonNegative(-x * SQRT1_2);
|
|
101
|
+
const step = (pnormStd(x, true) - y) / slope;
|
|
102
|
+
// For |x| > 1.3e154, a² in pnormStd overflows; return without correction there
|
|
103
|
+
return Number.isFinite(step) ? x - step : x;
|
|
104
|
+
}
|
|
105
|
+
const LN_SQRT_2PI = 0.918938533204672741780329736406;
|
|
106
|
+
const INV_SQRT_2PI = 0.398942280401432677939946059934;
|
|
107
|
+
function dnorm1(x, mean, sd, log) {
|
|
108
|
+
if (Number.isNaN(x) || Number.isNaN(mean) || Number.isNaN(sd))
|
|
109
|
+
return NaN;
|
|
110
|
+
if (sd < 0)
|
|
111
|
+
return NaN;
|
|
112
|
+
if (!Number.isFinite(sd))
|
|
113
|
+
return log ? -Infinity : 0;
|
|
114
|
+
if (!Number.isFinite(x) && mean === x)
|
|
115
|
+
return NaN; // Inf - Inf
|
|
116
|
+
if (sd === 0)
|
|
117
|
+
return x === mean ? Infinity : log ? -Infinity : 0;
|
|
118
|
+
const z = (x - mean) / sd;
|
|
119
|
+
if (!Number.isFinite(z))
|
|
120
|
+
return log ? -Infinity : 0;
|
|
121
|
+
if (log)
|
|
122
|
+
return -(LN_SQRT_2PI + 0.5 * z * z + Math.log(sd));
|
|
123
|
+
// Rounding of z^2 is amplified by the exponential in exp(-z^2/2), so split it as in pnorm
|
|
124
|
+
return scaledExpNegHalfSq(Math.abs(z), INV_SQRT_2PI / sd);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Density of the normal distribution. Equivalent to R's `dnorm(x, mean = 0, sd = 1, log = FALSE)`.
|
|
128
|
+
*
|
|
129
|
+
* @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
|
|
130
|
+
* @param options - Distribution parameters and output scale.
|
|
131
|
+
* @param options.mean - Mean. Default `0`.
|
|
132
|
+
* @param options.sd - Standard deviation. Default `1`.
|
|
133
|
+
* @param options.log - If `true`, return the log density. Default `false`.
|
|
134
|
+
* @returns The density (or log density).
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* dnorm(0); // 0.3989422804014327
|
|
139
|
+
* dnorm([-1, 0, 1]); // [0.24197072451914337, 0.3989422804014327, 0.24197072451914337]
|
|
140
|
+
* dnorm(1, { mean: 1, sd: 2, log: true }); // -1.612085713764618
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export function dnorm(x, options = {}) {
|
|
144
|
+
const { mean = 0, sd = 1, log = false } = options;
|
|
145
|
+
return vectorize(x, (v) => dnorm1(v, mean, sd, log));
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Distribution function of the normal distribution.
|
|
149
|
+
* Equivalent to R's `pnorm(q, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)`.
|
|
150
|
+
*
|
|
151
|
+
* @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
|
|
152
|
+
* @param options - `mean`, `sd`, `lowerTail` and `logP` (see {@link NormalOptions}).
|
|
153
|
+
* @returns The probability (or log probability).
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* pnorm(1.96); // 0.9750021048517795
|
|
158
|
+
* pnorm(1.96, { lowerTail: false }); // 0.024997895148220435
|
|
159
|
+
* pnorm(new Float64Array([-1, 0, 1])); // Float64Array [0.15865525393145707, 0.5, 0.8413447460685429]
|
|
160
|
+
* pnorm(-40, { logP: true }); // -804.6084420137538
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
export function pnorm(q, options = {}) {
|
|
164
|
+
const { mean = 0, sd = 1, lowerTail = true, logP = false } = options;
|
|
165
|
+
return vectorize(q, (v) => pnorm1(v, mean, sd, lowerTail, logP));
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Quantile function of the normal distribution.
|
|
169
|
+
* Equivalent to R's `qnorm(p, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)`.
|
|
170
|
+
*
|
|
171
|
+
* @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
|
|
172
|
+
* @param options - `mean`, `sd`, `lowerTail` and `logP` (see {@link NormalOptions}).
|
|
173
|
+
* @returns The quantile(s).
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* qnorm(0.975); // 1.959963984540054
|
|
178
|
+
* qnorm(0.05, { mean: 100, sd: 15 }); // 75.32719559572791
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export function qnorm(p, options = {}) {
|
|
182
|
+
const { mean = 0, sd = 1, lowerTail = true, logP = false } = options;
|
|
183
|
+
return vectorize(p, (v) => qnorm1(v, mean, sd, lowerTail, logP));
|
|
184
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function dnt1(x: number, df: number, ncp: number, log: boolean): number;
|
|
2
|
+
export declare function pnt1(q: number, df: number, ncp: number, lowerTail: boolean, logP: boolean): number;
|
|
3
|
+
export declare function qnt1(p: number, df: number, ncp: number, lowerTail: boolean, logP: boolean): number;
|