eQTL_Two_MR代码

How it all started

操作步骤

# ———————- 0. 环境初始化与依赖加载 ———————-

# 清空环境变量

rm(list = ls())

options(scipen = 999)  # 禁止科学计数法,提升结果可读性

# 检查并安装必要工具包(pacman 用于批量管理包)

if (!require(“pacman”)) install.packages(“pacman”, update = FALSE, ask = FALSE)

library(pacman)

# 设置 Bioconductor 镜像(加速下载)

options(BioC_mirror = “https://mirrors.ustc.edu.cn/bioc/”)

if (!require(“pacman”)) install.packages(“pacman”)

pacman::p_load(

  biomaRt,        # 获取基因在基因组中的位置信息(染色体、起始/结束坐标)

  dplyr,          # 数据清洗与筛选(如筛选上下游 SNP)

  tidyr           # 数据整理(可选)

)

# 批量加载/安装依赖包(核心包:TwoSampleMR 用于 MR 分析;ieuwasr 用于获取 GWAS 数据)

library(devtools)

library(TwoSampleMR)

library(ieugwasr)

library(biomaRt)

library(dplyr)

library(tidyr)

# ———————- 1. 获取目标基因的基因组位置信息 ———————-

get_gene_position <- function(gene_symbol) {

  ensembl <- useEnsembl(biomart = “genes”, dataset = “hsapiens_gene_ensembl”)

  gene_info <- getBM(

    attributes = c(“hgnc_symbol”, “chromosome_name”, “start_position”, “end_position”),

    filters = “hgnc_symbol”,

    values = gene_symbol,

    mart = ensembl

  )

  if (nrow(gene_info) == 0) stop(“基因位置获取失败”)

  list(chr = as.character(gene_info$chromosome_name[1]),

       start = as.integer(gene_info$start_position[1]),

       end = as.integer(gene_info$end_position[1]))

}

target_genes <- c(“PHB1”, “PHB2”)

all_gene_positions <- lapply(target_genes, get_gene_position)

# ———————- 2. 提取 eQTL 数据并筛选目标区域———————-

window <- 1e100 # 1MB = 1,000,000 bp

eqtl_study_ids <- c(“eqtl-a-ENSG00000167085”, “eqtl-a-ENSG00000215021”)

fetch_eqtl_in_region <- function(eqtl_study_id, target_chr, region_start, region_end) {

  eqtl_data <- extract_instruments(eqtl_study_id)

  if (is.null(eqtl_data) || nrow(eqtl_data) == 0) {

    warning(“eQTL 数据获取失败,返回空数据框”)

    return(data.frame())

  }

  # 使用实际列名 chr.exposure 和 pos.exposure 筛选

  filtered_eqtl <- eqtl_data %>%

    filter(

      chr.exposure == !!target_chr,  # 染色体列名是 chr.exposure

      pos.exposure >= !!region_start, # SNP 位置 ≥ 上游边界(起始-1MB)

      pos.exposure <= !!region_end # SNP 位置 ≤ 下游边界(结束+1MB)

    )

  return(filtered_eqtl)

}

all_exposure_eqtl <- list()

for (i in seq_along(target_genes)) {

  gene_position <- all_gene_positions[[i]]

  region_start <- gene_position$start – window

  region_end <- gene_position$end + window

  exposure_eqtl <- fetch_eqtl_in_region(

    eqtl_study_id = eqtl_study_ids[i],

    target_chr = gene_position$chr,

    region_start = region_start,

    region_end = region_end

  )

  all_exposure_eqtl[[i]] <- exposure_eqtl

  cat(“目标区域内筛选到”, nrow(exposure_eqtl), “个 eQTL SNP\n”)

}

# 合并所有 eQTL 数据

combined_exposure_eqtl <- bind_rows(all_exposure_eqtl)

cat(“合并后筛选到”, nrow(combined_exposure_eqtl), “个 eQTL SNP\n”)

# ———————- 3. 提取结局数据(疾病/表型 GWAS) ———————

# 函数:提取与 eQTL SNP 匹配的结局 GWAS 数据(支持合并后 SNP 列表)

fetch_outcome_for_eqtl <- function(combined_eqtl_snps, outcome_study_id) {

  # 提取结局数据(仅保留与合并后 eQTL SNP 匹配的 SNP)

  outcome_data <- extract_outcome_data(

    snps = combined_eqtl_snps$SNP,  # 使用合并后的 SNP 列表

    outcomes = outcome_study_id     # 结局的 GWAS ID(如 “ukb-d-HEARTFAIL”)

  )

  # 检查匹配的 SNP 数量(改为警告而非停止)

  if (is.null(outcome_data) || nrow(outcome_data) == 0) {

    warning(paste(“警告:结局数据(ID=”, outcome_study_id, “)中未匹配到任何 SNP!”))

    return(data.frame())  # 返回空数据框

  } else if (nrow(outcome_data) < 2) {

    warning(paste(“警告:结局数据(ID=”, outcome_study_id, “)中仅匹配到”,

                  nrow(outcome_data), “个 SNP,可能影响 MR 分析效力!”))

  }

  return(outcome_data)

}

# 示例:获取心力衰竭的 GWAS 数据(替换为你的结局 ID)

outcome_study_id <- “ukb-d-HEARTFAIL”

outcome_gwas <- fetch_outcome_for_eqtl(combined_exposure_eqtl, outcome_study_id)  # 使用合并后的 eQTL 数据

if (nrow(outcome_gwas) > 0) {

  cat(“结局数据中匹配到”, nrow(outcome_gwas), “个 SNP\n”)

}

# ———————- 4. 数据协调(统一 SNP 方向与格式) ———————-

harmonise_data_safe <- function(exposure_dat, outcome_dat) {

  if (nrow(exposure_dat) == 0 || nrow(outcome_dat) == 0) {

    warning(“数据协调失败:暴露或结局数据为空!”)

    return(data.frame())

  }

  harmonised_data <- harmonise_data(

    exposure_dat = exposure_dat,  # 合并后的 eQTL 数据(暴露)

    outcome_dat = outcome_dat     # GWAS 数据(结局)

  )

  # 检查协调后 SNP 数量(改为警告而非停止)

  if (nrow(harmonised_data) < 2) {

    warning(paste(“警告:协调后仅剩余”, nrow(harmonised_data), “个有效 SNP,MR 分析结果可能不可靠!”))

  }

  return(harmonised_data)

}

harmonised_data <- harmonise_data_safe(combined_exposure_eqtl, outcome_gwas)

# ———————- 5. MR 分析(输出所有方法结果) ———————-

run_mr_analysis <- function(harmonised_data) {

  if (nrow(harmonised_data) == 0) {

    warning(“无法进行 MR 分析:协调后无有效 SNP!”)

    return(NULL)

  }

  # 运行默认 MR 方法(IVW、MR-Egger、加权中位数等,取决于 SNP 数量)

  mr_result <- mr(harmonised_data)

  return(list(

    all_methods = mr_result,  # 包含所有运行的 MR 方法结果

    ivw = mr_result[mr_result$method == “Inverse variance weighted”, ]  # 单独提取 IVW 结果(若有)

  ))

}

# 执行 MR 分析

mr_result <- run_mr_analysis(harmonised_data)

# 输出所有方法结果(优化显示格式)

if (!is.null(mr_result)) {

  if (nrow(mr_result$all_methods) > 0) {

    cat(“\n==================== 所有 MR 方法分析结果 ====================\n”)

    # 打印表头

    cat(sprintf(“%-30s | %-15s | %-6s | %-10s | %-10s | %-10s\n”,

                “方法”, “SNP数量”, “效应值”, “标准误”, “P值”, “方法类型”))

    # 逐行打印结果

    for (i in 1:nrow(mr_result$all_methods)) {

      row <- mr_result$all_methods[i, ]

      cat(sprintf(“%-30s | %-15d | %-10.6f | %-10.6f | %-10.6f | %s\n”,

                  row$method, row$nsnp, row$b, row$se, row$pval,

                  ifelse(row$method == “Inverse variance weighted”, “主要方法(IVW)”, “辅助方法”)))

    }

    cat(“=================================================================\n”)

  } else {

    cat(“无有效 MR 方法结果\n”)

  }

  # 若需要,仍可单独打印 IVW 结果(可选)

  if (nrow(mr_result$ivw) > 0) {

    cat(“\n—————— 逆方差加权(IVW)详细结果 ——————\n”)

    print(mr_result$ivw, row.names = FALSE)

  }

}

# ———————- 6. 可视化与结果保存 ———————-

# 森林图:对比不同 MR 方法的效应值

mr_forest_plot(mr_result, title = paste(target_gene, “eQTL-MR 分析结果”))

# 散点图:展示 SNP 的暴露-结局效应关系

mr_scatter_plot(mr_result, dat = harmonised_data, title = paste(target_gene, “eQTL 与结局关联散点图”))

# 保存结果到 CSV

write.csv(mr_result, paste0(target_gene, “_eqtl_mr_results.csv”), row.names = FALSE)

# ———————- 7. 敏感性分析(评估结果稳健性) ———————-

# 水平多效性检验(MR-Egger 截距)

pleiotropy_test <- mr_pleiotropy_test(harmonised_data)

print(“水平多效性检验结果(P < 0.05 提示存在多效性):”)

print(pleiotropy_test)

# 异质性检验(Cochran’s Q)

heterogeneity_test <- mr_heterogeneity_test(harmonised_data)

print(“异质性检验结果(P < 0.05 提示 SNP 间效应不一致):”)

print(heterogeneity_test)

# 留一法分析(排除单个 SNP 后的结果变化)

leaveoneout_result <- mr_leaveoneout(harmonised_data)

mr_leaveoneout_plot(leaveoneout_result, title = paste(target_gene, “eQTL-MR 留一法分析”))。

“勇敢20秒,剩下的交给命运”

煤球