Skip to content

Add OTTERS pipeline, check_ld(), and fix pval_acat()#462

Merged
gaow merged 1 commit intomainfrom
add-otters-pipeline
Apr 7, 2026
Merged

Add OTTERS pipeline, check_ld(), and fix pval_acat()#462
gaow merged 1 commit intomainfrom
add-otters-pipeline

Conversation

@gaow
Copy link
Copy Markdown
Contributor

@gaow gaow commented Apr 7, 2026

Summary

Implement the OTTERS framework (Zhang et al. 2024) for omnibus TWAS, plus LD quality checking and a bug fix in pval_acat().

New functions

  • otters_weights(sumstats, LD, n, methods, p_thresholds, check_ld_method) — Stage I: train eQTL weights via P+T + any *_weights() learner. Defaults match original OTTERS exactly (phi=1e-4, thin=1, lambda from 1e-4, s grid search). Extensible via do.call dispatch.
  • otters_association(weights, gwas_z, LD, combine_method) — Stage II: per-method TWAS z-scores + ACAT/HMP combination. Includes dimension validation.
  • check_ld(R, method, r_tol, shrinkage) — LD matrix QC with three modes: "check" (diagnostic), "shrink" (regularization), "eigenfix" (set negative eigenvalues to 0). Integrated into otters_weights() by default.
  • lassosum_rss_weights() now searches over s = c(0.2, 0.5, 0.9, 1.0) grid (was fixed s=0.9).
  • lassosum_rss() lambda range extended to 1e-4 (was 1e-3), matching OTTERS.

Bug fix

pval_acat() had a sign error: qcauchy(p) = tan(π(p-0.5)) but ACAT needs T = mean(tan(π(0.5-p))). Combined p-values were near 1.0 instead of near 0.

Audit results

All 17 OTTERS default parameters verified matching. Systematic comparison of every formula, default, and edge case against the original Python/R pipeline. Key fixes: PRS-CS phi, SDPR thin, lassosum lambda/s, cor>=1 safeguard, P+T weight clamping.

Validation

         method   twas_z    twas_pval n_snps
1      PT_0.001 4.921729 8.578e-07      3
2       PT_0.05 4.770889 1.834e-06      5
3  lassosum_rss 4.440683 8.967e-06     46
4        prs_cs 2.775733 5.508e-03     50
5          sdpr 2.808055 4.984e-03     50
6 ACAT_combined       NA 2.743e-06     NA

Test plan

  • Unit tests for otters_weights(), otters_association(), check_ld()
  • End-to-end integration test with simulated data
  • Vignette with real data loading pattern + simulation demo
  • devtools::test(filter="otters") and devtools::test(filter="ld")
  • R CMD check

🤖 Generated with Claude Code

Add check_ld(R, method) with three modes:
- "check": diagnostic only (eigenvalues, PD status, condition number)
- "shrink": (1-s)*R + s*I regularization
- "eigenfix": set negative eigenvalues to 0, reconstruct (susieR approach)

Integrate into otters_weights() via check_ld_method parameter (default
"eigenfix"), matching OTTERS which forces PD via SVD before PRS-CS.
The check runs once at the top and the repaired LD is used for all methods.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@gaow gaow merged commit 50660d7 into main Apr 7, 2026
4 of 5 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.

1 participant