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

674 lines
30 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.
# -*- 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="性状 idbre_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 / 组合×siteyear 同理)"
)
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/ρ_colFalse 时用单参 ρ(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(IM)aKempthorne-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="性状 idbre_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="性状 idbre_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="性状 idbre_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="试验研究点 idbre_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="性状 idbre_trait")
trait_code: str = Field(..., description="性状编码")
year: int | None = Field(None, description="评价年份过滤;为空则全部")
block: bool = Field(
False, description="RCBD 设计基 ANOVATrue=从残差析出区组效应(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="性状 idbre_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="性状 idbre_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="性状 idbre_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="基因型数据集 idbre_genotyping_dataset")
trait_id: int = Field(..., description="性状 idbre_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="基因型数据集 idbre_genotyping_dataset")
trait_id: int = Field(..., description="性状 idbre_trait")
trait_code: str = Field(..., description="性状编码")
year: int | None = Field(None, description="评价年份过滤;为空则全部")
method: str = Field("gwas", description="gwasGLM+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="基因型数据集 idbre_genotyping_dataset")
trait_id: int = Field(..., description="性状 idbre_trait")
trait_code: str = Field(..., description="性状编码")
year: int | None = Field(None, description="评价年份过滤;为空则全部")
method: str = Field("gwas", description="gwasGLM+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="性状 idbre_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="峰标记 idbre_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="目标性状 idbre_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="标记 idbre_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")