SPB Git forge

spb/uqo-eval

Public
55commits 2branches 0releases
134.4 MBsize
maindefault branch
yesterdaylast push
TypeScript 90.1% JavaScript 3.2% Python 3.2% CSS 1.8% HTML 1.8%
5.4 KB · 162 lines typescript
Raw Blame History
1// UQO Éval — lois de probabilité pour l'inférence (t, F, χ², normale) sans dépendance.2// Implémentations classiques (Numerical Recipes) : fonction bêta incomplète régularisée et gamma incomplète.34function lnGamma(x: number): number {5  const cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5];6  let y = x;7  const tmp = x + 5.5 - (x + 0.5) * Math.log(x + 5.5);8  let ser = 1.000000000190015;9  for (let j = 0; j < 6; j++) ser += cof[j] / ++y;10  return -tmp + Math.log((2.5066282746310005 * ser) / x);11}1213function betacf(a: number, b: number, x: number): number {14  const MAXIT = 300;15  const EPS = 3e-14;16  const FPMIN = 1e-300;17  const qab = a + b;18  const qap = a + 1;19  const qam = a - 1;20  let c = 1;21  let d = 1 - (qab * x) / qap;22  if (Math.abs(d) < FPMIN) d = FPMIN;23  d = 1 / d;24  let h = d;25  for (let m = 1; m <= MAXIT; m++) {26    const m2 = 2 * m;27    let aa = (m * (b - m) * x) / ((qam + m2) * (a + m2));28    d = 1 + aa * d;29    if (Math.abs(d) < FPMIN) d = FPMIN;30    c = 1 + aa / c;31    if (Math.abs(c) < FPMIN) c = FPMIN;32    d = 1 / d;33    h *= d * c;34    aa = (-(a + m) * (qab + m) * x) / ((a + m2) * (qap + m2));35    d = 1 + aa * d;36    if (Math.abs(d) < FPMIN) d = FPMIN;37    c = 1 + aa / c;38    if (Math.abs(c) < FPMIN) c = FPMIN;39    d = 1 / d;40    const del = d * c;41    h *= del;42    if (Math.abs(del - 1) < EPS) break;43  }44  return h;45}4647/** I_x(a, b) — bêta incomplète régularisée. */48export function betaInc(a: number, b: number, x: number): number {49  if (x <= 0) return 0;50  if (x >= 1) return 1;51  const bt = Math.exp(lnGamma(a + b) - lnGamma(a) - lnGamma(b) + a * Math.log(x) + b * Math.log(1 - x));52  if (x < (a + 1) / (a + b + 2)) return (bt * betacf(a, b, x)) / a;53  return 1 - (bt * betacf(b, a, 1 - x)) / b;54}5556/** P(a, x) — gamma incomplète régularisée. */57export function gammaInc(a: number, x: number): number {58  if (x <= 0) return 0;59  if (x < a + 1) {60    let ap = a;61    let sum = 1 / a;62    let del = sum;63    for (let n = 0; n < 500; n++) {64      ap += 1;65      del *= x / ap;66      sum += del;67      if (Math.abs(del) < Math.abs(sum) * 3e-14) break;68    }69    return sum * Math.exp(-x + a * Math.log(x) - lnGamma(a));70  }71  // fraction continue (Lentz)72  const FPMIN = 1e-300;73  let b = x + 1 - a;74  let c = 1 / FPMIN;75  let d = 1 / b;76  let h = d;77  for (let i = 1; i < 500; i++) {78    const an = -i * (i - a);79    b += 2;80    d = an * d + b;81    if (Math.abs(d) < FPMIN) d = FPMIN;82    c = b + an / c;83    if (Math.abs(c) < FPMIN) c = FPMIN;84    d = 1 / d;85    const del = d * c;86    h *= del;87    if (Math.abs(del - 1) < 3e-14) break;88  }89  return 1 - Math.exp(-x + a * Math.log(x) - lnGamma(a)) * h;90}9192/** Φ(z). */93export function normalCdf(z: number): number {94  return 0.5 * (1 + erf(z / Math.SQRT2));95}9697function erf(x: number): number {98  // Abramowitz-Stegun 7.1.26 raffiné (erreur < 1.5e-7) — suffisant pour des p-valeurs99  const t = 1 / (1 + 0.3275911 * Math.abs(x));100  const y = 1 - ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * Math.exp(-x * x);101  return x >= 0 ? y : -y;102}103104/** Quantile de la normale standard (Acklam). */105export function normalQuantile(p: number): number {106  if (p <= 0) return -Infinity;107  if (p >= 1) return Infinity;108  const a = [-3.969683028665376e1, 2.209460984245205e2, -2.759285104469687e2, 1.38357751867269e2, -3.066479806614716e1, 2.506628277459239];109  const b = [-5.447609879822406e1, 1.615858368580409e2, -1.556989798598866e2, 6.680131188771972e1, -1.328068155288572e1];110  const c = [-7.784894002430293e-3, -3.223964580411365e-1, -2.400758277161838, -2.549732539343734, 4.374664141464968, 2.938163982698783];111  const d = [7.784695709041462e-3, 3.224671290700398e-1, 2.445134137142996, 3.754408661907416];112  const pl = 0.02425;113  let q: number, r: number;114  if (p < pl) {115    q = Math.sqrt(-2 * Math.log(p));116    return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);117  }118  if (p <= 1 - pl) {119    q = p - 0.5;120    r = q * q;121    return ((((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q) / (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);122  }123  q = Math.sqrt(-2 * Math.log(1 - p));124  return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);125}126127/** p-valeur bilatérale de Student. */128export function tPValue(t: number, df: number): number {129  if (!Number.isFinite(t)) return NaN;130  if (df <= 0) return NaN;131  const x = df / (df + t * t);132  return betaInc(df / 2, 0.5, x);133}134135/** Quantile t (bissection sur la CDF — suffisant). */136export function tQuantile(p: number, df: number): number {137  if (df > 200) return normalQuantile(p);138  let lo = 0;139  let hi = 50;140  const target = 2 * (1 - p); // p-valeur bilatérale correspondante141  for (let i = 0; i < 80; i++) {142    const mid = (lo + hi) / 2;143    if (tPValue(mid, df) > target) lo = mid;144    else hi = mid;145  }146  return (lo + hi) / 2;147}148149/** P(χ²_df > x). */150export function chi2Sf(x: number, df: number): number {151  if (x <= 0) return 1;152  return 1 - gammaInc(df / 2, x / 2);153}154155/** P(F_{d1,d2} > f). */156export function fSf(f: number, d1: number, d2: number): number {157  if (f <= 0) return 1;158  return betaInc(d2 / 2, d1 / 2, d2 / (d2 + d1 * f));159}160161export const stars = (p: number): string => (p < 0.001 ? "***" : p < 0.01 ? "**" : p < 0.05 ? "*" : p < 0.1 ? "·" : "");162