@bilig/formula 0.1.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/LICENSE +21 -0
- package/README.md +16 -0
- package/dist/addressing.d.ts +66 -0
- package/dist/addressing.js +179 -0
- package/dist/addressing.js.map +1 -0
- package/dist/ast.d.ts +74 -0
- package/dist/ast.js +2 -0
- package/dist/ast.js.map +1 -0
- package/dist/binder.d.ts +13 -0
- package/dist/binder.js +1700 -0
- package/dist/binder.js.map +1 -0
- package/dist/builtin-capabilities.d.ts +19 -0
- package/dist/builtin-capabilities.js +861 -0
- package/dist/builtin-capabilities.js.map +1 -0
- package/dist/builtins/complex.d.ts +10 -0
- package/dist/builtins/complex.js +407 -0
- package/dist/builtins/complex.js.map +1 -0
- package/dist/builtins/convert.d.ts +3 -0
- package/dist/builtins/convert.js +362 -0
- package/dist/builtins/convert.js.map +1 -0
- package/dist/builtins/datetime.d.ts +23 -0
- package/dist/builtins/datetime.js +1096 -0
- package/dist/builtins/datetime.js.map +1 -0
- package/dist/builtins/distribution-builtins.d.ts +16 -0
- package/dist/builtins/distribution-builtins.js +517 -0
- package/dist/builtins/distribution-builtins.js.map +1 -0
- package/dist/builtins/distributions.d.ts +34 -0
- package/dist/builtins/distributions.js +722 -0
- package/dist/builtins/distributions.js.map +1 -0
- package/dist/builtins/financial-builtins.d.ts +16 -0
- package/dist/builtins/financial-builtins.js +324 -0
- package/dist/builtins/financial-builtins.js.map +1 -0
- package/dist/builtins/financial.d.ts +11 -0
- package/dist/builtins/financial.js +241 -0
- package/dist/builtins/financial.js.map +1 -0
- package/dist/builtins/fixed-income-builtins.d.ts +14 -0
- package/dist/builtins/fixed-income-builtins.js +598 -0
- package/dist/builtins/fixed-income-builtins.js.map +1 -0
- package/dist/builtins/fixed-income.d.ts +42 -0
- package/dist/builtins/fixed-income.js +668 -0
- package/dist/builtins/fixed-income.js.map +1 -0
- package/dist/builtins/formatting.d.ts +8 -0
- package/dist/builtins/formatting.js +53 -0
- package/dist/builtins/formatting.js.map +1 -0
- package/dist/builtins/logical.d.ts +4 -0
- package/dist/builtins/logical.js +258 -0
- package/dist/builtins/logical.js.map +1 -0
- package/dist/builtins/lookup-array-shape-builtins.d.ts +21 -0
- package/dist/builtins/lookup-array-shape-builtins.js +517 -0
- package/dist/builtins/lookup-array-shape-builtins.js.map +1 -0
- package/dist/builtins/lookup-criteria-builtins.d.ts +16 -0
- package/dist/builtins/lookup-criteria-builtins.js +216 -0
- package/dist/builtins/lookup-criteria-builtins.js.map +1 -0
- package/dist/builtins/lookup-database-builtins.d.ts +17 -0
- package/dist/builtins/lookup-database-builtins.js +294 -0
- package/dist/builtins/lookup-database-builtins.js.map +1 -0
- package/dist/builtins/lookup-financial-builtins.d.ts +11 -0
- package/dist/builtins/lookup-financial-builtins.js +291 -0
- package/dist/builtins/lookup-financial-builtins.js.map +1 -0
- package/dist/builtins/lookup-hypothesis-builtins.d.ts +11 -0
- package/dist/builtins/lookup-hypothesis-builtins.js +57 -0
- package/dist/builtins/lookup-hypothesis-builtins.js.map +1 -0
- package/dist/builtins/lookup-matrix-builtins.d.ts +17 -0
- package/dist/builtins/lookup-matrix-builtins.js +218 -0
- package/dist/builtins/lookup-matrix-builtins.js.map +1 -0
- package/dist/builtins/lookup-order-statistics-builtins.d.ts +18 -0
- package/dist/builtins/lookup-order-statistics-builtins.js +575 -0
- package/dist/builtins/lookup-order-statistics-builtins.js.map +1 -0
- package/dist/builtins/lookup-reference-builtins.d.ts +18 -0
- package/dist/builtins/lookup-reference-builtins.js +300 -0
- package/dist/builtins/lookup-reference-builtins.js.map +1 -0
- package/dist/builtins/lookup-regression-builtins.d.ts +12 -0
- package/dist/builtins/lookup-regression-builtins.js +511 -0
- package/dist/builtins/lookup-regression-builtins.js.map +1 -0
- package/dist/builtins/lookup-sort-filter-builtins.d.ts +20 -0
- package/dist/builtins/lookup-sort-filter-builtins.js +382 -0
- package/dist/builtins/lookup-sort-filter-builtins.js.map +1 -0
- package/dist/builtins/lookup.d.ts +13 -0
- package/dist/builtins/lookup.js +867 -0
- package/dist/builtins/lookup.js.map +1 -0
- package/dist/builtins/math-builtins.d.ts +31 -0
- package/dist/builtins/math-builtins.js +420 -0
- package/dist/builtins/math-builtins.js.map +1 -0
- package/dist/builtins/numeric.d.ts +30 -0
- package/dist/builtins/numeric.js +150 -0
- package/dist/builtins/numeric.js.map +1 -0
- package/dist/builtins/placeholder.d.ts +9 -0
- package/dist/builtins/placeholder.js +540 -0
- package/dist/builtins/placeholder.js.map +1 -0
- package/dist/builtins/radix.d.ts +12 -0
- package/dist/builtins/radix.js +220 -0
- package/dist/builtins/radix.js.map +1 -0
- package/dist/builtins/statistical-builtins.d.ts +13 -0
- package/dist/builtins/statistical-builtins.js +240 -0
- package/dist/builtins/statistical-builtins.js.map +1 -0
- package/dist/builtins/statistics.d.ts +8 -0
- package/dist/builtins/statistics.js +74 -0
- package/dist/builtins/statistics.js.map +1 -0
- package/dist/builtins/text.d.ts +5 -0
- package/dist/builtins/text.js +1879 -0
- package/dist/builtins/text.js.map +1 -0
- package/dist/builtins.d.ts +8 -0
- package/dist/builtins.js +695 -0
- package/dist/builtins.js.map +1 -0
- package/dist/compatibility.d.ts +25 -0
- package/dist/compatibility.js +498 -0
- package/dist/compatibility.js.map +1 -0
- package/dist/compiler.d.ts +29 -0
- package/dist/compiler.js +474 -0
- package/dist/compiler.js.map +1 -0
- package/dist/external-function-adapter.d.ts +32 -0
- package/dist/external-function-adapter.js +42 -0
- package/dist/external-function-adapter.js.map +1 -0
- package/dist/generated/formula-inventory.d.ts +6839 -0
- package/dist/generated/formula-inventory.js +7368 -0
- package/dist/generated/formula-inventory.js.map +1 -0
- package/dist/group-pivot-evaluator.d.ts +28 -0
- package/dist/group-pivot-evaluator.js +435 -0
- package/dist/group-pivot-evaluator.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/js-evaluator.d.ts +107 -0
- package/dist/js-evaluator.js +1651 -0
- package/dist/js-evaluator.js.map +1 -0
- package/dist/lexer.d.ts +6 -0
- package/dist/lexer.js +115 -0
- package/dist/lexer.js.map +1 -0
- package/dist/optimizer.d.ts +2 -0
- package/dist/optimizer.js +353 -0
- package/dist/optimizer.js.map +1 -0
- package/dist/parser.d.ts +2 -0
- package/dist/parser.js +352 -0
- package/dist/parser.js.map +1 -0
- package/dist/program-arena.d.ts +22 -0
- package/dist/program-arena.js +67 -0
- package/dist/program-arena.js.map +1 -0
- package/dist/runtime-values.d.ts +17 -0
- package/dist/runtime-values.js +11 -0
- package/dist/runtime-values.js.map +1 -0
- package/dist/special-call-rewrites.d.ts +2 -0
- package/dist/special-call-rewrites.js +74 -0
- package/dist/special-call-rewrites.js.map +1 -0
- package/dist/translation.d.ts +28 -0
- package/dist/translation.js +569 -0
- package/dist/translation.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,722 @@
|
|
|
1
|
+
import { erfApprox, populationStandardDeviation, sampleStandardDeviation } from "./statistics.js";
|
|
2
|
+
const LANCZOS_G = 7;
|
|
3
|
+
const LANCZOS_COEFFICIENTS = [
|
|
4
|
+
676.5203681218851, -1259.1392167224028, 771.3234287776531, -176.6150291621406, 12.507343278686905,
|
|
5
|
+
-0.13857109526572012, 9.984369578019572e-6, 1.5056327351493116e-7,
|
|
6
|
+
];
|
|
7
|
+
const BESSEL_EPSILON = 1e-8;
|
|
8
|
+
export function logGamma(value) {
|
|
9
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
10
|
+
return Number.NaN;
|
|
11
|
+
}
|
|
12
|
+
let sum = 0.9999999999998099;
|
|
13
|
+
const shifted = value - 1;
|
|
14
|
+
LANCZOS_COEFFICIENTS.forEach((coefficient, index) => {
|
|
15
|
+
sum += coefficient / (shifted + index + 1);
|
|
16
|
+
});
|
|
17
|
+
const t = shifted + LANCZOS_G + 0.5;
|
|
18
|
+
return 0.5 * Math.log(2 * Math.PI) + (shifted + 0.5) * Math.log(t) - t + Math.log(sum);
|
|
19
|
+
}
|
|
20
|
+
function gammaReal(value) {
|
|
21
|
+
if (!Number.isFinite(value) || (value <= 0 && value === Math.trunc(value))) {
|
|
22
|
+
return Number.NaN;
|
|
23
|
+
}
|
|
24
|
+
if (value < 0.5) {
|
|
25
|
+
const sine = Math.sin(Math.PI * value);
|
|
26
|
+
return sine === 0 ? Number.NaN : Math.PI / (sine * gammaReal(1 - value));
|
|
27
|
+
}
|
|
28
|
+
return Math.exp(logGamma(value));
|
|
29
|
+
}
|
|
30
|
+
function besselSeries(order, x, alternating) {
|
|
31
|
+
const half = x / 2;
|
|
32
|
+
let term = half ** order / gammaReal(order + 1);
|
|
33
|
+
if (!Number.isFinite(term)) {
|
|
34
|
+
return Number.NaN;
|
|
35
|
+
}
|
|
36
|
+
let sum = term;
|
|
37
|
+
for (let index = 0; index < 400; index += 1) {
|
|
38
|
+
const denominator = (index + 1) * (index + order + 1);
|
|
39
|
+
if (denominator === 0) {
|
|
40
|
+
return Number.NaN;
|
|
41
|
+
}
|
|
42
|
+
term *= ((alternating ? -1 : 1) * half * half) / denominator;
|
|
43
|
+
sum += term;
|
|
44
|
+
if (Math.abs(term) <= Math.abs(sum) * 1e-15) {
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return sum;
|
|
49
|
+
}
|
|
50
|
+
export function besselJValue(x, order) {
|
|
51
|
+
if (x === 0) {
|
|
52
|
+
return order === 0 ? 1 : 0;
|
|
53
|
+
}
|
|
54
|
+
const absolute = Math.abs(x);
|
|
55
|
+
const result = besselSeries(order, absolute, true);
|
|
56
|
+
return x < 0 && order % 2 === 1 ? -result : result;
|
|
57
|
+
}
|
|
58
|
+
export function besselIValue(x, order) {
|
|
59
|
+
if (x === 0) {
|
|
60
|
+
return order === 0 ? 1 : 0;
|
|
61
|
+
}
|
|
62
|
+
const absolute = Math.abs(x);
|
|
63
|
+
const result = besselSeries(order, absolute, false);
|
|
64
|
+
return x < 0 && order % 2 === 1 ? -result : result;
|
|
65
|
+
}
|
|
66
|
+
export function besselYValue(x, order) {
|
|
67
|
+
const shiftedOrder = order + BESSEL_EPSILON;
|
|
68
|
+
return ((besselSeries(shiftedOrder, x, true) * Math.cos(Math.PI * shiftedOrder) -
|
|
69
|
+
besselSeries(-shiftedOrder, x, true)) /
|
|
70
|
+
Math.sin(Math.PI * shiftedOrder));
|
|
71
|
+
}
|
|
72
|
+
export function besselKValue(x, order) {
|
|
73
|
+
const shiftedOrder = order + BESSEL_EPSILON;
|
|
74
|
+
return (((Math.PI / 2) *
|
|
75
|
+
(besselSeries(-shiftedOrder, x, false) - besselSeries(shiftedOrder, x, false))) /
|
|
76
|
+
Math.sin(Math.PI * shiftedOrder));
|
|
77
|
+
}
|
|
78
|
+
export function gammaFunction(value) {
|
|
79
|
+
if (!Number.isFinite(value) || (Number.isInteger(value) && value <= 0)) {
|
|
80
|
+
return Number.NaN;
|
|
81
|
+
}
|
|
82
|
+
if (value < 0.5) {
|
|
83
|
+
const sine = Math.sin(Math.PI * value);
|
|
84
|
+
if (sine === 0) {
|
|
85
|
+
return Number.NaN;
|
|
86
|
+
}
|
|
87
|
+
return Math.PI / (sine * gammaFunction(1 - value));
|
|
88
|
+
}
|
|
89
|
+
return Math.exp(logGamma(value));
|
|
90
|
+
}
|
|
91
|
+
function regularizedLowerGamma(shape, x) {
|
|
92
|
+
if (!Number.isFinite(shape) || !Number.isFinite(x) || shape <= 0 || x < 0) {
|
|
93
|
+
return Number.NaN;
|
|
94
|
+
}
|
|
95
|
+
if (x === 0) {
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
const logGammaShape = logGamma(shape);
|
|
99
|
+
if (!Number.isFinite(logGammaShape)) {
|
|
100
|
+
return Number.NaN;
|
|
101
|
+
}
|
|
102
|
+
if (x < shape + 1) {
|
|
103
|
+
let term = 1 / shape;
|
|
104
|
+
let sum = term;
|
|
105
|
+
for (let iteration = 1; iteration < 1000; iteration += 1) {
|
|
106
|
+
term *= x / (shape + iteration);
|
|
107
|
+
sum += term;
|
|
108
|
+
if (Math.abs(term) <= Math.abs(sum) * 1e-14) {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return sum * Math.exp(-x + shape * Math.log(x) - logGammaShape);
|
|
113
|
+
}
|
|
114
|
+
let b = x + 1 - shape;
|
|
115
|
+
let c = 1 / 1e-300;
|
|
116
|
+
let d = 1 / b;
|
|
117
|
+
let h = d;
|
|
118
|
+
for (let iteration = 1; iteration < 1000; iteration += 1) {
|
|
119
|
+
const factor = -iteration * (iteration - shape);
|
|
120
|
+
b += 2;
|
|
121
|
+
d = factor * d + b;
|
|
122
|
+
if (Math.abs(d) < 1e-300) {
|
|
123
|
+
d = 1e-300;
|
|
124
|
+
}
|
|
125
|
+
c = b + factor / c;
|
|
126
|
+
if (Math.abs(c) < 1e-300) {
|
|
127
|
+
c = 1e-300;
|
|
128
|
+
}
|
|
129
|
+
d = 1 / d;
|
|
130
|
+
const delta = d * c;
|
|
131
|
+
h *= delta;
|
|
132
|
+
if (Math.abs(delta - 1) <= 1e-14) {
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return 1 - Math.exp(-x + shape * Math.log(x) - logGammaShape) * h;
|
|
137
|
+
}
|
|
138
|
+
export function regularizedUpperGamma(shape, x) {
|
|
139
|
+
const lower = regularizedLowerGamma(shape, x);
|
|
140
|
+
return Number.isFinite(lower) ? 1 - lower : Number.NaN;
|
|
141
|
+
}
|
|
142
|
+
function logBeta(alpha, beta) {
|
|
143
|
+
return logGamma(alpha) + logGamma(beta) - logGamma(alpha + beta);
|
|
144
|
+
}
|
|
145
|
+
function betaContinuedFraction(x, alpha, beta) {
|
|
146
|
+
const maxIterations = 200;
|
|
147
|
+
const epsilon = 1e-14;
|
|
148
|
+
const tiny = 1e-300;
|
|
149
|
+
const qab = alpha + beta;
|
|
150
|
+
const qap = alpha + 1;
|
|
151
|
+
const qam = alpha - 1;
|
|
152
|
+
let c = 1;
|
|
153
|
+
let d = 1 - (qab * x) / qap;
|
|
154
|
+
if (Math.abs(d) < tiny) {
|
|
155
|
+
d = tiny;
|
|
156
|
+
}
|
|
157
|
+
d = 1 / d;
|
|
158
|
+
let h = d;
|
|
159
|
+
for (let iteration = 1; iteration <= maxIterations; iteration += 1) {
|
|
160
|
+
const step = iteration * 2;
|
|
161
|
+
let factor = (iteration * (beta - iteration) * x) / ((qam + step) * (alpha + step));
|
|
162
|
+
d = 1 + factor * d;
|
|
163
|
+
if (Math.abs(d) < tiny) {
|
|
164
|
+
d = tiny;
|
|
165
|
+
}
|
|
166
|
+
c = 1 + factor / c;
|
|
167
|
+
if (Math.abs(c) < tiny) {
|
|
168
|
+
c = tiny;
|
|
169
|
+
}
|
|
170
|
+
d = 1 / d;
|
|
171
|
+
h *= d * c;
|
|
172
|
+
factor = (-(alpha + iteration) * (qab + iteration) * x) / ((alpha + step) * (qap + step));
|
|
173
|
+
d = 1 + factor * d;
|
|
174
|
+
if (Math.abs(d) < tiny) {
|
|
175
|
+
d = tiny;
|
|
176
|
+
}
|
|
177
|
+
c = 1 + factor / c;
|
|
178
|
+
if (Math.abs(c) < tiny) {
|
|
179
|
+
c = tiny;
|
|
180
|
+
}
|
|
181
|
+
d = 1 / d;
|
|
182
|
+
const delta = d * c;
|
|
183
|
+
h *= delta;
|
|
184
|
+
if (Math.abs(delta - 1) <= epsilon) {
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return h;
|
|
189
|
+
}
|
|
190
|
+
function regularizedBeta(x, alpha, beta) {
|
|
191
|
+
if (!Number.isFinite(x) ||
|
|
192
|
+
!Number.isFinite(alpha) ||
|
|
193
|
+
!Number.isFinite(beta) ||
|
|
194
|
+
alpha <= 0 ||
|
|
195
|
+
beta <= 0 ||
|
|
196
|
+
x < 0 ||
|
|
197
|
+
x > 1) {
|
|
198
|
+
return Number.NaN;
|
|
199
|
+
}
|
|
200
|
+
if (x === 0) {
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
203
|
+
if (x === 1) {
|
|
204
|
+
return 1;
|
|
205
|
+
}
|
|
206
|
+
const logTerm = alpha * Math.log(x) + beta * Math.log(1 - x) - logBeta(alpha, beta);
|
|
207
|
+
if (!Number.isFinite(logTerm)) {
|
|
208
|
+
return Number.NaN;
|
|
209
|
+
}
|
|
210
|
+
const front = Math.exp(logTerm);
|
|
211
|
+
if (x < (alpha + 1) / (alpha + beta + 2)) {
|
|
212
|
+
return (front * betaContinuedFraction(x, alpha, beta)) / alpha;
|
|
213
|
+
}
|
|
214
|
+
return 1 - (front * betaContinuedFraction(1 - x, beta, alpha)) / beta;
|
|
215
|
+
}
|
|
216
|
+
export function betaDistributionDensity(x, alpha, beta, lowerBound = 0, upperBound = 1) {
|
|
217
|
+
if (!Number.isFinite(x) ||
|
|
218
|
+
!Number.isFinite(alpha) ||
|
|
219
|
+
!Number.isFinite(beta) ||
|
|
220
|
+
!Number.isFinite(lowerBound) ||
|
|
221
|
+
!Number.isFinite(upperBound) ||
|
|
222
|
+
alpha <= 0 ||
|
|
223
|
+
beta <= 0 ||
|
|
224
|
+
upperBound <= lowerBound ||
|
|
225
|
+
x < lowerBound ||
|
|
226
|
+
x > upperBound) {
|
|
227
|
+
return Number.NaN;
|
|
228
|
+
}
|
|
229
|
+
const scale = upperBound - lowerBound;
|
|
230
|
+
const normalized = (x - lowerBound) / scale;
|
|
231
|
+
if (normalized === 0) {
|
|
232
|
+
if (alpha === 1) {
|
|
233
|
+
return beta / scale;
|
|
234
|
+
}
|
|
235
|
+
return alpha < 1 ? Number.POSITIVE_INFINITY : 0;
|
|
236
|
+
}
|
|
237
|
+
if (normalized === 1) {
|
|
238
|
+
if (beta === 1) {
|
|
239
|
+
return alpha / scale;
|
|
240
|
+
}
|
|
241
|
+
return beta < 1 ? Number.POSITIVE_INFINITY : 0;
|
|
242
|
+
}
|
|
243
|
+
return Math.exp((alpha - 1) * Math.log(normalized) +
|
|
244
|
+
(beta - 1) * Math.log(1 - normalized) -
|
|
245
|
+
logBeta(alpha, beta) -
|
|
246
|
+
Math.log(scale));
|
|
247
|
+
}
|
|
248
|
+
export function betaDistributionCdf(x, alpha, beta, lowerBound = 0, upperBound = 1) {
|
|
249
|
+
if (!Number.isFinite(x) ||
|
|
250
|
+
!Number.isFinite(alpha) ||
|
|
251
|
+
!Number.isFinite(beta) ||
|
|
252
|
+
!Number.isFinite(lowerBound) ||
|
|
253
|
+
!Number.isFinite(upperBound) ||
|
|
254
|
+
alpha <= 0 ||
|
|
255
|
+
beta <= 0 ||
|
|
256
|
+
upperBound <= lowerBound ||
|
|
257
|
+
x < lowerBound ||
|
|
258
|
+
x > upperBound) {
|
|
259
|
+
return Number.NaN;
|
|
260
|
+
}
|
|
261
|
+
return regularizedBeta((x - lowerBound) / (upperBound - lowerBound), alpha, beta);
|
|
262
|
+
}
|
|
263
|
+
function inverseRegularizedBeta(probability, alpha, beta) {
|
|
264
|
+
if (!Number.isFinite(probability) ||
|
|
265
|
+
!Number.isFinite(alpha) ||
|
|
266
|
+
!Number.isFinite(beta) ||
|
|
267
|
+
!(probability > 0 && probability < 1) ||
|
|
268
|
+
alpha <= 0 ||
|
|
269
|
+
beta <= 0) {
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
let lower = 0;
|
|
273
|
+
let upper = 1;
|
|
274
|
+
for (let iteration = 0; iteration < 100; iteration += 1) {
|
|
275
|
+
const midpoint = (lower + upper) / 2;
|
|
276
|
+
const cdf = regularizedBeta(midpoint, alpha, beta);
|
|
277
|
+
if (!Number.isFinite(cdf)) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
if (cdf < probability) {
|
|
281
|
+
lower = midpoint;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
upper = midpoint;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return (lower + upper) / 2;
|
|
288
|
+
}
|
|
289
|
+
export function betaDistributionInverse(probability, alpha, beta, lowerBound = 0, upperBound = 1) {
|
|
290
|
+
if (!Number.isFinite(probability) ||
|
|
291
|
+
!Number.isFinite(alpha) ||
|
|
292
|
+
!Number.isFinite(beta) ||
|
|
293
|
+
!Number.isFinite(lowerBound) ||
|
|
294
|
+
!Number.isFinite(upperBound) ||
|
|
295
|
+
!(probability > 0 && probability < 1) ||
|
|
296
|
+
alpha <= 0 ||
|
|
297
|
+
beta <= 0 ||
|
|
298
|
+
upperBound <= lowerBound) {
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
const normalized = inverseRegularizedBeta(probability, alpha, beta);
|
|
302
|
+
return normalized === undefined ? undefined : lowerBound + (upperBound - lowerBound) * normalized;
|
|
303
|
+
}
|
|
304
|
+
export function fDistributionDensity(x, degreesFreedom1, degreesFreedom2) {
|
|
305
|
+
if (!Number.isFinite(x) ||
|
|
306
|
+
!Number.isFinite(degreesFreedom1) ||
|
|
307
|
+
!Number.isFinite(degreesFreedom2) ||
|
|
308
|
+
x < 0 ||
|
|
309
|
+
degreesFreedom1 < 1 ||
|
|
310
|
+
degreesFreedom2 < 1) {
|
|
311
|
+
return Number.NaN;
|
|
312
|
+
}
|
|
313
|
+
if (x === 0) {
|
|
314
|
+
return degreesFreedom1 === 2 ? 1 : degreesFreedom1 < 2 ? Number.POSITIVE_INFINITY : 0;
|
|
315
|
+
}
|
|
316
|
+
const a = degreesFreedom1 / 2;
|
|
317
|
+
const b = degreesFreedom2 / 2;
|
|
318
|
+
return Math.exp(a * Math.log(degreesFreedom1) +
|
|
319
|
+
b * Math.log(degreesFreedom2) +
|
|
320
|
+
(a - 1) * Math.log(x) -
|
|
321
|
+
(a + b) * Math.log(degreesFreedom1 * x + degreesFreedom2) -
|
|
322
|
+
logBeta(a, b));
|
|
323
|
+
}
|
|
324
|
+
export function fDistributionCdf(x, degreesFreedom1, degreesFreedom2) {
|
|
325
|
+
if (!Number.isFinite(x) ||
|
|
326
|
+
!Number.isFinite(degreesFreedom1) ||
|
|
327
|
+
!Number.isFinite(degreesFreedom2) ||
|
|
328
|
+
x < 0 ||
|
|
329
|
+
degreesFreedom1 < 1 ||
|
|
330
|
+
degreesFreedom2 < 1) {
|
|
331
|
+
return Number.NaN;
|
|
332
|
+
}
|
|
333
|
+
const a = degreesFreedom1 / 2;
|
|
334
|
+
const b = degreesFreedom2 / 2;
|
|
335
|
+
const transformed = (degreesFreedom1 * x) / (degreesFreedom1 * x + degreesFreedom2);
|
|
336
|
+
return regularizedBeta(transformed, a, b);
|
|
337
|
+
}
|
|
338
|
+
export function inverseFDistribution(probability, degreesFreedom1, degreesFreedom2) {
|
|
339
|
+
if (!Number.isFinite(probability) ||
|
|
340
|
+
!Number.isFinite(degreesFreedom1) ||
|
|
341
|
+
!Number.isFinite(degreesFreedom2) ||
|
|
342
|
+
!(probability > 0 && probability < 1) ||
|
|
343
|
+
degreesFreedom1 < 1 ||
|
|
344
|
+
degreesFreedom2 < 1) {
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
const transformed = inverseRegularizedBeta(probability, degreesFreedom1 / 2, degreesFreedom2 / 2);
|
|
348
|
+
if (transformed === undefined || transformed >= 1) {
|
|
349
|
+
return undefined;
|
|
350
|
+
}
|
|
351
|
+
return (degreesFreedom2 * transformed) / (degreesFreedom1 * (1 - transformed));
|
|
352
|
+
}
|
|
353
|
+
export function studentTDensity(x, degreesFreedom) {
|
|
354
|
+
if (!Number.isFinite(x) || !Number.isFinite(degreesFreedom) || degreesFreedom < 1) {
|
|
355
|
+
return Number.NaN;
|
|
356
|
+
}
|
|
357
|
+
const halfDegrees = degreesFreedom / 2;
|
|
358
|
+
return Math.exp(logGamma((degreesFreedom + 1) / 2) -
|
|
359
|
+
logGamma(halfDegrees) -
|
|
360
|
+
0.5 * (Math.log(degreesFreedom) + Math.log(Math.PI)) -
|
|
361
|
+
((degreesFreedom + 1) / 2) * Math.log(1 + (x * x) / degreesFreedom));
|
|
362
|
+
}
|
|
363
|
+
export function studentTCdf(x, degreesFreedom) {
|
|
364
|
+
if (!Number.isFinite(x) || !Number.isFinite(degreesFreedom) || degreesFreedom < 1) {
|
|
365
|
+
return Number.NaN;
|
|
366
|
+
}
|
|
367
|
+
if (x === 0) {
|
|
368
|
+
return 0.5;
|
|
369
|
+
}
|
|
370
|
+
const transformed = degreesFreedom / (degreesFreedom + x * x);
|
|
371
|
+
const tail = regularizedBeta(transformed, degreesFreedom / 2, 0.5);
|
|
372
|
+
if (!Number.isFinite(tail)) {
|
|
373
|
+
return Number.NaN;
|
|
374
|
+
}
|
|
375
|
+
return x > 0 ? 1 - tail / 2 : tail / 2;
|
|
376
|
+
}
|
|
377
|
+
export function inverseStudentT(probability, degreesFreedom) {
|
|
378
|
+
if (!Number.isFinite(probability) ||
|
|
379
|
+
!Number.isFinite(degreesFreedom) ||
|
|
380
|
+
!(probability > 0 && probability < 1) ||
|
|
381
|
+
degreesFreedom < 1) {
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
if (probability === 0.5) {
|
|
385
|
+
return 0;
|
|
386
|
+
}
|
|
387
|
+
if (probability < 0.5) {
|
|
388
|
+
const mirrored = inverseStudentT(1 - probability, degreesFreedom);
|
|
389
|
+
return mirrored === undefined ? undefined : -mirrored;
|
|
390
|
+
}
|
|
391
|
+
let lower = 0;
|
|
392
|
+
let upper = 1;
|
|
393
|
+
let upperCdf = studentTCdf(upper, degreesFreedom);
|
|
394
|
+
while (Number.isFinite(upperCdf) && upperCdf < probability && upper < 1e10) {
|
|
395
|
+
lower = upper;
|
|
396
|
+
upper *= 2;
|
|
397
|
+
upperCdf = studentTCdf(upper, degreesFreedom);
|
|
398
|
+
}
|
|
399
|
+
if (!Number.isFinite(upperCdf)) {
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
for (let iteration = 0; iteration < 100; iteration += 1) {
|
|
403
|
+
const midpoint = (lower + upper) / 2;
|
|
404
|
+
const cdf = studentTCdf(midpoint, degreesFreedom);
|
|
405
|
+
if (!Number.isFinite(cdf)) {
|
|
406
|
+
return undefined;
|
|
407
|
+
}
|
|
408
|
+
if (cdf < probability) {
|
|
409
|
+
lower = midpoint;
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
upper = midpoint;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return (lower + upper) / 2;
|
|
416
|
+
}
|
|
417
|
+
function logCombination(total, chosen) {
|
|
418
|
+
if (!Number.isInteger(total) || !Number.isInteger(chosen) || chosen < 0 || chosen > total) {
|
|
419
|
+
return Number.NaN;
|
|
420
|
+
}
|
|
421
|
+
return logGamma(total + 1) - logGamma(chosen + 1) - logGamma(total - chosen + 1);
|
|
422
|
+
}
|
|
423
|
+
export function binomialProbability(successes, trials, probability) {
|
|
424
|
+
if (!Number.isInteger(successes) ||
|
|
425
|
+
!Number.isInteger(trials) ||
|
|
426
|
+
successes < 0 ||
|
|
427
|
+
trials < 0 ||
|
|
428
|
+
successes > trials ||
|
|
429
|
+
probability < 0 ||
|
|
430
|
+
probability > 1) {
|
|
431
|
+
return Number.NaN;
|
|
432
|
+
}
|
|
433
|
+
if (probability === 0) {
|
|
434
|
+
return successes === 0 ? 1 : 0;
|
|
435
|
+
}
|
|
436
|
+
if (probability === 1) {
|
|
437
|
+
return successes === trials ? 1 : 0;
|
|
438
|
+
}
|
|
439
|
+
return Math.exp(logCombination(trials, successes) +
|
|
440
|
+
successes * Math.log(probability) +
|
|
441
|
+
(trials - successes) * Math.log(1 - probability));
|
|
442
|
+
}
|
|
443
|
+
export function negativeBinomialProbability(failures, successes, probability) {
|
|
444
|
+
if (!Number.isInteger(failures) ||
|
|
445
|
+
!Number.isInteger(successes) ||
|
|
446
|
+
failures < 0 ||
|
|
447
|
+
successes <= 0 ||
|
|
448
|
+
probability < 0 ||
|
|
449
|
+
probability > 1) {
|
|
450
|
+
return Number.NaN;
|
|
451
|
+
}
|
|
452
|
+
if (probability === 0) {
|
|
453
|
+
return 0;
|
|
454
|
+
}
|
|
455
|
+
if (probability === 1) {
|
|
456
|
+
return failures === 0 ? 1 : 0;
|
|
457
|
+
}
|
|
458
|
+
return Math.exp(logCombination(failures + successes - 1, failures) +
|
|
459
|
+
failures * Math.log(1 - probability) +
|
|
460
|
+
successes * Math.log(probability));
|
|
461
|
+
}
|
|
462
|
+
export function hypergeometricProbability(sampleSuccesses, sampleSize, populationSuccesses, populationSize) {
|
|
463
|
+
if (!Number.isInteger(sampleSuccesses) ||
|
|
464
|
+
!Number.isInteger(sampleSize) ||
|
|
465
|
+
!Number.isInteger(populationSuccesses) ||
|
|
466
|
+
!Number.isInteger(populationSize) ||
|
|
467
|
+
sampleSuccesses < 0 ||
|
|
468
|
+
sampleSize < 0 ||
|
|
469
|
+
populationSuccesses < 0 ||
|
|
470
|
+
populationSize <= 0 ||
|
|
471
|
+
sampleSize > populationSize ||
|
|
472
|
+
populationSuccesses > populationSize) {
|
|
473
|
+
return Number.NaN;
|
|
474
|
+
}
|
|
475
|
+
const minimum = Math.max(0, sampleSize - (populationSize - populationSuccesses));
|
|
476
|
+
const maximum = Math.min(sampleSize, populationSuccesses);
|
|
477
|
+
if (sampleSuccesses < minimum || sampleSuccesses > maximum) {
|
|
478
|
+
return 0;
|
|
479
|
+
}
|
|
480
|
+
return Math.exp(logCombination(populationSuccesses, sampleSuccesses) +
|
|
481
|
+
logCombination(populationSize - populationSuccesses, sampleSize - sampleSuccesses) -
|
|
482
|
+
logCombination(populationSize, sampleSize));
|
|
483
|
+
}
|
|
484
|
+
export function poissonProbability(events, mean) {
|
|
485
|
+
if (!Number.isInteger(events) || events < 0 || !Number.isFinite(mean) || mean < 0) {
|
|
486
|
+
return Number.NaN;
|
|
487
|
+
}
|
|
488
|
+
if (mean === 0) {
|
|
489
|
+
return events === 0 ? 1 : 0;
|
|
490
|
+
}
|
|
491
|
+
return Math.exp(events * Math.log(mean) - mean - logGamma(events + 1));
|
|
492
|
+
}
|
|
493
|
+
export function gammaDistributionDensity(x, alpha, beta) {
|
|
494
|
+
if (!Number.isFinite(x) || !Number.isFinite(alpha) || !Number.isFinite(beta)) {
|
|
495
|
+
return Number.NaN;
|
|
496
|
+
}
|
|
497
|
+
if (x < 0 || alpha <= 0 || beta <= 0) {
|
|
498
|
+
return Number.NaN;
|
|
499
|
+
}
|
|
500
|
+
if (x === 0) {
|
|
501
|
+
if (alpha === 1) {
|
|
502
|
+
return 1 / beta;
|
|
503
|
+
}
|
|
504
|
+
return alpha < 1 ? Number.POSITIVE_INFINITY : 0;
|
|
505
|
+
}
|
|
506
|
+
return Math.exp((alpha - 1) * Math.log(x) - x / beta - logGamma(alpha) - alpha * Math.log(beta));
|
|
507
|
+
}
|
|
508
|
+
export function gammaDistributionCdf(x, alpha, beta) {
|
|
509
|
+
return regularizedLowerGamma(alpha, x / beta);
|
|
510
|
+
}
|
|
511
|
+
export function inverseGammaDistribution(probability, alpha, beta) {
|
|
512
|
+
if (!Number.isFinite(probability) ||
|
|
513
|
+
!Number.isFinite(alpha) ||
|
|
514
|
+
!Number.isFinite(beta) ||
|
|
515
|
+
!(probability > 0 && probability < 1) ||
|
|
516
|
+
!(alpha > 0) ||
|
|
517
|
+
!(beta > 0)) {
|
|
518
|
+
return undefined;
|
|
519
|
+
}
|
|
520
|
+
let estimate = alpha * beta;
|
|
521
|
+
if (!(estimate > 0) || !Number.isFinite(estimate)) {
|
|
522
|
+
estimate = 1;
|
|
523
|
+
}
|
|
524
|
+
let lower = 0;
|
|
525
|
+
let upper = Math.max(estimate, 1);
|
|
526
|
+
let upperCdf = gammaDistributionCdf(upper, alpha, beta);
|
|
527
|
+
for (let iteration = 0; iteration < 64 && upperCdf < probability; iteration += 1) {
|
|
528
|
+
upper *= 2;
|
|
529
|
+
upperCdf = gammaDistributionCdf(upper, alpha, beta);
|
|
530
|
+
}
|
|
531
|
+
if (!(upperCdf >= probability)) {
|
|
532
|
+
return undefined;
|
|
533
|
+
}
|
|
534
|
+
let current = Math.min(Math.max(estimate, lower), upper);
|
|
535
|
+
for (let iteration = 0; iteration < 20; iteration += 1) {
|
|
536
|
+
const cdf = gammaDistributionCdf(current, alpha, beta);
|
|
537
|
+
if (!Number.isFinite(cdf)) {
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
if (cdf < probability) {
|
|
541
|
+
lower = current;
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
upper = current;
|
|
545
|
+
}
|
|
546
|
+
const density = gammaDistributionDensity(current, alpha, beta);
|
|
547
|
+
if (!(density > 0) || !Number.isFinite(density)) {
|
|
548
|
+
current = (lower + upper) / 2;
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
const next = current - (cdf - probability) / density;
|
|
552
|
+
current = Number.isFinite(next) && next > lower && next < upper ? next : (lower + upper) / 2;
|
|
553
|
+
}
|
|
554
|
+
for (let iteration = 0; iteration < 60; iteration += 1) {
|
|
555
|
+
const midpoint = (lower + upper) / 2;
|
|
556
|
+
const cdf = gammaDistributionCdf(midpoint, alpha, beta);
|
|
557
|
+
if (!Number.isFinite(cdf)) {
|
|
558
|
+
return undefined;
|
|
559
|
+
}
|
|
560
|
+
if (cdf < probability) {
|
|
561
|
+
lower = midpoint;
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
upper = midpoint;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return (lower + upper) / 2;
|
|
568
|
+
}
|
|
569
|
+
export function chiSquareDensity(x, degreesFreedom) {
|
|
570
|
+
return gammaDistributionDensity(x, degreesFreedom / 2, 2);
|
|
571
|
+
}
|
|
572
|
+
export function chiSquareCdf(x, degreesFreedom) {
|
|
573
|
+
return gammaDistributionCdf(x, degreesFreedom / 2, 2);
|
|
574
|
+
}
|
|
575
|
+
export function inverseChiSquare(probability, degreesFreedom) {
|
|
576
|
+
if (!Number.isFinite(probability) ||
|
|
577
|
+
!Number.isFinite(degreesFreedom) ||
|
|
578
|
+
!(probability > 0 && probability < 1) ||
|
|
579
|
+
!(degreesFreedom >= 1)) {
|
|
580
|
+
return undefined;
|
|
581
|
+
}
|
|
582
|
+
const z = inverseStandardNormal(probability);
|
|
583
|
+
const approximationFactor = z === undefined
|
|
584
|
+
? Number.NaN
|
|
585
|
+
: 1 - 2 / (9 * degreesFreedom) + z * Math.sqrt(2 / (9 * degreesFreedom));
|
|
586
|
+
let estimate = Number.isFinite(approximationFactor) && approximationFactor > 0
|
|
587
|
+
? degreesFreedom * approximationFactor ** 3
|
|
588
|
+
: degreesFreedom;
|
|
589
|
+
if (!(estimate > 0) || !Number.isFinite(estimate)) {
|
|
590
|
+
estimate = Math.max(degreesFreedom, 1);
|
|
591
|
+
}
|
|
592
|
+
let lower = 0;
|
|
593
|
+
let upper = Math.max(estimate, 1);
|
|
594
|
+
let upperCdf = chiSquareCdf(upper, degreesFreedom);
|
|
595
|
+
for (let iteration = 0; iteration < 64 && upperCdf < probability; iteration += 1) {
|
|
596
|
+
upper *= 2;
|
|
597
|
+
upperCdf = chiSquareCdf(upper, degreesFreedom);
|
|
598
|
+
}
|
|
599
|
+
if (!(upperCdf >= probability)) {
|
|
600
|
+
return undefined;
|
|
601
|
+
}
|
|
602
|
+
let current = Math.min(Math.max(estimate, lower), upper);
|
|
603
|
+
for (let iteration = 0; iteration < 20; iteration += 1) {
|
|
604
|
+
const cdf = chiSquareCdf(current, degreesFreedom);
|
|
605
|
+
if (!Number.isFinite(cdf)) {
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
if (cdf < probability) {
|
|
609
|
+
lower = current;
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
upper = current;
|
|
613
|
+
}
|
|
614
|
+
const density = chiSquareDensity(current, degreesFreedom);
|
|
615
|
+
if (!(density > 0) || !Number.isFinite(density)) {
|
|
616
|
+
current = (lower + upper) / 2;
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
const next = current - (cdf - probability) / density;
|
|
620
|
+
current = Number.isFinite(next) && next > lower && next < upper ? next : (lower + upper) / 2;
|
|
621
|
+
}
|
|
622
|
+
for (let iteration = 0; iteration < 60; iteration += 1) {
|
|
623
|
+
const midpoint = (lower + upper) / 2;
|
|
624
|
+
const cdf = chiSquareCdf(midpoint, degreesFreedom);
|
|
625
|
+
if (!Number.isFinite(cdf)) {
|
|
626
|
+
return undefined;
|
|
627
|
+
}
|
|
628
|
+
if (cdf < probability) {
|
|
629
|
+
lower = midpoint;
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
upper = midpoint;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return (lower + upper) / 2;
|
|
636
|
+
}
|
|
637
|
+
export function standardNormalPdf(value) {
|
|
638
|
+
return Math.exp(-(value * value) / 2) / Math.sqrt(2 * Math.PI);
|
|
639
|
+
}
|
|
640
|
+
export function standardNormalCdf(value) {
|
|
641
|
+
return 0.5 * (1 + erfApprox(value / Math.SQRT2));
|
|
642
|
+
}
|
|
643
|
+
export function inverseStandardNormal(probability) {
|
|
644
|
+
if (!(probability > 0 && probability < 1)) {
|
|
645
|
+
return undefined;
|
|
646
|
+
}
|
|
647
|
+
const a = [
|
|
648
|
+
-39.69683028665376, 220.9460984245205, -275.9285104469687, 138.357751867269, -30.66479806614716,
|
|
649
|
+
2.506628277459239,
|
|
650
|
+
];
|
|
651
|
+
const b = [
|
|
652
|
+
-54.47609879822406, 161.5858368580409, -155.6989798598866, 66.80131188771972,
|
|
653
|
+
-13.28068155288572,
|
|
654
|
+
];
|
|
655
|
+
const c = [
|
|
656
|
+
-0.007784894002430293, -0.3223964580411365, -2.400758277161838, -2.549732539343734,
|
|
657
|
+
4.374664141464968, 2.938163982698783,
|
|
658
|
+
];
|
|
659
|
+
const d = [0.007784695709041462, 0.3224671290700398, 2.445134137142996, 3.754408661907416];
|
|
660
|
+
const lower = 0.02425;
|
|
661
|
+
const upper = 1 - lower;
|
|
662
|
+
if (probability < lower) {
|
|
663
|
+
const q = Math.sqrt(-2 * Math.log(probability));
|
|
664
|
+
return ((((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
|
|
665
|
+
((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1));
|
|
666
|
+
}
|
|
667
|
+
if (probability > upper) {
|
|
668
|
+
const q = Math.sqrt(-2 * Math.log(1 - probability));
|
|
669
|
+
return -((((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
|
|
670
|
+
((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1));
|
|
671
|
+
}
|
|
672
|
+
const q = probability - 0.5;
|
|
673
|
+
const r = q * q;
|
|
674
|
+
return (((((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q) /
|
|
675
|
+
(((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1));
|
|
676
|
+
}
|
|
677
|
+
export function skewSample(numbers) {
|
|
678
|
+
if (numbers.length < 3) {
|
|
679
|
+
return undefined;
|
|
680
|
+
}
|
|
681
|
+
const mean = numbers.reduce((sum, value) => sum + value, 0) / numbers.length;
|
|
682
|
+
const stddev = sampleStandardDeviation(numbers);
|
|
683
|
+
if (!(stddev > 0)) {
|
|
684
|
+
return undefined;
|
|
685
|
+
}
|
|
686
|
+
const moment3 = numbers.reduce((sum, value) => sum + (value - mean) ** 3, 0);
|
|
687
|
+
const n = numbers.length;
|
|
688
|
+
return (n * moment3) / ((n - 1) * (n - 2) * stddev ** 3);
|
|
689
|
+
}
|
|
690
|
+
export function skewPopulation(numbers) {
|
|
691
|
+
if (numbers.length === 0) {
|
|
692
|
+
return undefined;
|
|
693
|
+
}
|
|
694
|
+
const mean = numbers.reduce((sum, value) => sum + value, 0) / numbers.length;
|
|
695
|
+
const stddev = populationStandardDeviation(numbers);
|
|
696
|
+
if (!(stddev > 0)) {
|
|
697
|
+
return undefined;
|
|
698
|
+
}
|
|
699
|
+
const moment3 = numbers.reduce((sum, value) => sum + (value - mean) ** 3, 0) / numbers.length;
|
|
700
|
+
return moment3 / stddev ** 3;
|
|
701
|
+
}
|
|
702
|
+
export function kurtosis(numbers) {
|
|
703
|
+
if (numbers.length < 4) {
|
|
704
|
+
return undefined;
|
|
705
|
+
}
|
|
706
|
+
const mean = numbers.reduce((sum, value) => sum + value, 0) / numbers.length;
|
|
707
|
+
const stddev = sampleStandardDeviation(numbers);
|
|
708
|
+
if (!(stddev > 0)) {
|
|
709
|
+
return undefined;
|
|
710
|
+
}
|
|
711
|
+
const n = numbers.length;
|
|
712
|
+
const sum4 = numbers.reduce((sum, value) => sum + ((value - mean) / stddev) ** 4, 0);
|
|
713
|
+
return ((n * (n + 1) * sum4) / ((n - 1) * (n - 2) * (n - 3)) - (3 * (n - 1) ** 2) / ((n - 2) * (n - 3)));
|
|
714
|
+
}
|
|
715
|
+
export function percentileNormal(mean, standardDeviation, value) {
|
|
716
|
+
return standardNormalCdf((value - mean) / standardDeviation);
|
|
717
|
+
}
|
|
718
|
+
export function inverseNormal(probability, mean, standardDeviation) {
|
|
719
|
+
const z = inverseStandardNormal(probability);
|
|
720
|
+
return z === undefined ? undefined : mean + standardDeviation * z;
|
|
721
|
+
}
|
|
722
|
+
//# sourceMappingURL=distributions.js.map
|