674 lines
30 KiB
Python
674 lines
30 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""育种统计 Schema(一期 2 个最基础:ABLUP·EBV + 配合力 GCA/SCA)"""
|
||
from datetime import datetime
|
||
|
||
from pydantic import BaseModel, ConfigDict, Field
|
||
|
||
|
||
class RunStatsIn(BaseModel):
|
||
"""提交统计任务的入参。"""
|
||
trait_id: int = Field(..., description="性状 id(bre_trait)")
|
||
trait_code: str = Field(..., description="性状编码(bre_trait.trait_code,如 avg_fruit_weight)")
|
||
year: int | None = Field(None, description="评价年份过滤;为空则全部")
|
||
fixed_effects: list[str] | None = Field(
|
||
None, description="固定效应因子:支持 trial_study(试验/站点) / rootstock(砧木)"
|
||
)
|
||
covariate: str | None = Field(
|
||
None, description="协变量(BLUP 校正):crop_load(负载量) / competition(空间竞争:相邻株数)"
|
||
)
|
||
data_gate: bool = Field(
|
||
True, description="数据就绪门禁(G1):每 clone/家系最小 n、系谱完整率、缺失率任一不达标则拒绝运行"
|
||
)
|
||
min_clone_n: int = Field(
|
||
2, ge=0, description="每 clone/家系最小样本量(观测树数低于该值的组列入未达标)"
|
||
)
|
||
min_pedigree_rate: float = Field(
|
||
0.3, ge=0.0, le=1.0, description="系谱完整率下限(有父母或组合亲本的观测树占比)"
|
||
)
|
||
max_missing_rate: float = Field(
|
||
0.8, ge=0.0, le=1.0, description="表型缺失率上限(缺失=有评价记录但无该性状数值)"
|
||
)
|
||
gxe: bool = Field(
|
||
False, description="启用 G×E 交互随机效应(clone×site / 组合×site;year 同理)"
|
||
)
|
||
gxe_env: str = Field(
|
||
"site", description="G×E 环境维度:site(trial_study_id) / year(evaluate_year)"
|
||
)
|
||
gxr: bool = Field(
|
||
False, description="启用砧木×接穗随机互作(G×R):rootstock 从固定效应移到随机效应,与 gxe 互斥"
|
||
)
|
||
spatial: bool = Field(
|
||
False,
|
||
description=(
|
||
"启用 AR1×AR1 空间协方差(残差 e~N(0,σ²e·R),R_ij=ρ^(|Δrow|+|Δcol|)):"
|
||
"需观测株均有 bre_tree.row_no/col_no;与 gxe/gxr 互斥"
|
||
),
|
||
)
|
||
spatial_aniso: bool = Field(
|
||
False,
|
||
description=(
|
||
"空间协方差各向异性双参数(需 spatial=True):R_ij=ρ_row^|Δrow|·ρ_col^|Δcol|,"
|
||
"方法 AR1×AR1(aniso),输出 ρ_row/ρ_col;False 时用单参 ρ(v1 兼容)"
|
||
),
|
||
)
|
||
block: bool = Field(
|
||
False,
|
||
description=(
|
||
"启用区组随机效应(不完全区组/增广/α-格子):bre_tree.block_no 作第二随机效应,"
|
||
"精度收益进入遗传评估;需 ≥2 个区组;与 gxe/gxr/spatial 互斥"
|
||
),
|
||
)
|
||
stage: str | None = Field(
|
||
None, description="发育阶段拆分:juvenile(童期)/evaluation(成株);给定则只取该阶段观测建模并拆独立批次"
|
||
)
|
||
design_type: str = Field(
|
||
"full_diallel",
|
||
description=(
|
||
"交配设计(配合力分析用):full_diallel=完全双列(Griffing) / "
|
||
"partial_diallel=部分双列 / line_tester=line×tester(NCII 两因素模型) / "
|
||
"nciii=NCIII 测交"
|
||
),
|
||
)
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class PredictionOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
model_name: str | None = None
|
||
trait_id: int | None = None
|
||
method: str | None = None
|
||
stage: str | None = None
|
||
accuracy: float | None = None
|
||
train_n: int | None = None
|
||
heritability: float | None = None
|
||
data_version: str | None = None
|
||
input_hash: str | None = None
|
||
engine_version: str | None = None
|
||
is_active: bool | None = None
|
||
note: str | None = None
|
||
created_time: datetime | None = None
|
||
|
||
|
||
class PredictionValueOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
prediction_id: int
|
||
germplasm_id: int | None = None
|
||
tree_id: int | None = None
|
||
trait_id: int | None = None
|
||
predicted_value: float | None = None
|
||
reliability: float | None = None
|
||
pa: float | None = None
|
||
rank: int | None = None
|
||
|
||
|
||
class CombiningAbilityOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
model_name: str | None = None
|
||
trait_id: int | None = None
|
||
method: str | None = None
|
||
design_type: str | None = None
|
||
gca_json: dict | None = None
|
||
sca_json: dict | None = None
|
||
anova_json: dict | None = None
|
||
|
||
|
||
class StatisticsJobOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
job_type: str | None = None
|
||
status: str
|
||
params_json: dict | None = None
|
||
result_ref: int | None = None
|
||
error_msg: str | None = None
|
||
started_time: datetime | None = None
|
||
finished_time: datetime | None = None
|
||
|
||
|
||
# ---------- 纯 Python 统计分析(不依赖 R) ----------
|
||
class DescribeStatsIn(BaseModel):
|
||
"""描述性统计入参。"""
|
||
trait_codes: list[str] | None = Field(None, description="数值性状列名;为空则用全部数值列")
|
||
group_by: str = Field("none", description="分组维度:none/year/combination")
|
||
year: int | None = Field(None, description="评价年份过滤")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class CorrelationIn(BaseModel):
|
||
"""相关分析入参。"""
|
||
trait_codes: list[str] | None = Field(None, description="参与相关的数值性状;至少2个")
|
||
mode: str = Field(
|
||
"pheno",
|
||
description="相关类型:pheno=表型相关(皮尔逊,纯Python);genetic=遗传相关(成对双性状BLUP REML)",
|
||
)
|
||
g_method: str = Field(
|
||
"mtblup",
|
||
description="遗传相关 r_g 来源(仅 mode=genetic):mtblup=成对双性状BLUP(REML精确);calo=可靠性校正EBV相关,无生效EBV批次时回退mtblup",
|
||
)
|
||
year: int | None = Field(None, description="评价年份过滤")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class SelectionIndexIn(BaseModel):
|
||
"""选择指数入参。"""
|
||
weights: dict[str, float] | None = Field(None, description="性状->权重,缺省等权;自动归一化")
|
||
year: int | None = Field(None, description="评价年份过滤(仅表型回退路径使用)")
|
||
top_n: int = Field(50, description="返回排名前 N 的单株")
|
||
batch_ids: dict[str, int] | None = Field(None, description="性状->ABLUP批次id,指定则用该批次的EBV作为指数输入")
|
||
use_h2: bool = Field(True, description="有效权重 = 用户权重 × 遗传力(h²) 加权(仅 zsum 模式生效)")
|
||
method: str = Field(
|
||
"zsum",
|
||
description=(
|
||
"zsum=加权Z综合(轻量,可表型回退);"
|
||
"smith_hazel=真Smith-Hazel b=P⁻¹Ga(需各性状指定EBV批次且含h²);"
|
||
"restricted=约束指数 b=P⁻¹G(I−M)a(Kempthorne-Nordskog,受限性状 ΔG=0,需 restricted_traits)"
|
||
),
|
||
)
|
||
aggregate: str = Field(
|
||
"clone",
|
||
description=(
|
||
"聚合粒度:clone=无性系级(同系多株可靠加权聚合为一个遗传实体再排名,桃无性繁殖默认);"
|
||
"tree=单株级(旧行为)"
|
||
),
|
||
)
|
||
stage: str | None = Field(
|
||
None, description="发育阶段过滤:juvenile(童期)/evaluation(成株);不传且性状横跨两阶段时返回 stage_warning"
|
||
)
|
||
g_method: str = Field(
|
||
"calo", description="遗传相关 r_g 来源(smith_hazel / restricted 生效):calo=可靠性校正EBV相关;mtblup=成对双性状BLUP(REML精确估计,单对不收敛回退calo)"
|
||
)
|
||
auto_weights: bool = Field(
|
||
False,
|
||
description="自动权重:以各性状 default_h2(实测h²优先,兜底0.1)为权重;强制 use_h2=False 防双重相乘",
|
||
)
|
||
restricted_traits: list[str] | None = Field(
|
||
None, description="受限性状列表(仅 method=restricted 生效):这些性状的遗传增益被约束为 0,其余性状自由响应(Kempthorne-Nordskog 闭式投影)"
|
||
)
|
||
economic_weights: dict[str, float] | None = Field(
|
||
None, description="经济权重(经济价值,绝对尺度不归一化;smith_hazel / restricted 生效):直接作为聚合基因型系数 a,未列出性状按 0 处理"
|
||
)
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class SelectionIndexOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
model_name: str | None = None
|
||
method: str | None = None
|
||
weights_json: dict | None = None
|
||
batch_refs_json: dict | None = None
|
||
heritability_json: dict | None = None
|
||
top_n: int | None = None
|
||
result_json: dict | None = None
|
||
created_time: datetime | None = None
|
||
|
||
|
||
class SelectionIndexApplyIn(BaseModel):
|
||
"""选择指数 -> 决选 入参。"""
|
||
top_n: int = Field(50, description="写入前 N 名单株")
|
||
selection_year: int | None = Field(None, description="入选年份;为空用当年")
|
||
rule_id: int | None = Field(None, description="来源选择规则(决策预览选定,用于溯源 rule_id/from_stage/to_stage)")
|
||
min_reliability: float = Field(
|
||
0.2, ge=0.0, le=1.0,
|
||
description="EBV 可靠性硬门槛:指数引用批次的 EBV 可靠性低于该值的单株跳过(证据不足不入选)",
|
||
)
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class KinshipIn(BaseModel):
|
||
"""亲缘/近交分析入参。"""
|
||
threshold: float = Field(0.25, description="亲缘系数预警阈值(r>A 预警)")
|
||
tree_ids: list[int] | None = Field(None, description="限定单株范围;为空用全部活动单株")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class DataQualityIn(BaseModel):
|
||
"""数据质量/异常值诊断入参。"""
|
||
trait_id: int = Field(..., description="性状 id(bre_trait)")
|
||
trait_code: str = Field(..., description="性状编码")
|
||
year: int | None = Field(None, description="评价年份过滤;为空则全部")
|
||
trial_study_id: int | None = Field(None, description="限定试验/站点(MET);为空则全部")
|
||
dataset_id: int | None = Field(
|
||
None, description="基因型数据集:提供时附加孟德尔检验段(最新批次计数+flag树+隔离状态)")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class GeneticGainIn(BaseModel):
|
||
"""ΔG 遗传增益投影入参。"""
|
||
trait_id: int = Field(..., description="性状 id(bre_trait)")
|
||
trait_code: str = Field(..., description="性状编码")
|
||
prediction_id: int | None = Field(None, description="指定 EBV 批次;为空用该性状最新批次")
|
||
top_p: float | None = Field(None, gt=0, lt=1, description="入选比例(0-1,与 top_n 二选一)")
|
||
top_n: int | None = Field(None, ge=1, description="入选株数(与 top_p 二选一)")
|
||
generation_interval: float = Field(1.0, gt=0, description="世代间隔(年);年增益=ΔG/L")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class InbreedingDepressionIn(BaseModel):
|
||
"""近交衰退分析入参。"""
|
||
trait_id: int = Field(..., description="性状 id(bre_trait)")
|
||
trait_code: str = Field(..., description="性状编码")
|
||
year: int | None = Field(None, description="评价年份过滤;为空则全部")
|
||
trial_study_id: int | None = Field(None, description="限定试验/站点(MET);为空则全部")
|
||
min_n: int = Field(10, ge=2, description="回归最小样本量(同时具备 F 与表型的树)")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class TrialDesignIn(BaseModel):
|
||
"""试验设计生成入参(rcbd / augmented / alpha)。"""
|
||
trial_study_id: int = Field(..., description="试验研究点 id(bre_trial_study)")
|
||
design_type: str = Field("rcbd", description="设计类型:rcbd=随机完全区组、augmented=增广、alpha=α-格子")
|
||
seed: int | None = Field(None, description="随机种子;固定 seed 可复现同一设计")
|
||
check_germplasm_ids: list[int] = Field(default_factory=list,
|
||
description="增广设计对照种质 id 列表(每区组重复)")
|
||
block_size: int | None = Field(None, description="α-格子区组大小 k(每重复区组块大小)")
|
||
reps: int | None = Field(None, description="α-格子重复数 r(平方格子缺省 k+1)")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class MatingRecommendIn(BaseModel):
|
||
"""主动选配推荐入参。"""
|
||
candidate_germplasm_ids: list[int] = Field(..., description="候选亲本种质 id 集合(≥2)")
|
||
prediction_id: int | None = Field(None, description="EBV 批次;为空用最新 ABLUP/GBLUP 批次")
|
||
kinship_threshold: float = Field(0.25, description="亲缘惩罚阈值(r>阈值开始扣分)")
|
||
w_ebv: float = Field(1.0, ge=0, description="EBV 互补权重")
|
||
w_kin: float = Field(1.0, ge=0, description="近交惩罚权重")
|
||
max_pairs: int = Field(20, ge=1, le=500, description="返回 top N 配对")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class OcsIn(BaseModel):
|
||
"""最优贡献选择(OCS)入参。"""
|
||
candidate_germplasm_ids: list[int] = Field(..., description="候选亲本种质 id 集合")
|
||
n_select: int = Field(5, ge=1, description="选择数量(贡献总和 Σc=n_select)")
|
||
lam: float = Field(0.1, ge=0, description="近交约束权重 λ:越大越压低子代平均亲缘(avg_kinship 单调非增),0=无近交约束(满额投最高 EBV 单亲)")
|
||
prediction_id: int | None = Field(None, description="EBV 批次;为空用最新 ABLUP/GBLUP 批次")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class MabcIn(BaseModel):
|
||
"""MABC 标记辅助回交进度入参。"""
|
||
candidate_tree_ids: list[int] = Field(..., description="候选回交单株树 id 集合(BC 世代分离群体)")
|
||
foreground_panel_ids: list[int] = Field(..., description="前景选择面板 id(目标性状 MAS 面板)")
|
||
background_panel_ids: list[int] = Field(..., description="背景恢复面板 id(全基因组标记面板)")
|
||
recurrent_parent_tree_id: int = Field(..., description="轮回亲本树 id(背景纯合一致对照)")
|
||
foreground_min_hits: int = Field(1, ge=1, description="前景通过阈值(有利剂量命中标记数 ≥ 此值,stage 无关、童期可用)")
|
||
generation: str = Field("BC1", description="候选当前回交代(F1/BC1/BC2/BC3),用于晋级建议")
|
||
background_target: float = Field(90.0, gt=0, le=100, description="背景恢复目标 %(达到则建议晋级下一回交代)")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class AnovaIn(BaseModel):
|
||
"""ANOVA / 广义遗传力 H² 入参。"""
|
||
trait_id: int = Field(..., description="性状 id(bre_trait)")
|
||
trait_code: str = Field(..., description="性状编码")
|
||
year: int | None = Field(None, description="评价年份过滤;为空则全部")
|
||
block: bool = Field(
|
||
False, description="RCBD 设计基 ANOVA:True=从残差析出区组效应(SS_block/F_block/p_block),遗传差异为区组校正后;需观测株 bre_tree.block_no")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class AnovaOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
model_name: str | None = None
|
||
trait_id: int | None = None
|
||
method: str | None = None
|
||
result_json: dict | None = None
|
||
created_time: datetime | None = None
|
||
|
||
|
||
class CvRunIn(BaseModel):
|
||
"""k-fold 交叉验证入参。"""
|
||
trait_id: int = Field(..., description="性状 id(bre_trait)")
|
||
trait_code: str = Field(..., description="性状编码")
|
||
year: int | None = Field(None, description="评价年份过滤;为空则全部")
|
||
fixed_effects: list[str] | None = Field(
|
||
None, description="固定效应因子:trial_study(试验/站点) / rootstock(砧木)"
|
||
)
|
||
covariate: str | None = Field(
|
||
None, description="协变量:crop_load(负载量) / competition(空间竞争)"
|
||
)
|
||
gxe: bool = Field(False, description="启用 G×E 交互随机效应")
|
||
gxe_env: str = Field("site", description="G×E 环境维度:site / year")
|
||
k: int = Field(5, ge=2, le=10, description="折数")
|
||
dataset_id: int | None = Field(
|
||
None, description="基因型数据集 id;给定则走 GS 交叉验证(GBLUP/ssGBLUP/rrBLUP/BayesB)"
|
||
)
|
||
method: str | None = Field(
|
||
None, description="GS 方法:gblup / ssgblup / rrblup / bayesb(仅 dataset_id 给定时生效)"
|
||
)
|
||
maf_min: float = Field(0.05, description="MAF 下限(GS 交叉验证用)")
|
||
split: str = Field(
|
||
"random", description="GS 折划分:random=固定种子随机分层(默认)/ family=家系阻塞折(同父半同胞同折,防亲缘泄漏)"
|
||
)
|
||
seed: int = Field(20260805, description="BayesB 随机种子(折划分与 Gibbs 共用,固定可复现)")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class CvResultOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
trait_id: int | None = None
|
||
trait_code: str | None = None
|
||
method: str | None = None
|
||
k: int | None = None
|
||
n_total: int | None = None
|
||
n_individuals: int | None = None
|
||
mean_pearson: float | None = None
|
||
mean_rmse: float | None = None
|
||
pooled_pearson: float | None = None
|
||
pooled_rmse: float | None = None
|
||
cv_accuracy: float | None = None
|
||
h2: float | None = None
|
||
data_version: str | None = None
|
||
input_hash: str | None = None
|
||
engine_version: str | None = None
|
||
note: str | None = None
|
||
created_time: datetime | None = None
|
||
|
||
|
||
class CvFoldOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
cv_result_id: int
|
||
fold_idx: int | None = None
|
||
n_train: int | None = None
|
||
n_test: int | None = None
|
||
n_eval: int | None = None
|
||
pearson: float | None = None
|
||
rmse: float | None = None
|
||
error: str | None = None
|
||
|
||
|
||
class DecisionPreviewIn(BaseModel):
|
||
"""选择规则 × 表型/EBV/标记(MAS) 决策预览入参。"""
|
||
rule_ids: list[int] | None = Field(None, description="指定规则;为空用全部启用规则")
|
||
prediction_id: int | None = Field(None, description="EBV 源批次;为空用最新 ABLUP 批次")
|
||
year: int | None = Field(None, description="表型均值年份过滤")
|
||
min_reliability: float = Field(
|
||
0.2, ge=0.0, le=1.0,
|
||
description="EBV 可靠性门槛:单株 EBV 可靠性低于该值视为证据不足,不参与晋级/淘汰判定;"
|
||
"ebv 条件内可配 min_reliability 逐性状覆盖",
|
||
)
|
||
stage: str | None = Field(
|
||
None, description="发育阶段过滤:juvenile(童期)/evaluation(成株);不传且规则引用性状横跨两阶段时返回 stage_warning"
|
||
)
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class DecisionPreviewOut(BaseModel):
|
||
rule_id: int | None = None
|
||
rule_name: str | None = None
|
||
action: str | None = None
|
||
stage: str | None = None
|
||
matched: bool | None = None
|
||
reasons: list[str] | None = None
|
||
tree_id: int | None = None
|
||
tree_no: str | None = None
|
||
combination_id: int | None = None
|
||
|
||
|
||
# ---------- AMMI / Finlay-Wilkinson 稳定性(§8.18) ----------
|
||
class StabilityRunIn(BaseModel):
|
||
"""稳定性分析入参。"""
|
||
trait_id: int = Field(..., description="性状 id(bre_trait)")
|
||
trait_code: str = Field(..., description="性状编码")
|
||
gxe_env: str = Field("site", description="环境维度:site(trial_study_id) / year(evaluate_year)")
|
||
year: int | None = Field(None, description="评价年份过滤;为空则全部")
|
||
methods: list[str] = Field(
|
||
["ammi", "finlay"], description="分析方法:ammi / finlay,可多选"
|
||
)
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class StabilityOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
trait_id: int | None = None
|
||
trait_code: str | None = None
|
||
env_dim: str | None = None
|
||
methods: str | None = None
|
||
detail_json: dict | None = None
|
||
data_version: str | None = None
|
||
input_hash: str | None = None
|
||
engine_version: str | None = None
|
||
note: str | None = None
|
||
created_time: datetime | None = None
|
||
|
||
|
||
# ---------- 遗传相关矩阵(MT-BLUP,§8.18) ----------
|
||
class GeneticCorrIn(BaseModel):
|
||
"""遗传相关矩阵入参。"""
|
||
trait_ids: list[int] = Field(..., description="参与遗传相关的性状 id(≥2)")
|
||
year: int | None = Field(None, description="评价年份过滤;为空则全部")
|
||
full_mtblup: bool = Field(
|
||
False, description="True=全多变量 EM-REML(一次估计完整 G0⊗A,替代逐对 bivariate)")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class GeneticCorrOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
trait_ids_json: dict | None = None
|
||
matrix_json: dict | None = None
|
||
sigma_a_json: dict | None = None
|
||
heritability_json: dict | None = None
|
||
pairs_json: dict | None = None
|
||
n_common: int | None = None
|
||
data_version: str | None = None
|
||
input_hash: str | None = None
|
||
engine_version: str | None = None
|
||
note: str | None = None
|
||
created_time: datetime | None = None
|
||
|
||
|
||
# ---------- Type-B 多环境遗传相关(§8.27) ----------
|
||
class TypeBIn(BaseModel):
|
||
"""Type-B 多环境遗传相关入参。"""
|
||
trait_id: int = Field(..., description="性状 id(bre_trait,数值型)")
|
||
trait_code: str = Field(..., description="性状编码")
|
||
env_dim: str = Field("site", description="环境维度:site(研究点 trial_study_id) / year(评价年份) / stage(发育阶段:juvenile童期 vs evaluation成株,估幼年-成年遗传相关)")
|
||
method: str = Field(
|
||
"reml", description="方法:reml=环境互为性状逐对REML双性状BLUP(默认);calo=分环境EBV相关/√rel(无环境级EBV时回退reml)"
|
||
)
|
||
year: int | None = Field(None, description="评价年份过滤;为空则全部")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class TypeBOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
trait_id: int | None = None
|
||
trait_code: str | None = None
|
||
env_dim: str | None = None
|
||
method: str | None = None
|
||
envs_json: dict | None = None
|
||
matrix_json: dict | None = None
|
||
pairs_json: dict | None = None
|
||
n_common: int | None = None
|
||
data_version: str | None = None
|
||
input_hash: str | None = None
|
||
engine_version: str | None = None
|
||
note: str | None = None
|
||
created_time: datetime | None = None
|
||
|
||
|
||
# ---------- UPGMA 聚类(§8.27,计算端点不落库) ----------
|
||
class ClusterIn(BaseModel):
|
||
"""UPGMA 聚类入参(实体=单株或无性系,按多性状轮廓聚类)。"""
|
||
trait_ids: list[int] = Field(..., description="参与聚类的数值性状 id(≥2)")
|
||
entity_type: str = Field("tree", description="聚类实体:tree=单株;clone=无性系(同系多株均值聚合)")
|
||
mode: str = Field("pheno", description="值来源:pheno=表型均值;genetic=EBV(无生效批次回退表型)")
|
||
distance: str = Field(
|
||
"corr", description="实体距离:corr=1-|皮尔逊r|(轮廓相似);euclidean=标准化欧氏距离"
|
||
)
|
||
k: int | None = Field(None, description="聚成 k 类;为空按合并距离最大跳变自动选择")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
# ---------- GBLUP / ssGBLUP 基因组选择(§8.18) ----------
|
||
class GblupRunIn(BaseModel):
|
||
"""GBLUP/ssGBLUP/rrBLUP/BayesB 基因组选择入参(§8.18)。"""
|
||
dataset_id: int = Field(..., description="基因型数据集 id(bre_genotyping_dataset)")
|
||
trait_id: int = Field(..., description="性状 id(bre_trait)")
|
||
trait_code: str = Field(..., description="性状编码")
|
||
year: int | None = Field(None, description="评价年份过滤;为空则全部")
|
||
method: str = Field("gblup", description="gblup(仅基因型株)/ ssgblup(单步法,含非基因型亲属)/ rrblup(岭回归逐标记,输出 marker effects)/ bayesb(贝叶斯可变选择,固定 seed 可复现)")
|
||
maf_min: float = Field(0.05, ge=0.0, lt=0.5, description="MAF 过滤下限")
|
||
seed: int = Field(20260805, description="BayesB 随机种子(固定保证可复现;MLOps 铁律)")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class GenotypingDatasetOut(BaseModel):
|
||
id: int
|
||
dataset_name: str | None = None
|
||
platform: str | None = None
|
||
panel: str | None = None
|
||
purpose: str | None = None
|
||
run_date: datetime | None = None
|
||
n_samples: int | None = None
|
||
|
||
|
||
# ---------- GWAS / QTL / MAS 标记辅助选择(§8.22) ----------
|
||
class GwasRunIn(BaseModel):
|
||
"""GWAS 关联分析入参(GLM+PC 首版)。"""
|
||
dataset_id: int = Field(..., description="基因型数据集 id(bre_genotyping_dataset)")
|
||
trait_id: int = Field(..., description="性状 id(bre_trait)")
|
||
trait_code: str = Field(..., description="性状编码")
|
||
year: int | None = Field(None, description="评价年份过滤;为空则全部")
|
||
method: str = Field("gwas", description="gwas(GLM+PC)/ emmax(混合模型 EMMAX)/ ssgwas(单步 GWAS)")
|
||
maf_min: float = Field(0.05, ge=0.0, lt=0.5, description="MAF 过滤下限")
|
||
n_pc: int = Field(3, ge=1, le=10, description="群体结构主成分数")
|
||
sig_level: float = Field(0.05, gt=0.0, le=1.0, description="显著性水平")
|
||
qtl_window: int = Field(1_000_000, ge=1, description="QTL 合并窗口(bp)")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class GwasQtlXEIn(BaseModel):
|
||
"""QTL×E 入参(按环境分层 GWAS + 稳定性判定,计算端点不建表)。"""
|
||
dataset_id: int = Field(..., description="基因型数据集 id(bre_genotyping_dataset)")
|
||
trait_id: int = Field(..., description="性状 id(bre_trait)")
|
||
trait_code: str = Field(..., description="性状编码")
|
||
year: int | None = Field(None, description="评价年份过滤;为空则全部")
|
||
method: str = Field("gwas", description="gwas(GLM+PC)/ emmax(混合模型 EMMAX)")
|
||
maf_min: float = Field(0.05, ge=0.0, lt=0.5, description="MAF 过滤下限")
|
||
n_pc: int = Field(3, ge=1, le=10, description="群体结构主成分数")
|
||
sig_level: float = Field(0.05, gt=0.0, le=1.0, description="显著性水平")
|
||
qtl_window: int = Field(1_000_000, ge=1, description="QTL 合并窗口(bp)")
|
||
env_dim: str = Field("site", description="环境维度:site(trial_study_id) / year(evaluate_year)")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class GwasResultOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
dataset_id: int | None = None
|
||
trait_id: int | None = None
|
||
trait_code: str | None = None
|
||
method: str | None = None
|
||
n_individuals: int | None = None
|
||
n_markers: int | None = None
|
||
m_after_maf: int | None = None
|
||
maf_min: float | None = None
|
||
n_pc: int | None = None
|
||
sig_level: float | None = None
|
||
threshold_bonf: float | None = None
|
||
n_sig_bonf: int | None = None
|
||
n_sig_fdr: int | None = None
|
||
n_qtl: int | None = None
|
||
data_version: str | None = None
|
||
input_hash: str | None = None
|
||
engine_version: str | None = None
|
||
remark: str | None = None
|
||
created_time: datetime | None = None
|
||
|
||
|
||
class GwasSnpOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
gwas_result_id: int
|
||
marker_id: int | None = None
|
||
marker_name: str | None = None
|
||
chromosome: str | None = None
|
||
position: int | None = None
|
||
maf: float | None = None
|
||
effect: float | None = None
|
||
se: float | None = None
|
||
t_value: float | None = None
|
||
p_value: float | None = None
|
||
neg_log10p: float | None = None
|
||
q_value: float | None = None
|
||
sig_bonf: bool | None = None
|
||
sig_fdr: bool | None = None
|
||
|
||
|
||
class QtlIn(BaseModel):
|
||
"""已知 QTL 录入(source=known)入参。"""
|
||
trait_id: int | None = Field(None, description="性状 id(bre_trait)")
|
||
chromosome: str | None = Field(None, description="染色体")
|
||
start_bp: int | None = Field(None, description="区间起点(bp)")
|
||
end_bp: int | None = Field(None, description="区间终点(bp)")
|
||
peak_marker_id: int | None = Field(None, description="峰标记 id(bre_marker)")
|
||
peak_p: float | None = Field(None, description="峰标记 p 值")
|
||
n_markers: int | None = Field(None, description="区间显著标记数")
|
||
effect: float | None = Field(None, description="峰效应")
|
||
remark: str | None = Field(None, description="备注")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class QtlOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
trait_id: int | None = None
|
||
chromosome: str | None = None
|
||
start_bp: int | None = None
|
||
end_bp: int | None = None
|
||
peak_marker_id: int | None = None
|
||
peak_marker_name: str | None = None
|
||
peak_p: float | None = None
|
||
n_markers: int | None = None
|
||
effect: float | None = None
|
||
source: str | None = None
|
||
gwas_result_id: int | None = None
|
||
remark: str | None = None
|
||
created_time: datetime | None = None
|
||
|
||
|
||
class MasPanelIn(BaseModel):
|
||
"""MAS 标记辅助选择面板入参。"""
|
||
panel_name: str = Field(..., description="面板名称")
|
||
trait_id: int | None = Field(None, description="目标性状 id(bre_trait)")
|
||
remark: str | None = Field(None, description="备注")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class MasPanelOut(BaseModel):
|
||
model_config = ConfigDict(from_attributes=True)
|
||
id: int
|
||
panel_name: str | None = None
|
||
trait_id: int | None = None
|
||
n_markers: int | None = None
|
||
remark: str | None = None
|
||
created_time: datetime | None = None
|
||
|
||
|
||
class MasPanelMarkerIn(BaseModel):
|
||
"""面板标记(有利剂量 + 方向 + 基因作用模式)。"""
|
||
marker_id: int | None = Field(None, description="标记 id(bre_marker)")
|
||
favorable_dose: int | None = Field(None, description="有利剂量(0/1/2)")
|
||
effect: float | None = Field(None, description="效应(参考)")
|
||
direction: str | None = Field("high", description="high=剂量≥favorable_dose命中 / low=剂量≤命中")
|
||
mode: str | None = Field("additive", description="基因作用模式:additive/dominance/recessive/allele/haplotype")
|
||
favorable_allele: str | None = Field(None, description="有利等位(allele/haplotype 模式用,SSR 等位索引)")
|
||
haplotype_group: str | None = Field(None, description="单倍型组(haplotype 模式:同组标记全命中才计 1)")
|
||
model_config = ConfigDict(extra="ignore")
|
||
|
||
|
||
class MasPanelSetMarkersIn(BaseModel):
|
||
"""面板标记全量替换入参。"""
|
||
markers: list[MasPanelMarkerIn] = Field(default_factory=list, description="面板标记列表")
|
||
model_config = ConfigDict(extra="ignore")
|