Skip to content

Drop panel-unsupported variants at QC LD lookups instead of aborting - #594

Merged
danielnachun merged 1 commit into
StatFunGen:mainfrom
Yining97:qc-drop-panel-orphans
Sep 8, 2026
Merged

danielnachun merged 1 commit into
StatFunGen:mainfrom
Yining97:qc-drop-panel-orphans

Conversation

@Yining97

@Yining97 Yining97 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

What

summaryStatsQc aborts when a harmonized variant has no entry in the pruned LD panel:

summaryStatsQc: kriging prefilter: N variant id(s) not present in the LD sketch panel

The kriging prefilter (.qcKrigingFlip) and z-mismatch QC (.applyLdMismatchQcToEntry) now call .ldFromSketch(onMissing = "drop") and align the entry to the panel-supported subset, dropping the unsupported variants (recorded in the qcInfo audit) instead of aborting. Fine-mapping's .ldFromSketch(onMissing = "error") is unchanged.

Why

The panel MAF/MAC/missingness filter prunes the LD panel once, before harmonization, and harmonization then drops any GWAS variant the pruned panel no longer covers -- so the panel-filter path is normally orphan-safe (a --maf sweep to 0.45, dropping 9349 of 9633 variants, orphans nothing). Co-located indels are the exception: the R5 panel puts an insertion and a deletion on the same POS column with tag ids (e.g. chr21:13988152:INS:T and chr21:13988153:DEL:T, both at POS 13988152). Harmonization joins by (chrom, pos) so the deletion survives the prune, but the QC lookups match by (chrom, pos, allele) tuple and can't resolve it once its partner row is pruned.

.panelVariantFilter already documents that a panel-absent variant is passed through and the drop-or-error decision belongs to .ldFromSketch's onMissing. #585 dropped these during harmonization; #590 replaced that with .repairVariantIds (which keeps them), re-exposing the abort. Dropped variants have no LD in the pruned panel and can't be fine-mapped regardless, so dropping is the correct outcome.

Tests

  • New unit tests drive .qcKrigingFlip and .applyLdMismatchQcToEntry with the orphan condition: they drop the variant and record the count instead of aborting; a no-orphan entry is unchanged; fine-mapping still errors on a genuinely absent variant.
  • Real chr21 ADSP R5 construct, baseline vs fixed on a non-orphan region: identical (same 7939 variants, max |ΔZ| = 0, imputed count unchanged) -- the change is a no-op when nothing is orphaned.

summaryStatsQc aborts when a harmonized GWAS variant has no entry in the
pruned LD panel (kriging prefilter: N variant id(s) not present in the LD
sketch panel). The panel filter prunes once before harmonization, and
harmonization drops variants the pruned panel can't cover -- but co-located
indels survive that: the R5 panel puts an insertion and a deletion on the
same POS column with tag ids, harmonization joins by (chrom, pos) so the
deletion survives, then the kriging and z-mismatch lookups match by
(chrom, pos, allele) tuple and can't resolve it once its partner row is
pruned.

.panelVariantFilter already documents that the drop-or-error call for a
panel-absent variant belongs to .ldFromSketch's onMissing; the QC lookups
were the only callers still using the default "error". Make .qcKrigingFlip
and .applyLdMismatchQcToEntry pass onMissing="drop", align the entry to the
panel-supported subset, and record the dropped count in the qcInfo audit.
Leave fine-mapping's onMissing="error" as the post-QC invariant.
@danielnachun
danielnachun merged commit e6f3820 into StatFunGen:main Sep 8, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants