pygapit-ng 1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygapit/__init__.py +108 -0
- pygapit/_typing.py +81 -0
- pygapit/cli.py +299 -0
- pygapit/gapit.py +1405 -0
- pygapit/gs/__init__.py +3 -0
- pygapit/gs/blup.py +480 -0
- pygapit/gwas/__init__.py +20 -0
- pygapit/gwas/blink.py +316 -0
- pygapit/gwas/farmcpu.py +295 -0
- pygapit/gwas/glm.py +243 -0
- pygapit/gwas/mlm.py +234 -0
- pygapit/gwas/mlmm.py +375 -0
- pygapit/io/__init__.py +25 -0
- pygapit/io/formats.py +649 -0
- pygapit/models/genomic_prediction.py +349 -0
- pygapit/py.typed +1 -0
- pygapit/stats/__init__.py +26 -0
- pygapit/stats/emma.py +494 -0
- pygapit/stats/kinship.py +138 -0
- pygapit/stats/pca.py +136 -0
- pygapit/stats/testing.py +103 -0
- pygapit/utils/ld.py +105 -0
- pygapit/visualization/__init__.py +21 -0
- pygapit/visualization/plots.py +695 -0
- pygapit_ng-1.2.1.dist-info/METADATA +540 -0
- pygapit_ng-1.2.1.dist-info/RECORD +29 -0
- pygapit_ng-1.2.1.dist-info/WHEEL +4 -0
- pygapit_ng-1.2.1.dist-info/entry_points.txt +2 -0
- pygapit_ng-1.2.1.dist-info/licenses/LICENSE +232 -0
pygapit/__init__.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""GAPIT-style genomic association and prediction tools for Python."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = version("pygapit-ng")
|
|
7
|
+
except PackageNotFoundError: # source tree imported without installation
|
|
8
|
+
__version__ = "0+unknown"
|
|
9
|
+
__author__ = "pyGAPIT contributors (based on GAPIT by Jiabo Wang & Zhiwu Zhang)"
|
|
10
|
+
__license__ = "GPL-3.0"
|
|
11
|
+
|
|
12
|
+
from .gapit import GAPIT, GAPITOutputFiles, GAPITResult, ModelRunResult
|
|
13
|
+
from .gs.blup import (
|
|
14
|
+
GBLUPResult,
|
|
15
|
+
SUPERSelectionResult,
|
|
16
|
+
cblup,
|
|
17
|
+
gblup,
|
|
18
|
+
predict_new,
|
|
19
|
+
sblup,
|
|
20
|
+
select_super_qtns,
|
|
21
|
+
)
|
|
22
|
+
from .gwas.blink import BLINKResult, blink_gwas
|
|
23
|
+
from .gwas.farmcpu import FarmCPUResult, farmcpu_gwas
|
|
24
|
+
from .gwas.glm import GLMResult, glm_gwas
|
|
25
|
+
from .gwas.mlm import MLMResult, cmlm_gwas, mlm_gwas
|
|
26
|
+
from .gwas.mlmm import MLMMResult, mlmm_gwas
|
|
27
|
+
from .io.formats import (
|
|
28
|
+
AlignedData,
|
|
29
|
+
GenotypeData,
|
|
30
|
+
PhenotypeData,
|
|
31
|
+
align_inputs,
|
|
32
|
+
align_taxa,
|
|
33
|
+
maf_filter,
|
|
34
|
+
read_hapmap,
|
|
35
|
+
read_numeric,
|
|
36
|
+
read_phenotype,
|
|
37
|
+
)
|
|
38
|
+
from .stats.emma import emma_remle, emmax_p3d
|
|
39
|
+
from .stats.kinship import vanraden_kinship, zhang_kinship
|
|
40
|
+
from .stats.pca import build_covariate_matrix, compute_pca
|
|
41
|
+
from .stats.testing import (
|
|
42
|
+
benjamini_hochberg,
|
|
43
|
+
bonferroni_threshold,
|
|
44
|
+
genomic_inflation_factor,
|
|
45
|
+
get_significant_snps,
|
|
46
|
+
)
|
|
47
|
+
from .visualization.plots import (
|
|
48
|
+
gs_scatter,
|
|
49
|
+
kinship_heatmap,
|
|
50
|
+
manhattan_interactive,
|
|
51
|
+
manhattan_plot,
|
|
52
|
+
pca_plot_2d,
|
|
53
|
+
pca_plot_3d_interactive,
|
|
54
|
+
phenotype_distribution,
|
|
55
|
+
qq_plot,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"GAPIT",
|
|
60
|
+
"AlignedData",
|
|
61
|
+
"BLINKResult",
|
|
62
|
+
"FarmCPUResult",
|
|
63
|
+
"GAPITOutputFiles",
|
|
64
|
+
"GAPITResult",
|
|
65
|
+
"GBLUPResult",
|
|
66
|
+
"GLMResult",
|
|
67
|
+
"GenotypeData",
|
|
68
|
+
"MLMMResult",
|
|
69
|
+
"MLMResult",
|
|
70
|
+
"ModelRunResult",
|
|
71
|
+
"PhenotypeData",
|
|
72
|
+
"SUPERSelectionResult",
|
|
73
|
+
"align_inputs",
|
|
74
|
+
"align_taxa",
|
|
75
|
+
"benjamini_hochberg",
|
|
76
|
+
"blink_gwas",
|
|
77
|
+
"bonferroni_threshold",
|
|
78
|
+
"build_covariate_matrix",
|
|
79
|
+
"cblup",
|
|
80
|
+
"cmlm_gwas",
|
|
81
|
+
"compute_pca",
|
|
82
|
+
"emma_remle",
|
|
83
|
+
"emmax_p3d",
|
|
84
|
+
"farmcpu_gwas",
|
|
85
|
+
"gblup",
|
|
86
|
+
"genomic_inflation_factor",
|
|
87
|
+
"get_significant_snps",
|
|
88
|
+
"glm_gwas",
|
|
89
|
+
"gs_scatter",
|
|
90
|
+
"kinship_heatmap",
|
|
91
|
+
"maf_filter",
|
|
92
|
+
"manhattan_interactive",
|
|
93
|
+
"manhattan_plot",
|
|
94
|
+
"mlm_gwas",
|
|
95
|
+
"mlmm_gwas",
|
|
96
|
+
"pca_plot_2d",
|
|
97
|
+
"pca_plot_3d_interactive",
|
|
98
|
+
"phenotype_distribution",
|
|
99
|
+
"predict_new",
|
|
100
|
+
"qq_plot",
|
|
101
|
+
"read_hapmap",
|
|
102
|
+
"read_numeric",
|
|
103
|
+
"read_phenotype",
|
|
104
|
+
"sblup",
|
|
105
|
+
"select_super_qtns",
|
|
106
|
+
"vanraden_kinship",
|
|
107
|
+
"zhang_kinship",
|
|
108
|
+
]
|
pygapit/_typing.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Shared NumPy array types used across PyGAPIT."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typing as t
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
Array: t.TypeAlias = np.ndarray[tuple[int, ...], np.dtype[np.generic]]
|
|
10
|
+
Vector: t.TypeAlias = np.ndarray[tuple[int], np.dtype[np.generic]]
|
|
11
|
+
Matrix: t.TypeAlias = np.ndarray[tuple[int, int], np.dtype[np.generic]]
|
|
12
|
+
|
|
13
|
+
NumericVector: t.TypeAlias = np.ndarray[tuple[int], np.dtype[np.number]]
|
|
14
|
+
|
|
15
|
+
FloatVector: t.TypeAlias = np.ndarray[tuple[int], np.dtype[np.float64]]
|
|
16
|
+
FloatMatrix: t.TypeAlias = np.ndarray[tuple[int, int], np.dtype[np.float64]]
|
|
17
|
+
|
|
18
|
+
IntVector: t.TypeAlias = np.ndarray[tuple[int], np.dtype[np.int_]]
|
|
19
|
+
BoolVector: t.TypeAlias = np.ndarray[tuple[int], np.dtype[np.bool_]]
|
|
20
|
+
StrVector: t.TypeAlias = np.ndarray[tuple[int], np.dtype[np.str_]]
|
|
21
|
+
|
|
22
|
+
LabelVector: t.TypeAlias = StrVector | NumericVector
|
|
23
|
+
ArrayT = t.TypeVar("ArrayT", bound=Array)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def readonly_copy(values: ArrayT) -> ArrayT:
|
|
27
|
+
"""Return an independent NumPy array whose contents cannot be mutated."""
|
|
28
|
+
result = values.copy()
|
|
29
|
+
result.setflags(write=False)
|
|
30
|
+
return result
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def as_float_vector(values: object, *, name: str = "array") -> FloatVector:
|
|
34
|
+
"""Convert values to a one-dimensional float64 array."""
|
|
35
|
+
try:
|
|
36
|
+
result = np.asarray(values, dtype=np.float64)
|
|
37
|
+
except (TypeError, ValueError) as exc:
|
|
38
|
+
raise ValueError(f"{name} must contain numeric values") from exc
|
|
39
|
+
if result.ndim != 1:
|
|
40
|
+
raise ValueError(f"{name} must be one-dimensional; got {result.ndim}D")
|
|
41
|
+
return result
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def as_float_matrix(values: object, *, name: str = "array") -> FloatMatrix:
|
|
45
|
+
"""Convert values to a two-dimensional float64 array."""
|
|
46
|
+
try:
|
|
47
|
+
result = np.asarray(values, dtype=np.float64)
|
|
48
|
+
except (TypeError, ValueError) as exc:
|
|
49
|
+
raise ValueError(f"{name} must contain numeric values") from exc
|
|
50
|
+
if result.ndim != 2:
|
|
51
|
+
raise ValueError(f"{name} must be two-dimensional; got {result.ndim}D")
|
|
52
|
+
return result
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def as_str_vector(values: object, *, name: str = "array") -> StrVector:
|
|
56
|
+
"""Convert values to a one-dimensional Unicode array."""
|
|
57
|
+
result = np.asarray(values, dtype=str)
|
|
58
|
+
if result.ndim != 1:
|
|
59
|
+
raise ValueError(f"{name} must be one-dimensional; got {result.ndim}D")
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def require_length(values: Vector, expected: int, *, name: str) -> None:
|
|
64
|
+
"""Require a vector to have the expected number of elements."""
|
|
65
|
+
if len(values) != expected:
|
|
66
|
+
raise ValueError(f"{name} must have length {expected}; got {len(values)}")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def require_row_count(values: Matrix, expected: int, *, name: str) -> None:
|
|
70
|
+
"""Require a matrix to have the expected number of rows."""
|
|
71
|
+
if values.shape[0] != expected:
|
|
72
|
+
raise ValueError(f"{name} must have {expected} rows; got {values.shape[0]}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def require_square(values: Matrix, *, name: str, size: int | None = None) -> None:
|
|
76
|
+
"""Require a square matrix, optionally with an exact side length."""
|
|
77
|
+
rows, columns = values.shape
|
|
78
|
+
if rows != columns:
|
|
79
|
+
raise ValueError(f"{name} must be square; got shape {values.shape}")
|
|
80
|
+
if size is not None and rows != size:
|
|
81
|
+
raise ValueError(f"{name} must have shape ({size}, {size}); got {values.shape}")
|
pygapit/cli.py
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-line interface for pyGAPIT.
|
|
3
|
+
|
|
4
|
+
Usage
|
|
5
|
+
-----
|
|
6
|
+
pygapit --Y pheno.txt --GD geno.txt --GM map.txt --model BLINK
|
|
7
|
+
pygapit --Y pheno.txt --GD geno.txt --GM map.txt --model MLM FarmCPU BLINK
|
|
8
|
+
pygapit --Y pheno.txt --G hapmap.hmp.txt --model BLINK --PCA_total 5
|
|
9
|
+
pygapit --Y pheno.txt --GD geno.txt --GM map.txt --model gBLUP
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from pygapit.gapit import GAPITResult
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def main() -> None:
|
|
19
|
+
parser = argparse.ArgumentParser(
|
|
20
|
+
prog="pygapit",
|
|
21
|
+
description="pyGAPIT: Genome Association and Prediction Integrated Tool (Python)",
|
|
22
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
23
|
+
epilog="""
|
|
24
|
+
Examples:
|
|
25
|
+
# BLINK GWAS (default, highest power):
|
|
26
|
+
pygapit --Y traits.txt --GD geno.txt --GM map.txt --model BLINK
|
|
27
|
+
|
|
28
|
+
# Multiple models:
|
|
29
|
+
pygapit --Y traits.txt --GD geno.txt --GM map.txt --model GLM MLM BLINK FarmCPU
|
|
30
|
+
|
|
31
|
+
# Genomic prediction:
|
|
32
|
+
pygapit --Y traits.txt --GD geno.txt --GM map.txt --model gBLUP
|
|
33
|
+
|
|
34
|
+
# HapMap format:
|
|
35
|
+
pygapit --Y traits.txt --G genotype.hmp.txt --model BLINK
|
|
36
|
+
|
|
37
|
+
# Custom settings:
|
|
38
|
+
pygapit --Y traits.txt --GD geno.txt --GM map.txt \\
|
|
39
|
+
--model BLINK --PCA_total 5 --maf_threshold 0.01 --output_dir results/
|
|
40
|
+
""",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Input files
|
|
44
|
+
io_group = parser.add_argument_group("Input data")
|
|
45
|
+
io_group.add_argument(
|
|
46
|
+
"--Y", required=True, help="Phenotype file (tab-delimited, col1=Taxa)"
|
|
47
|
+
)
|
|
48
|
+
io_group.add_argument(
|
|
49
|
+
"--GD", help="Numeric genotype file (col1=taxa, col2+=SNPs 0/1/2)"
|
|
50
|
+
)
|
|
51
|
+
io_group.add_argument(
|
|
52
|
+
"--GM", help="SNP map file (3 cols: SNP, Chromosome, Position)"
|
|
53
|
+
)
|
|
54
|
+
io_group.add_argument(
|
|
55
|
+
"--G", help="HapMap genotype file (alternative to --GD + --GM)"
|
|
56
|
+
)
|
|
57
|
+
io_group.add_argument(
|
|
58
|
+
"--KI", help="Kinship matrix file (optional; computed from GD if absent)"
|
|
59
|
+
)
|
|
60
|
+
io_group.add_argument("--CV", help="Covariate file (optional)")
|
|
61
|
+
|
|
62
|
+
# Model selection
|
|
63
|
+
model_group = parser.add_argument_group("Model")
|
|
64
|
+
model_group.add_argument(
|
|
65
|
+
"--model",
|
|
66
|
+
nargs="+",
|
|
67
|
+
default=["BLINK"],
|
|
68
|
+
choices=[
|
|
69
|
+
"GLM",
|
|
70
|
+
"MLM",
|
|
71
|
+
"CMLM",
|
|
72
|
+
"MLMM",
|
|
73
|
+
"FarmCPU",
|
|
74
|
+
"BLINK",
|
|
75
|
+
"gBLUP",
|
|
76
|
+
"cBLUP",
|
|
77
|
+
"sBLUP",
|
|
78
|
+
],
|
|
79
|
+
help="GWAS/GS model(s) to run (default: BLINK)",
|
|
80
|
+
)
|
|
81
|
+
model_group.add_argument(
|
|
82
|
+
"--trait", help="Trait name or column index to analyze (default: all traits)"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# PCA / QC
|
|
86
|
+
qc_group = parser.add_argument_group("Quality control & PCA")
|
|
87
|
+
qc_group.add_argument(
|
|
88
|
+
"--PCA_total",
|
|
89
|
+
type=int,
|
|
90
|
+
default=3,
|
|
91
|
+
help="Number of PCs for population structure control (default: 3)",
|
|
92
|
+
)
|
|
93
|
+
qc_group.add_argument(
|
|
94
|
+
"--maf_threshold",
|
|
95
|
+
type=float,
|
|
96
|
+
default=0.05,
|
|
97
|
+
help="Minimum minor allele frequency (default: 0.05)",
|
|
98
|
+
)
|
|
99
|
+
qc_group.add_argument(
|
|
100
|
+
"--SNP_impute",
|
|
101
|
+
default="middle",
|
|
102
|
+
choices=["middle", "major", "minor", "mean", "none"],
|
|
103
|
+
help="Missing genotype imputation method (default: middle)",
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# GWAS thresholds
|
|
107
|
+
thresh_group = parser.add_argument_group("Significance thresholds")
|
|
108
|
+
thresh_group.add_argument(
|
|
109
|
+
"--cutOff",
|
|
110
|
+
type=float,
|
|
111
|
+
default=None,
|
|
112
|
+
help="P-value threshold (default: Bonferroni 0.05/m)",
|
|
113
|
+
)
|
|
114
|
+
thresh_group.add_argument(
|
|
115
|
+
"--LD",
|
|
116
|
+
type=float,
|
|
117
|
+
default=0.7,
|
|
118
|
+
help="LD threshold for BLINK pruning (default: 0.7)",
|
|
119
|
+
)
|
|
120
|
+
thresh_group.add_argument(
|
|
121
|
+
"--maxLoop",
|
|
122
|
+
type=int,
|
|
123
|
+
default=10,
|
|
124
|
+
help="Max iterations for BLINK/FarmCPU (default: 10)",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# CMLM parameters
|
|
128
|
+
cmlm_group = parser.add_argument_group("CMLM parameters")
|
|
129
|
+
cmlm_group.add_argument(
|
|
130
|
+
"--group_from", type=int, default=1, help="Min groups for CMLM (default: 1)"
|
|
131
|
+
)
|
|
132
|
+
cmlm_group.add_argument(
|
|
133
|
+
"--group_to",
|
|
134
|
+
type=int,
|
|
135
|
+
default=None,
|
|
136
|
+
help="Max groups for CMLM (default: n individuals)",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# FarmCPU parameters
|
|
140
|
+
farm_group = parser.add_argument_group("FarmCPU parameters")
|
|
141
|
+
farm_group.add_argument(
|
|
142
|
+
"--bin_size",
|
|
143
|
+
type=int,
|
|
144
|
+
default=5_000_000,
|
|
145
|
+
help="Bin size in bp for FarmCPU (default: 5000000)",
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
super_group = parser.add_argument_group("SUPER/sBLUP parameters")
|
|
149
|
+
super_group.add_argument(
|
|
150
|
+
"--super_bin_size",
|
|
151
|
+
type=int,
|
|
152
|
+
default=10_000,
|
|
153
|
+
help="Genomic bin size in bp for SUPER selection (default: 10000)",
|
|
154
|
+
)
|
|
155
|
+
super_group.add_argument(
|
|
156
|
+
"--super_qtn_counts",
|
|
157
|
+
type=int,
|
|
158
|
+
nargs="+",
|
|
159
|
+
default=None,
|
|
160
|
+
help="Pseudo-QTN counts evaluated by SUPER (default: 10,20,...,100)",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Simulation
|
|
164
|
+
sim_group = parser.add_argument_group("Phenotype simulation")
|
|
165
|
+
sim_group.add_argument(
|
|
166
|
+
"--h2",
|
|
167
|
+
type=float,
|
|
168
|
+
default=None,
|
|
169
|
+
help="Heritability for phenotype simulation (e.g. 0.7)",
|
|
170
|
+
)
|
|
171
|
+
sim_group.add_argument(
|
|
172
|
+
"--NQTN", type=int, default=None, help="Number of QTNs for simulation (e.g. 20)"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Output
|
|
176
|
+
out_group = parser.add_argument_group("Output")
|
|
177
|
+
out_group.add_argument(
|
|
178
|
+
"--output_dir",
|
|
179
|
+
default=".",
|
|
180
|
+
help="Output directory for results (default: current dir)",
|
|
181
|
+
)
|
|
182
|
+
out_group.add_argument(
|
|
183
|
+
"--no_file_output",
|
|
184
|
+
action="store_true",
|
|
185
|
+
help="Suppress file output (only return object)",
|
|
186
|
+
)
|
|
187
|
+
out_group.add_argument(
|
|
188
|
+
"--buspred", action="store_true", help="Run genomic prediction after GWAS"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
args = parser.parse_args()
|
|
192
|
+
|
|
193
|
+
# Validate inputs
|
|
194
|
+
if args.G is None and (args.GD is None or args.GM is None):
|
|
195
|
+
parser.error(
|
|
196
|
+
"Provide either --G (HapMap) or both --GD and --GM (numeric format)."
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
if args.h2 is not None and args.NQTN is None:
|
|
200
|
+
parser.error("--NQTN is required when --h2 is provided for simulation.")
|
|
201
|
+
|
|
202
|
+
print("=" * 60)
|
|
203
|
+
print(" pyGAPIT — Genome Association & Prediction Tool (Python)")
|
|
204
|
+
print("=" * 60)
|
|
205
|
+
|
|
206
|
+
import warnings
|
|
207
|
+
|
|
208
|
+
warnings.filterwarnings("ignore")
|
|
209
|
+
|
|
210
|
+
import pandas as pd
|
|
211
|
+
|
|
212
|
+
from pygapit import GAPIT
|
|
213
|
+
|
|
214
|
+
# Load data
|
|
215
|
+
print(f"\n[CLI] Loading phenotype: {args.Y}")
|
|
216
|
+
Y = pd.read_csv(args.Y, sep="\t")
|
|
217
|
+
|
|
218
|
+
GD = GM = G = None
|
|
219
|
+
if args.G:
|
|
220
|
+
print(f"[CLI] Loading HapMap: {args.G}")
|
|
221
|
+
G = pd.read_csv(args.G, sep="\t", header=None)
|
|
222
|
+
else:
|
|
223
|
+
print(f"[CLI] Loading genotype: {args.GD}")
|
|
224
|
+
print(f"[CLI] Loading map: {args.GM}")
|
|
225
|
+
GD = pd.read_csv(args.GD, sep="\t")
|
|
226
|
+
GM = pd.read_csv(args.GM, sep="\t")
|
|
227
|
+
|
|
228
|
+
KI = pd.read_csv(args.KI, sep="\t", header=None) if args.KI else None
|
|
229
|
+
CV = pd.read_csv(args.CV, sep="\t") if args.CV else None
|
|
230
|
+
|
|
231
|
+
print(f"[CLI] Model(s): {args.model}")
|
|
232
|
+
print(f"[CLI] Output directory: {Path(args.output_dir).resolve()}")
|
|
233
|
+
|
|
234
|
+
# Run GAPIT
|
|
235
|
+
result = GAPIT(
|
|
236
|
+
Y=Y,
|
|
237
|
+
G=G,
|
|
238
|
+
GD=GD,
|
|
239
|
+
GM=GM,
|
|
240
|
+
KI=KI,
|
|
241
|
+
CV=CV,
|
|
242
|
+
model=args.model,
|
|
243
|
+
trait=args.trait,
|
|
244
|
+
PCA_total=args.PCA_total,
|
|
245
|
+
maf_threshold=args.maf_threshold,
|
|
246
|
+
SNP_impute=args.SNP_impute,
|
|
247
|
+
cutOff=args.cutOff,
|
|
248
|
+
LD=args.LD,
|
|
249
|
+
maxLoop=args.maxLoop,
|
|
250
|
+
group_from=args.group_from,
|
|
251
|
+
group_to=args.group_to,
|
|
252
|
+
bin_size=args.bin_size,
|
|
253
|
+
super_bin_size=args.super_bin_size,
|
|
254
|
+
super_qtn_counts=args.super_qtn_counts,
|
|
255
|
+
h2=args.h2,
|
|
256
|
+
NQTN=args.NQTN,
|
|
257
|
+
file_output=not args.no_file_output,
|
|
258
|
+
output_dir=args.output_dir,
|
|
259
|
+
buspred=args.buspred,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Summary
|
|
263
|
+
print("\n" + "=" * 60)
|
|
264
|
+
print(" Results Summary")
|
|
265
|
+
print("=" * 60)
|
|
266
|
+
|
|
267
|
+
if isinstance(result, dict):
|
|
268
|
+
for key, r in result.items():
|
|
269
|
+
_print_summary(key, r)
|
|
270
|
+
else:
|
|
271
|
+
_print_summary(f"{result.trait} / {result.model}", result)
|
|
272
|
+
|
|
273
|
+
print("\n[CLI] Done.")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _print_summary(label: str, result: GAPITResult) -> None:
|
|
277
|
+
print(f"\n {label}")
|
|
278
|
+
print(f" h^2 = {result.h2:.4f}")
|
|
279
|
+
print(f" lambda (GC) = {result.lambda_gc:.4f}")
|
|
280
|
+
if result.GWAS is not None:
|
|
281
|
+
print(f" SNPs = {len(result.GWAS):,}")
|
|
282
|
+
if result.significant is not None and len(result.significant) > 0:
|
|
283
|
+
print(f" Sig SNPs = {len(result.significant)} (Bonferroni)")
|
|
284
|
+
top = result.significant.nsmallest(3, "P.value")
|
|
285
|
+
for _, row in top.iterrows():
|
|
286
|
+
print(
|
|
287
|
+
f" {row['SNP']} chr{row['Chr']}:{int(float(str(row['Pos']))):,} "
|
|
288
|
+
f"p={row['P.value']:.2e} effect={row['effect']:.4f}"
|
|
289
|
+
)
|
|
290
|
+
else:
|
|
291
|
+
print(" Sig SNPs = 0 (no Bonferroni-significant hits)")
|
|
292
|
+
if result.QTNs is not None and len(result.QTNs) > 0:
|
|
293
|
+
print(f" QTNs selected = {len(result.QTNs)}")
|
|
294
|
+
if result.runtime_seconds > 0:
|
|
295
|
+
print(f" Runtime = {result.runtime_seconds:.1f}s")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
if __name__ == "__main__":
|
|
299
|
+
main()
|