Files
dpb/backend/app/api/v1/module_bre/statistics/schema.py
T

674 lines
30 KiB
Python
Raw Normal View History

# -*- 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")