| name | bio-causal-genomics-pleiotropy-detection |
| description | Detect and correct for horizontal pleiotropy in Mendelian randomization analyses using MR-PRESSO for outlier removal, MR-Egger regression for directional pleiotropy, and Steiger filtering for variant directionality. Use when validating MR results, detecting pleiotropic instruments, or running sensitivity analyses for causal inference. |
| tool_type | r |
| primary_tool | MR-PRESSO |
Version Compatibility
Reference examples tested with: MR-PRESSO 1.0+, TwoSampleMR 0.5+
Before using code patterns, verify installed versions match. If versions differ:
- R:
packageVersion("<pkg>") then ?function_name to verify parameters
If code throws ImportError, AttributeError, or TypeError, introspect the installed
package and adapt the example to match the actual API rather than retrying.
Pleiotropy Detection
"Check my MR results for pleiotropic bias" → Detect and correct for horizontal pleiotropy using outlier removal (MR-PRESSO), directional pleiotropy testing (MR-Egger intercept), and variant directionality filtering (Steiger) to validate causal inference results.
- R:
MRPRESSO::mr_presso() for global and distortion tests
- R:
TwoSampleMR::mr_egger_regression() for Egger intercept test
Overview
Horizontal pleiotropy violates the exclusion restriction assumption of MR: instruments
affect the outcome through pathways other than the exposure. Detecting and correcting
for pleiotropy is essential for valid causal inference.
Types of pleiotropy:
- Vertical (mediated): Instrument -> exposure -> outcome (valid, not a problem)
- Horizontal (direct): Instrument -> outcome bypassing exposure (violates MR assumptions)
- Balanced: Pleiotropic effects cancel out (IVW still valid, Egger intercept ~0)
- Directional: Pleiotropic effects are systematic (biases IVW, Egger detects this)
MR-PRESSO
Goal: Detect and remove pleiotropic outlier instruments from an MR analysis.
Approach: Run MR-PRESSO to test for global pleiotropy, identify individual outlier SNPs, test whether their removal changes the causal estimate (distortion test), and obtain a corrected estimate.
library(MRPRESSO)
presso_input <- data.frame(
bx = dat$beta.exposure,
by = dat$beta.outcome,
bxse = dat$se.exposure,
byse = dat$se.outcome
)
presso_result <- mr_presso(
BetaOutcome = 'by', BetaExposure = 'bx',
SdOutcome = 'byse', SdExposure = 'bxse',
OUTLIERtest = TRUE, DISTORTIONtest = TRUE,
data = presso_input,
NbDistribution = 5000,
SignifThreshold = 0.05
)
global_p <- presso_result$`MR-PRESSO results`$`Global Test`$Pvalue
cat('Global test p-value:', global_p, '\n')
outliers <- presso_result$`MR-PRESSO results`$`Outlier Test`
cat('\nOutlier test results:\n')
print(outliers)
outlier_indices <- which(outliers$Pvalue < 0.05)
cat('Outlier SNPs:', length(outlier_indices), '\n')
distortion_p <- presso_result$`MR-PRESSO results`$`Distortion Test`$Pvalue
cat('Distortion test p-value:', distortion_p, '\n')
main_results <- presso_result$`Main MR results`
cat('\nRaw IVW estimate:', main_results$`Causal Estimate`[1], '\n')
cat('Corrected IVW estimate:', main_results$`Causal Estimate`[2], '\n')
MR-Egger Diagnostics
Goal: Test for directional pleiotropy and obtain a pleiotropy-adjusted causal estimate.
Approach: Fit MR-Egger regression where the intercept estimates average pleiotropic bias, and check I-squared for instrument strength under the NOME assumption.
library(TwoSampleMR)
egger <- mr_egger_regression(dat$beta.exposure, dat$beta.outcome,
dat$se.exposure, dat$se.outcome)
cat('Egger intercept:', round(egger$b_i, 5), '\n')
cat('Intercept SE:', round(egger$se_i, 5), '\n')
cat('Intercept p-value:', format.pval(egger$pval_i), '\n')
cat('\nEgger causal estimate:', round(egger$b, 4), '\n')
cat('Egger SE:', round(egger$se, 4), '\n')
cat('Egger p-value:', format.pval(egger$pval), '\n')
isq <- Isq(dat$beta.exposure, dat$se.exposure)
cat('\nI-squared:', round(isq, 3), '\n')
if (isq < 0.9) cat('Warning: I-squared < 0.9; Egger estimate may be unreliable (NOME violation)\n')
Steiger Filtering
Goal: Verify that instruments act in the correct causal direction (exposure -> outcome, not reverse).
Approach: Apply the Steiger test to each instrument, remove those explaining more outcome variance than exposure variance, and re-run MR on filtered instruments.
library(TwoSampleMR)
steiger <- steiger_filtering(dat)
dat_steiger <- steiger[steiger$steiger_dir == TRUE, ]
cat('Instruments passing Steiger filter:', nrow(dat_steiger), 'of', nrow(steiger), '\n')
results_steiger <- mr(dat_steiger)
print(results_steiger[, c('method', 'nsnp', 'b', 'se', 'pval')])
direction <- directionality_test(dat)
cat('\nCorrect causal direction:', direction$correct_causal_direction, '\n')
cat('Steiger p-value:', format.pval(direction$steiger_pval), '\n')
Additional Sensitivity Methods
library(TwoSampleMR)
mr_conmix <- mr(dat, method_list = 'mr_raps')
library(MendelianRandomization)
mr_input <- mr_input(
bx = dat$beta.exposure, bxse = dat$se.exposure,
by = dat$beta.outcome, byse = dat$se.outcome
)
raps_result <- mr_raps(mr_input)
cat('MR-RAPS estimate:', raps_result$Estimate, '\n')
cat('MR-RAPS p-value:', raps_result$Pvalue, '\n')
exposure1 <- extract_instruments('ieu-a-300')
exposure2 <- extract_instruments('ieu-a-302')
Comprehensive Sensitivity Framework
Goal: Run a complete battery of MR sensitivity analyses to validate causal findings.
Approach: Apply IVW, MR-Egger, weighted median, weighted mode, heterogeneity, Egger intercept, leave-one-out, and MR-PRESSO in a single function and summarize results.
library(TwoSampleMR)
library(MRPRESSO)
run_sensitivity <- function(dat) {
results <- list()
results$ivw <- mr(dat, method_list = 'mr_ivw')
results$egger <- mr(dat, method_list = 'mr_egger_regression')
results$median <- mr(dat, method_list = 'mr_weighted_median')
results$mode <- mr(dat, method_list = 'mr_weighted_mode')
results$het <- mr_heterogeneity(dat)
results$pleio <- mr_pleiotropy_test(dat)
results$loo <- mr_leaveoneout(dat)
presso_input <- data.frame(
bx = dat$beta.exposure, by = dat$beta.outcome,
bxse = dat$se.exposure, byse = dat$se.outcome
)
results$presso <- mr_presso(
BetaOutcome = 'by', BetaExposure = 'bx',
SdOutcome = 'byse', SdExposure = 'bxse',
OUTLIERtest = TRUE, DISTORTIONtest = TRUE,
data = presso_input, NbDistribution = 5000, SignifThreshold = 0.05
)
results
}
summarize_sensitivity <- function(sens) {
cat('=== MR Sensitivity Analysis Summary ===\n\n')
all_mr <- rbind(sens$ivw, sens$egger, sens$median, sens$mode)
cat('Method comparison:\n')
print(all_mr[, c('method', 'b', 'se', 'pval')])
cat('\nHeterogeneity (Cochran Q):\n')
cat(' Q p-value (IVW):', sens$het$Q_pval[sens$het$method == 'Inverse variance weighted'], '\n')
cat('\nEgger intercept:\n')
cat(' Intercept:', sens$pleio$egger_intercept, '\n')
cat(' P-value:', sens$pleio$pval, '\n')
cat('\nMR-PRESSO global test p-value:',
sens$presso$`MR-PRESSO results`$`Global Test`$Pvalue, '\n')
cat('\n--- Interpretation ---\n')
cat('Consistent estimates across methods: Evidence strengthened\n')
cat('Significant Egger intercept: Directional pleiotropy present\n')
cat('Significant MR-PRESSO global: Horizontal pleiotropy detected\n')
cat('Significant heterogeneity: Instruments may be invalid\n')
}
STROBE-MR Reporting
When reporting MR analyses, follow STROBE-MR guidelines:
- Report all MR methods tested (not just the most significant)
- Report heterogeneity Q-statistic and p-value
- Report Egger intercept with p-value
- Report MR-PRESSO global test and number of outliers removed
- Report F-statistics for instrument strength
- Report Steiger directionality test
- State whether results are consistent across sensitivity analyses
- Acknowledge limitations of the MR assumptions
Related Skills
- mendelian-randomization - Primary MR analysis that pleiotropy tests validate
- fine-mapping - Identify causal variants at instrument loci
- population-genetics/association-testing - GWAS data for MR instruments