Files
dpb/backend/scripts/breeding_stats/fdist.py
T

98 lines
2.9 KiB
Python
Raw Normal View History

"""F 分布 CDF / p 值(纯标准库,无需 scipy)。
用正则不完全 beta 连分数(Numerical Recipes betacf/Lentz 法)计算
P(F ≤ f),供 ANOVA / 配合力显著性检验共用。
"""
from __future__ import annotations
import math
def _betacf(a: float, b: float, x: float, itmax: int = 200, eps: float = 3e-14) -> float:
qab, qap, qam = a + b, a + 1.0, a - 1.0
c = 1.0
d = 1.0 - qab * x / qap
if abs(d) < 1e-30:
d = 1e-30
d = 1.0 / d
h = d
for m in range(1, itmax + 1):
m2 = 2 * m
aa = m * (b - m) * x / ((qam + m2) * (a + m2))
d = 1.0 + aa * d
if abs(d) < 1e-30:
d = 1e-30
c = 1.0 + aa / c
if abs(c) < 1e-30:
c = 1e-30
d = 1.0 / d
h *= d * c
aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2))
d = 1.0 + aa * d
if abs(d) < 1e-30:
d = 1e-30
c = 1.0 + aa / c
if abs(c) < 1e-30:
c = 1e-30
d = 1.0 / d
delta = d * c
h *= delta
if abs(delta - 1.0) < eps:
break
return h
def _reg_beta(a: float, b: float, x: float) -> float:
"""正则不完全 beta I_x(a,b)。"""
if x <= 0.0:
return 0.0
if x >= 1.0:
return 1.0
ln_beta = math.lgamma(a) + math.lgamma(b) - math.lgamma(a + b)
bt = math.exp(a * math.log(x) + b * math.log1p(-x) - ln_beta)
if x < (a + 1.0) / (a + b + 2.0):
return bt * _betacf(a, b, x) / a
return 1.0 - bt * _betacf(b, a, 1.0 - x) / b
def f_cdf(f: float, d1: float, d2: float) -> float:
"""F 分布累积分布函数 P(F ≤ f)d1/d2 为两个自由度。"""
if f <= 0.0 or d1 <= 0.0 or d2 <= 0.0:
return 0.0
x = d1 * f / (d1 * f + d2)
return _reg_beta(d1 / 2.0, d2 / 2.0, x)
def f_pvalue(f: float, d1: float, d2: float) -> float:
"""双侧(右尾) p 值 = 1 - P(F ≤ f)。"""
return 1.0 - f_cdf(f, d1, d2)
def f_icdf(p: float, d1: float, d2: float, *, tol: float = 1e-9, max_iter: int = 200) -> float:
"""F 分布分位数(CDF 反解):求 f 使 P(F ≤ f) = p。
用二分法(f_cdf 关于 f 严格递增),适用于 DUS LSD 等需临界值场景;
高自由度下 F 值可能很大,上界从 1 起按 2 倍扩展。
"""
p = max(min(p, 1.0 - 1e-12), 1e-12)
lo, hi = 0.0, 1.0
while f_cdf(hi, d1, d2) < p and hi < 1e12:
hi *= 2.0
for _ in range(max_iter):
mid = 0.5 * (lo + hi)
if f_cdf(mid, d1, d2) < p:
lo = mid
else:
hi = mid
if hi - lo < tol:
break
return 0.5 * (lo + hi)
def t_crit(alpha: float, df: float) -> float:
"""双侧 t 分布临界值:P(|t| > t_crit) = alpha。
利用 t² ~ F(1, df)df 为误差自由度),t_crit = √F_icdf(1-alpha, 1, df)。
"""
return math.sqrt(f_icdf(1.0 - alpha, 1.0, df))