fetch_sources.sh downloads PharmVar per-gene zips; pgx db build adds core alleles for genes CPIC does not define (normalised against the FASTA, *1 = reference). Masks re-derived with the new variants. GeT-RM: CYP3A4 15/15, CYP2A6 7/9; all comparable genes 154/168 (CYP1A2/CYP2C8 definitions differ from GeT-RM's).
96 lines
6.0 KiB
Markdown
96 lines
6.0 KiB
Markdown
# V2 — PGx DB and star-allele calling (PGX-001 … PGX-021)
|
||
|
||
Status: **implemented for CPIC star-allele genes, interim validation** (2026-09-29).
|
||
|
||
* `scripts/pgx/fetch_sources.sh` → CPIC API tables (CC0, data version 2026-08-03).
|
||
* `simurg pgx db build` → deterministic SQLite: 22 genes, 1354 alleles, 1897 normalised variant rows; GSA v4 PGx
|
||
probe map 5002 links; two builds byte-identical.
|
||
* `simurg pgx star-allele call` → `diplotype_summary.tsv`, `pgx_calls.json` (11 star-allele genes).
|
||
* `simurg pgx phenotype` (alias `annotate`) → per-sample `<sample>.pgx.json` (`simurg.pgx/1`) and
|
||
`diplotype_annotations.tsv` with `#` metadata lines (PGX-024 … PGX-028); CPIC activity scores (xN = N × activity,
|
||
*5/*0 = 0) and function-count lookups; disagreeing feasible solutions → `Indeterminate` plus per-solution
|
||
phenotypes. `tests/unit/test_pgx_phenotype.py`.
|
||
* Unit tests: `tests/unit/test_pgx_db.py`, `tests/unit/test_pgx_star.py`.
|
||
|
||
## Data and truth
|
||
|
||
* Arrays: the GSA v4 PGx demo (36 arrays = 30 CEPH/1000G individuals, `scripts/pgx/demo36_identity.tsv`), SNV VCFs
|
||
and PGx CN VCFs from SimurgArray.
|
||
* Star-allele truth: GeT-RM consolidated PGx/HLA table (CDC, 2025 V4) — 16 of the 30 individuals
|
||
(`scripts/pgx/getrm_truth.py`).
|
||
* Probe mask (PGX-010): array vs 1000G 30x (NYGC phased panel, fetched per gene over HTTPS) for the **other 14
|
||
individuals** (`scripts/pgx/derive_masks.py`), so the mask is independent of the GeT-RM validation set: 8 of 658
|
||
assessed records (23 probe names) below 95 % concordance — e.g. CYP2C19 rs367543002 and CYP2D6 rs1135836 called
|
||
homozygous ALT in every sample, the UGT1A1 `C>CATAT` TA-repeat probe. Regenerate with the command in the script
|
||
header (the mask lists vendor probe names and is not committed).
|
||
|
||
## Result (16 individuals; replicate arrays counted once)
|
||
|
||
| Gene | Top solution = GeT-RM | Truth among array-equivalent feasible solutions |
|
||
|---|---|---|
|
||
| CYP2C9 | **15/15** | 15/15 |
|
||
| CYP2C19 | **14/15** | 15/15 |
|
||
| CYP2D6 | **13/15** | **15/15** |
|
||
| CYP3A5 | 15/15 | 15/15 |
|
||
| TPMT | 15/16 | 15/16 |
|
||
| CYP2B6 | 12/14 | 12/14 |
|
||
| CYP4F2 | 8/9 | 8/9 |
|
||
| NAT2 | 8/9 | 8/9 |
|
||
| NUDT15 | 1/1 | 1/1 |
|
||
| VKORC1 (rs9923231, c.-1639G>A) | 13/15 | 13/15 |
|
||
| GSTM1 (CN: DEL/DEL vs present) | 9/9 | 9/9 |
|
||
| GSTT1 (CN 0/1/2) | 7/9 | 7/9 |
|
||
| UGT2B17 (CN) | 2/2 | 2/2 |
|
||
| CYP3A4 (PharmVar) | **15/15** | 15/15 |
|
||
| CYP2A6 (PharmVar; *4 deletion from CN) | 7/9 | 8/9 |
|
||
| **total** | **154/168 (91.7 %)** | **158/168 (94.0 %)** |
|
||
|
||
PGX-032 target (≥ 90 % for CYP2D6/CYP2C19/CYP2C9 against truth): **42/45 = 93 %** (top solution), 45/45 within
|
||
the array-equivalent feasible solutions.
|
||
|
||
CYP2D6 structures (PGX-013) come from the three sub-region CNs (`simurg.pgx.star.cyp2d6_structure`): NA12878
|
||
`*3/*68+*4` and NA11832 `*1/*68+*4` are called exactly as GeT-RM reports them; one sample is called `*36+*10/*4`
|
||
(p5 = intron 2 = 3, exon 9 = 2; no GeT-RM truth). The two remaining top-solution misses are *1 vs *2, which this
|
||
array cannot separate (both within the feasible set).
|
||
Details and every discordant call: `V2_pgx_star_getrm16.json`.
|
||
|
||
Not in the total (GeT-RM uses older names or definitions than CPIC/PharmVar today): UGT1A1 (GeT-RM `*60`;
|
||
0/9 top), SLCO1B1 (`*1B/*17/*21` vs CPIC `*37/*15/*20`; 9/15), CYP1A2 (lettered `*1F`; 0/9) and CYP2C8 (PharmVar now
|
||
defines rs2071426 alone as `*19`; the array calls it concordantly with 1000G, GeT-RM reports those carriers as
|
||
`*1`; 8/15).
|
||
|
||
Remaining discordances: CYP2D6 *2 vs *1 where the array cannot separate them (prior picks *1), CYP2C19 *8, TPMT *21, CYP2B6 *27, CYP4F2 *2 — rare
|
||
alleles whose defining variants are absent or masked on this array.
|
||
|
||
## Error model (K-029)
|
||
|
||
After masking, 0 of 9 740 array calls at DB variants disagreed with 1000G 30x in any GenCall-score bin (0.15–1.0),
|
||
so the per-call error is 1e-3 (GS ≥ 0.15) and errors are handled by the probe mask. The first model,
|
||
ε = 0.5·(1 − GS)², let correct low-GS calls lose to the prior (CYP2C9 7/15 → 15/15 after the change).
|
||
|
||
## Caveats
|
||
|
||
* 16 individuals, one array type, European (CEPH) only; masks from 14 individuals (spec: ≥ 100 samples).
|
||
* CPIC now defines the GRCh38 reference as its own allele where it differs from *1 (CYP2C19 *38): a sample
|
||
heterozygous at the *1-defining rs3758581 fits *1/*38 best (raw score 0.98), but *38 has no CPIC frequency, so the
|
||
prior keeps *1/*1 on top — which is what legacy GeT-RM reports.
|
||
* `simurg pgx coverage` (PGX-009/035) on GSA v4 PGx with the mask: 438 alleles callable, 68 array-equivalent to
|
||
another allele, 88 not covered, 4 masked. Against the reference's known limitations: CYP4F2 *2 not covered (reported
|
||
as *1) and *4 ≡ *3 — as the reference; UGT1A1 *28 callable (its C>CAT probe matches 1000G) and *37 masked (the
|
||
C>CATAT probe fails); CYP2D6 *40/*58 and NUDT15 *6/*9 are separable here (CPIC core definitions differ from the
|
||
reference's).
|
||
* Detail outputs (PGX-016 … PGX-020, PGX-027/028, PGX-031): `supporting_variants.tsv`, `unresolved_variants.tsv`,
|
||
`variants_not_covered.tsv`, per-allele supporting/unresolved sites for **both** alleles of every candidate,
|
||
`variant_change` vs the top solution (`distant_alternative` above 3 changes), `unexplained_variants`, full HGVS on
|
||
GRCh38 RefSeq accessions.
|
||
* All CPIC gene kinds are called: star alleles, G6PD (named haplotypes; males hemizygous from the VCF ploidy),
|
||
variant-based genes (ABCG2, CACNA1S, CFTR, DPYD, IFNL3, MT-RNR1, RYR1, VKORC1; genotype as `rs… REF/ALT` or
|
||
`Reference`, phenotype diplotype from the two lowest-activity carried variants), HLA-A/B tag variants (PGX-007,
|
||
positions resolved from the manifest's probes, flag `tag_caveat`), and GSTM1/GSTT1/UGT2B17 `*0` genotypes from
|
||
the targeted CN (PGX-006).
|
||
* PharmVar genes without CPIC definitions (CYP1A2, CYP2A6, CYP2A13, CYP2C8, CYP3A4; PharmVar 6.2.29): core alleles
|
||
from the GRCh38 haplotype VCFs, `*1` = reference, prior reference 0.8 (no population frequencies); genotype only
|
||
(no CPIC phenotype). The probe mask was re-derived with these variants (one more record: CYP2A6 19:40843845 C>A,
|
||
concordance 0.47, which had produced false *5 calls).
|
||
* Not yet: DPWG tables.
|