Files
dpb/backend/scripts/breeding_stats/fdist.py
T
34047007@qq.com b95053c52c init: 初始化 dpb 桃育种系统代码库
前后端 + 后端 FastAPI 全量源码、部署脚本与文档。
2026-08-06 00:17:49 +08:00

98 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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))