甲基化芯片分析(450K/EPIC芯片数据分析)¶
一句话概述¶
甲基化芯片分析是利用Illumina 450K或EPIC芯片平台,通过minfi/ChAMP等R包对全基因组DNA甲基化水平进行定量检测,识别差异甲基化位点(DMP)和差异甲基化区域(DMR),从而揭示表观遗传调控机制的生物信息学分析流程。
核心知识点表格¶
| 知识点 | 说明 |
|---|---|
| 450K芯片 | Illumina HumanMethylation450 BeadChip,覆盖约48万个CpG位点 |
| EPIC芯片 | Illumina MethylationEPIC BeadChip,v1覆盖约86.6万、v2覆盖约93.5万个CpG位点,增强了enhancer区域覆盖 |
| Beta值 | 甲基化水平的度量,范围0~1,表示甲基化比例 |
| M值 | Beta值的logit变换,M = log2(Beta/(1-Beta)),统计分析时更优 |
| DMP | Differentially Methylated Position,差异甲基化位点 |
| DMR | Differentially Methylated Region,差异甲基化区域 |
| minfi | Bioconductor核心甲基化分析R包,功能全面 |
| ChAMP | 整合流程的甲基化分析R包,适合初学者 |
| SeSAMe | SEnsible Step-wise Analysis of DNA MEthylation,支持EPIC v2,推荐用于预处理 |
| Probe类型 | Infinium I型(2个探针)和II型(1个探针),需做类型归一化 |
| BMIQ | Beta-Mixture Quantile归一化,校正I/II型探针偏差 |
| SVD | Singular Value Decomposition,用于检测批次效应来源 |
| 功能注释 | 将DMP/DMR映射到基因、CpG岛、调控区域等功能元件 |
各步骤详解¶
第一步:实验原理与芯片平台理解¶
白话解释: DNA甲基化就像给基因加了一个"开关锁"——在DNA的CpG二核苷酸上加一个甲基基团(-CH3)。这个修饰通常会让基因"沉默",不让它表达。甲基化芯片就是一种高通量工具,能一次性检测几十万个CpG位点的甲基化状态。
技术细节: Illumina甲基化芯片基于亚硫酸氢盐转化(bisulfite conversion)原理: - 未甲基化的胞嘧啶(C)被转化为尿嘧啶(U),测序时读为T - 甲基化的胞嘧啶(mC)不受影响,仍读为C - 通过比较甲基化探针和非甲基化探针的荧光信号强度,计算Beta值
450K vs EPIC对比:
| 特性 | 450K | EPIC v1 | EPIC v2 |
|---|---|---|---|
| CpG位点数 | ~485,000 | ~866,836 | ~935,000(937,690个探针) |
| Enhancer覆盖 | 有限 | 大幅增强 | 进一步增强 |
| 样本通量 | 12样本/芯片 | 8样本/芯片 | 8样本/芯片 |
| 探针类型 | I + II | I + II | I + II |
Beta值与M值的关系:
Beta = M_methylated / (M_methylated + M_unmethylated + alpha)
# alpha通常取100,防止分母为0
M_value = log2(Beta / (1 - Beta))
Beta值直观(0=完全未甲基化,1=完全甲基化),但在极端值处方差不均匀(heteroscedastic),不适合直接做统计检验。M值经过logit变换后方差更均匀,适合线性模型分析,但生物学解读不如Beta值直观。
第二步:数据获取与读取¶
白话解释: 芯片实验产生的原始数据是IDAT格式文件(每个样本两个文件:红色通道和绿色通道)。我们需要用专门的R包把这些文件读入R中进行分析。
技术细节: IDAT文件是Illumina扫描仪生成的二进制文件,包含每个珠子(bead)的荧光强度信息。minfi和ChAMP都支持直接读取IDAT文件。
代码实现(minfi):
# 安装必要的R包
if (!requireNamespace("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install(c("minfi", "IlluminaHumanMethylation450kanno.ilmn12.hg19",
"IlluminaHumanMethylation450kmanifest",
"IlluminaHumanMethylationEPICanno.ilm10b4.hg19",
"IlluminaHumanMethylationEPICmanifest"))
library(minfi)
# 准备样本信息表(Sample Sheet)
# CSV文件至少包含:Sample_Name, Sentrix_ID, Sentrix_Position, 分组信息
targets <- read.metharray.sheet(baseDir = "path/to/idat/", pattern = "SampleSheet.csv")
# 读取IDAT文件为RGChannelSet对象
rgSet <- read.metharray.exp(targets = targets)
# 查看基本信息
rgSet
pData(rgSet) # 样本表型信息
代码实现(ChAMP):
BiocManager::install("ChAMP")
library(ChAMP)
# ChAMP一步读取+质控
myLoad <- champ.load(
directory = "path/to/idat/",
method = "ChAMP", # 或 "minfi"
methValue = "B", # Beta值
autoimpute = TRUE, # 自动填补缺失值
filterDetP = TRUE, # 过滤检测P值不显著的探针
ProbeCutoff = 0, # 样本中失败探针的比例阈值
SampleCutoff = 0.1, # 探针在样本中失败的比例阈值
detPcut = 0.01, # 检测P值阈值
filterBeads = TRUE, # 过滤bead数不足的探针
beadCutoff = 0.05, # bead数阈值
filterNoCG = TRUE, # 过滤非CpG探针
filterSNPs = TRUE, # 过滤SNP相关探针
filterMultiHit = TRUE, # 过滤多重映射探针
filterXY = TRUE, # 过滤性染色体探针
arraytype = "450K" # 或 "EPIC"
)
第三步:质量控制(QC)¶
白话解释: 就像体检时先要确认检测仪器准不准、样本质量好不好一样,甲基化芯片分析也需要先做质量控制——检查是否有样本实验失败、是否有样本搞混了、是否有技术偏差需要校正。
技术细节: 质控主要包括以下几个方面: 1. 检测P值:每个探针都有一个P值表示信号是否可靠,P > 0.01的探针应被过滤 2. 样本间相关性:通过密度图、MDS图等检查离群样本 3. 性别检查:利用性染色体甲基化模式验证样本标注是否正确 4. SNP探针检查:利用包含已知SNP的探针进行样本身份验证 5. 探针过滤:去除交叉反应探针、含SNP的探针、性染色体探针等
# ===== minfi QC =====
library(minfi)
# 1. 检测P值
detP <- detectionP(rgSet)
# 统计每个样本失败探针比例
failed_fraction <- colMeans(detP > 0.01)
print(failed_fraction)
# 去除质量差的样本(>5%探针失败)
keep_samples <- failed_fraction < 0.05
rgSet <- rgSet[, keep_samples]
# 2. QC报告(生成PDF)
qcReport(rgSet, sampNames = pData(rgSet)$Sample_Name,
sampGroups = pData(rgSet)$Group,
pdf = "QC_report.pdf")
# 3. 密度图
densityPlot(rgSet, sampGroups = pData(rgSet)$Group, main = "Beta Value Density")
# 4. MDS图(多维缩放)检查批次效应和分组
mdsPlot(rgSet, sampGroups = pData(rgSet)$Group, numPositions = 1000)
# 5. 性别预测
predictedSex <- getSex(mapToGenome(rgSet))
plotSex(predictedSex)
# 6. SNP探针用于样本身份验证
snpBetas <- getSnpBeta(rgSet)
# 可用于检查样本是否搞混
# ===== ChAMP QC =====
# ChAMP在champ.load()时已完成大部分过滤
# 额外QC
champ.QC(
beta = myLoad$beta,
pheno = myLoad$pd$Sample_Group,
mdsPlot = TRUE,
densityPlot = TRUE,
dendrogram = TRUE
)
第四步:数据预处理与归一化¶
白话解释: 芯片上有两种不同设计的探针(I型和II型),它们测出来的值有系统性偏差。归一化就是要消除这些技术偏差,让不同探针、不同样本之间的数据可以公平比较。
技术细节: 常用归一化方法包括: - Quantile normalization:分位数归一化,假设所有样本的Beta值分布相同 - BMIQ (Beta-Mixture Quantile):专门校正I/II型探针偏差 - Functional normalization (Funnorm):利用控制探针信息进行归一化,推荐用于有已知生物学差异的样本 - SWAN:Subset-quantile Within Array Normalization - Noob:Normal-exponential out-of-band归一化
# ===== minfi 预处理 =====
# 方法1:Funnorm(推荐,适合有大的生物学差异时)
grSet_funnorm <- preprocessFunnorm(rgSet)
# 方法2:Quantile归一化
grSet_quantile <- preprocessQuantile(rgSet)
# 方法3:Noob + BMIQ组合
mSet_noob <- preprocessNoob(rgSet)
# 然后可以额外做BMIQ
# 提取Beta值和M值
beta_values <- getBeta(grSet_funnorm)
m_values <- getM(grSet_funnorm)
# 过滤检测P值不合格的探针
detP_filtered <- detP[rownames(beta_values), colnames(beta_values)]
beta_values[detP_filtered > 0.01] <- NA
# 去除含NA过多的探针
keep_probes <- rowMeans(is.na(beta_values)) < 0.1
beta_values <- beta_values[keep_probes, ]
m_values <- m_values[keep_probes, ]
# ===== ChAMP归一化 =====
myNorm <- champ.norm(
beta = myLoad$beta,
rgSet = myLoad$rgSet,
mset = myLoad$mset,
resultsDir = "./CHAMP_Normalization/",
method = "BMIQ", # 或 "PBC", "SWAN", "FunctionalNormalization"
plotBMIQ = TRUE,
arraytype = "450K",
cores = 4
)
第五步:批次效应校正¶
白话解释: 不同时间做的实验、不同批次的芯片之间会有技术性差异(批次效应),这些差异会干扰真正的生物学信号。我们需要用统计方法把这些技术差异去掉。
技术细节: 批次效应检测和校正的常用方法: - SVD(Singular Value Decomposition):检测哪些技术因素(芯片批次、扫描日期等)影响数据变异 - ComBat:经验贝叶斯方法,校正已知批次效应 - SVA(Surrogate Variable Analysis):发现和校正未知的混杂因素
# ===== ChAMP SVD分析 =====
champ.SVD(
beta = myNorm,
pd = myLoad$pd,
resultsDir = "./CHAMP_SVD/"
)
# SVD热图会显示哪些技术因素与主成分显著相关
# ===== ComBat校正 =====
# 使用ChAMP
myCombat <- champ.runCombat(
beta = myNorm,
pd = myLoad$pd,
batchname = c("Slide"), # 批次变量名
variablename = "Sample_Group" # 保护的生物学变量
)
# 使用sva包
library(sva)
# 需要M值做ComBat
m_values_combat <- ComBat(
dat = m_values,
batch = pData(grSet_funnorm)$Slide,
mod = model.matrix(~ Group, data = pData(grSet_funnorm))
)
第六步:差异甲基化分析(DMP)¶
白话解释: DMP分析就是逐个CpG位点比较两组样本的甲基化水平,找出哪些位点的甲基化程度有显著差异。这些差异位点可能参与疾病发生、发展等生物学过程。
技术细节: DMP分析通常使用线性回归模型(limma框架),以M值作为因变量。筛选标准通常结合统计显著性(adjusted P < 0.05)和效应大小(|ΔBeta| > 0.1或0.2)。
# ===== minfi + limma =====
library(limma)
# 构建设计矩阵
group <- factor(pData(grSet_funnorm)$Group)
design <- model.matrix(~ 0 + group)
colnames(design) <- levels(group)
# 对比矩阵
contrast_matrix <- makeContrasts(
Disease_vs_Control = Disease - Control,
levels = design
)
# 用M值做limma分析
fit <- lmFit(m_values, design)
fit2 <- contrasts.fit(fit, contrast_matrix)
fit3 <- eBayes(fit2)
# 提取结果
dmp_results <- topTable(fit3, coef = "Disease_vs_Control", number = Inf, sort.by = "p")
# 计算delta Beta
beta_disease <- rowMeans(beta_values[, group == "Disease"])
beta_control <- rowMeans(beta_values[, group == "Control"])
dmp_results$deltaBeta <- beta_disease[rownames(dmp_results)] - beta_control[rownames(dmp_results)]
# 筛选显著DMP
sig_dmp <- dmp_results[dmp_results$adj.P.Val < 0.05 & abs(dmp_results$deltaBeta) > 0.1, ]
nrow(sig_dmp)
# ===== ChAMP DMP =====
myDMP <- champ.DMP(
beta = myCombat,
pheno = myLoad$pd$Sample_Group,
compare.group = c("Disease", "Control"),
adjPVal = 0.05,
adjust.method = "BH",
arraytype = "450K"
)
# 结果是一个列表,每个比较一个数据框
head(myDMP[[1]])
# ===== 可视化 =====
# 火山图
library(ggplot2)
ggplot(dmp_results, aes(x = deltaBeta, y = -log10(adj.P.Val))) +
geom_point(aes(color = (adj.P.Val < 0.05 & abs(deltaBeta) > 0.1)), size = 0.5) +
scale_color_manual(values = c("grey", "red")) +
geom_hline(yintercept = -log10(0.05), linetype = "dashed") +
geom_vline(xintercept = c(-0.1, 0.1), linetype = "dashed") +
theme_classic() +
labs(x = "Delta Beta", y = "-log10(adj.P.Val)", title = "DMP Volcano Plot")
# 热图
library(pheatmap)
top50 <- head(sig_dmp, 50)
pheatmap(beta_values[rownames(top50), ],
annotation_col = data.frame(Group = group, row.names = colnames(beta_values)),
scale = "row",
show_rownames = FALSE)
第七步:差异甲基化区域分析(DMR)¶
白话解释: DMP是看单个位点,而DMR是把相邻的差异位点"连片"来看——在基因组上,如果一段区域内多个CpG位点都有甲基化变化,这个区域可能更有生物学意义(因为甲基化调控通常是区域性的)。
技术细节: DMR检测的核心思想是利用CpG位点的空间邻近性和甲基化变化的一致性。常用方法: - Bumphunter:minfi内置,基于平滑后的统计量寻找"bump" - DMRcate:基于核平滑方法,简单高效 - ProbeLasso:ChAMP内置方法
# ===== DMRcate =====
BiocManager::install("DMRcate")
library(DMRcate)
# 构建注释对象
myAnnotation <- cpg.annotate(
datatype = "array",
object = m_values,
what = "M",
analysis.type = "differential",
design = design,
contrasts = TRUE,
cont.matrix = contrast_matrix,
coef = "Disease_vs_Control",
arraytype = "450K", # 或 "EPIC"
fdr = 0.05
)
# 提取DMR
dmr_results <- dmrcate(myAnnotation, lambda = 1000, C = 2)
# 转换为GRanges对象
dmr_ranges <- extractRanges(dmr_results, genome = "hg19")
dmr_ranges
# DMR可视化
DMR.plot(
ranges = dmr_ranges,
dmr = 1, # 第几个DMR
CpGs = getBeta(grSet_funnorm),
phen.col = c("blue", "red"),
what = "Beta",
arraytype = "450K",
genome = "hg19"
)
# ===== ChAMP DMR =====
myDMR <- champ.DMR(
beta = myCombat,
pheno = myLoad$pd$Sample_Group,
compare.group = c("Disease", "Control"),
arraytype = "450K",
method = "Bumphunter", # 或 "DMRcate", "ProbeLasso"
minProbes = 3, # DMR至少包含的探针数
adjPvalDmr = 0.05,
cores = 4,
resultsDir = "./CHAMP_DMR/"
)
第八步:功能注释与富集分析¶
白话解释: 找到差异甲基化位点/区域后,我们需要知道它们在基因组上的"位置"——是在基因的启动子区还是基因体区?是在CpG岛还是CpG shore?这些位点涉及哪些生物学通路?
技术细节: 功能注释维度包括: - 基因组位置:TSS1500, TSS200, 5'UTR, 1st Exon, Body, 3'UTR, Intergenic - CpG岛关系:Island, Shore(±2kb), Shelf(±4kb), OpenSea - 调控元件:Enhancer, DHS(DNase超敏位点) - 富集分析:GO、KEGG通路分析(注意:甲基化芯片有探针设计偏倚,需用missMethyl校正)
# ===== missMethyl富集分析(推荐) =====
# missMethyl考虑了芯片设计偏倚:大基因上的探针更多,更容易被选中
BiocManager::install("missMethyl")
library(missMethyl)
# GO分析
go_results <- gometh(
sig.cpg = rownames(sig_dmp), # 显著DMP
all.cpg = rownames(m_values), # 所有检测的CpG
collection = "GO",
array.type = "450K", # 或 "EPIC"
plot.bias = TRUE # 绘制偏倚校正图
)
# 显示前20个通路
topGO <- topGSA(go_results, number = 20)
print(topGO)
# KEGG分析
kegg_results <- gometh(
sig.cpg = rownames(sig_dmp),
all.cpg = rownames(m_values),
collection = "KEGG",
array.type = "450K"
)
topKEGG <- topGSA(kegg_results, number = 20)
print(topKEGG)
# ===== ChAMP GSEA =====
myGSEA <- champ.GSEA(
beta = myCombat,
DMP = myDMP[[1]],
DMR = myDMR,
pheno = myLoad$pd$Sample_Group,
arraytype = "450K",
adjPval = 0.05,
method = "gometh"
)
# ===== 注释信息提取 =====
# 使用芯片注释包
library(IlluminaHumanMethylation450kanno.ilmn12.hg19)
ann450k <- getAnnotation(IlluminaHumanMethylation450kanno.ilmn12.hg19)
# 给DMP结果添加注释
sig_dmp_anno <- merge(sig_dmp, ann450k[, c("chr", "pos", "UCSC_RefGene_Name",
"UCSC_RefGene_Group", "Relation_to_Island",
"Regulatory_Feature_Group")],
by = "row.names")
# 统计各区域分布
table(sig_dmp_anno$UCSC_RefGene_Group)
table(sig_dmp_anno$Relation_to_Island)
第九步:高级分析¶
白话解释: 除了基本的差异分析,还有一些进阶分析方法,比如用甲基化数据估算细胞组成(在血液样本中特别有用)、甲基化年龄预测、拷贝数变异推断等。
# ===== 细胞组成估计(血液样本) =====
library(FlowSorted.Blood.450k)
# 或 FlowSorted.Blood.EPIC
cellCounts <- estimateCellCounts(
rgSet,
compositeCellType = "Blood",
processMethod = "auto",
cellTypes = c("CD8T", "CD4T", "NK", "Bcell", "Mono", "Gran")
)
head(cellCounts)
# 将细胞组成纳入差异分析模型(校正混杂)
design_adj <- model.matrix(~ 0 + group + cellCounts)
# ===== 甲基化年龄(Epigenetic Clock) =====
# 使用Horvath时钟或其他时钟
# 推荐使用在线工具:https://dnamage.genetics.ucla.edu/
# 或R包 methylclock
BiocManager::install("methylclock")
library(methylclock)
# 计算多种甲基化年龄
age_results <- DNAmAge(beta_values, clocks = c("Horvath", "Hannum", "PhenoAge", "skinHorvath"))
# ===== 拷贝数变异(CNV)推断 =====
# minfi内置功能
library(conumee)
# 需要参考样本(正常样本)
# cnv_data <- CNV.load(rgSet)
# cnv_fit <- CNV.fit(cnv_data[tumor_samples], cnv_data[normal_samples])
# CNV.genomeplot(cnv_fit)
实战命令(可复制)¶
完整minfi分析流程¶
# === 环境准备 ===
if (!requireNamespace("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install(c("minfi", "limma", "DMRcate", "missMethyl",
"IlluminaHumanMethylation450kanno.ilmn12.hg19",
"IlluminaHumanMethylation450kmanifest",
"pheatmap", "ggplot2"))
library(minfi)
library(limma)
library(DMRcate)
library(missMethyl)
library(pheatmap)
library(ggplot2)
# === 读取数据 ===
targets <- read.metharray.sheet("./idat_dir/", pattern = "SampleSheet.csv")
rgSet <- read.metharray.exp(targets = targets)
# === 质控 ===
detP <- detectionP(rgSet)
keep <- colMeans(detP > 0.01) < 0.05
rgSet <- rgSet[, keep]
detP <- detP[, keep]
# === 归一化 ===
grSet <- preprocessFunnorm(rgSet)
beta <- getBeta(grSet)
mval <- getM(grSet)
# === 过滤 ===
# 检测P值不合格的探针
beta[detP[rownames(beta), colnames(beta)] > 0.01] <- NA
mval[detP[rownames(mval), colnames(mval)] > 0.01] <- NA
keep_probes <- rowMeans(is.na(beta)) == 0
beta <- beta[keep_probes, ]
mval <- mval[keep_probes, ]
# 过滤SNP探针和交叉反应探针
grSet_filtered <- dropLociWithSnps(grSet[keep_probes, ])
beta <- getBeta(grSet_filtered)
mval <- getM(grSet_filtered)
# 过滤性染色体
ann <- getAnnotation(grSet_filtered)
keep_autosome <- !(ann$chr %in% c("chrX", "chrY"))
beta <- beta[keep_autosome, ]
mval <- mval[keep_autosome, ]
# === DMP分析 ===
group <- factor(pData(grSet)$Group)
design <- model.matrix(~ 0 + group)
colnames(design) <- levels(group)
contMatrix <- makeContrasts(Disease - Control, levels = design)
fit <- lmFit(mval, design)
fit2 <- contrasts.fit(fit, contMatrix)
fit3 <- eBayes(fit2)
dmp <- topTable(fit3, number = Inf, coef = 1, sort.by = "p")
# 添加delta Beta
dmp$deltaBeta <- rowMeans(beta[rownames(dmp), group == "Disease"]) -
rowMeans(beta[rownames(dmp), group == "Control"])
sig_dmp <- dmp[dmp$adj.P.Val < 0.05 & abs(dmp$deltaBeta) > 0.1, ]
# === DMR分析 ===
myAnno <- cpg.annotate("array", mval, what = "M", analysis.type = "differential",
design = design, contrasts = TRUE, cont.matrix = contMatrix,
coef = 1, arraytype = "450K", fdr = 0.05)
dmr <- dmrcate(myAnno, lambda = 1000, C = 2)
dmr_ranges <- extractRanges(dmr, genome = "hg19")
# === 富集分析 ===
go <- gometh(sig.cpg = rownames(sig_dmp), all.cpg = rownames(mval),
collection = "GO", array.type = "450K")
kegg <- gometh(sig.cpg = rownames(sig_dmp), all.cpg = rownames(mval),
collection = "KEGG", array.type = "450K")
完整ChAMP分析流程¶
library(ChAMP)
# 一步加载+质控+过滤
myLoad <- champ.load(directory = "./idat_dir/", arraytype = "450K")
# 质控可视化
champ.QC(beta = myLoad$beta, pheno = myLoad$pd$Sample_Group)
# 归一化
myNorm <- champ.norm(beta = myLoad$beta, rgSet = myLoad$rgSet,
method = "BMIQ", arraytype = "450K", cores = 4)
# 批次效应检测
champ.SVD(beta = myNorm, pd = myLoad$pd)
# 批次校正(如需要)
myCombat <- champ.runCombat(beta = myNorm, pd = myLoad$pd, batchname = c("Slide"))
# DMP分析
myDMP <- champ.DMP(beta = myCombat, pheno = myLoad$pd$Sample_Group)
# DMR分析
myDMR <- champ.DMR(beta = myCombat, pheno = myLoad$pd$Sample_Group,
method = "Bumphunter", arraytype = "450K", cores = 4)
# 富集分析
myGSEA <- champ.GSEA(beta = myCombat, DMP = myDMP[[1]], DMR = myDMR,
pheno = myLoad$pd$Sample_Group, arraytype = "450K")
GEO数据下载¶
# 从GEO下载已有甲基化芯片数据
library(GEOquery)
gset <- getGEO("GSE123456", destdir = "./geo_data/")
# 提取Beta值矩阵
beta_geo <- exprs(gset[[1]])
pheno_geo <- pData(gset[[1]])
面试常问点¶
Q1: Beta值和M值有什么区别?什么时候用哪个?¶
A: Beta值范围0-1,直观表示甲基化比例,适合结果展示和生物学解读。M值是Beta值的logit变换(M = log2(Beta/(1-Beta))),方差更均匀(homoscedastic),适合统计分析(如limma线性模型)。实践中通常用M值做统计检验,用Beta值做可视化和结果报告。Du et al. (2010) 的论文详细比较了两者在统计分析中的表现。
Q2: 为什么要用missMethyl做富集分析而不是直接用clusterProfiler?¶
A: 甲基化芯片存在"探针数量偏倚"——大基因上的探针多,被选为差异的概率天然更高。直接用标准的超几何检验(如clusterProfiler)会导致大基因相关通路被过度富集。missMethyl的gometh函数使用Wallenius非中心超几何分布,根据每个基因上的探针数量进行校正,消除这种偏倚。这是甲基化芯片分析的独有问题。
Q3: Infinium I型和II型探针有什么区别?为什么要做类型归一化?¶
A: I型探针使用两个珠子类型(甲基化和非甲基化各一个),只用一个颜色通道。II型探针只用一个珠子类型,同时用红绿两个颜色通道区分甲基化状态。II型探针的动态范围窄于I型(尤其在极端Beta值处),导致两种探针的Beta值分布不同。BMIQ等归一化方法将II型分布校正为I型分布,消除这种技术差异。
Q4: 450K和EPIC芯片的数据能混合分析吗?¶
A: 可以但需谨慎。核心思路是取两个平台的交集探针(约45万个),然后做联合归一化。需要注意:(1) 使用combine函数合并数据前需统一annotation;(2) 必须用ComBat等方法校正平台间的批次效应;(3) 仅限于交集探针分析,EPIC特有的enhancer探针信息会丢失。建议使用ChAMP的champ.load()搭配merge功能。
Q5: 血液样本分析时为什么要估算细胞组成?¶
A: 血液中不同免疫细胞类型(T细胞、B细胞、NK细胞、单核细胞、粒细胞等)有不同的DNA甲基化图谱。如果疾病组和对照组的细胞组成比例不同(这很常见),观察到的甲基化差异可能仅仅反映细胞比例变化,而非真正的表观遗传变化。使用minfi的estimateCellCounts()估算细胞组成后纳入线性模型,可以校正这种混杂。这是甲基化研究中的重要混杂因素之一。
Q6: 如何定义DMP的筛选标准?¶
A: 通常采用双重标准:(1) 统计显著性:adjusted P-value < 0.05(BH校正);(2) 效应大小:|ΔBeta| > 0.1或0.2。仅用P值会选出很多ΔBeta很小(如0.01-0.02)的位点,这些在生物学上可能没有意义。ΔBeta阈值的选择取决于研究背景:肿瘤研究中常用0.2(甲基化变化更大),而环境暴露研究中可能用0.05(效应较小)。
Q7: DMR和DMP的关系是什么?什么时候更应该关注DMR?¶
A: DMP关注单个CpG位点,DMR关注相邻多个CpG位点组成的区域。DMR通常更有生物学意义,因为甲基化调控往往是区域性的——一个调控元件上的多个CpG位点倾向于共同变化。当研究启动子区域甲基化对基因表达的调控时,DMR更合适。但DMP分析计算简单、统计功效更好,且某些单个CpG位点确实可作为生物标志物。
易错点¶
1. 没有过滤交叉反应探针和SNP探针¶
问题: 450K/EPIC芯片上有些探针可以与多个基因组位置杂交(cross-reactive),还有些探针的目标CpG位点包含常见SNP。这些探针的甲基化信号不可靠。 解决: 必须使用已发表的交叉反应探针列表(Chen et al. 2013; Pidsley et al. 2016)和dbSNP注释进行过滤。ChAMP的champ.load()默认会做这些过滤,minfi需手动使用dropLociWithSnps()。
2. 直接对Beta值做线性回归¶
问题: Beta值在0和1附近方差压缩严重(heteroscedastic),违反线性模型的等方差假设,导致P值不准确——极端甲基化位点的统计显著性被低估。 解决: 统计分析时使用M值。如果坚持用Beta值,可以考虑beta回归模型(betareg包),但计算量大。
3. 忽略细胞组成的混杂效应¶
问题: 在全血或混合组织样本中,观察到的甲基化差异可能只是细胞比例变化的反映,而非真正的表观遗传变化。发表后被reviewer质疑。 解决: 对血液样本使用estimateCellCounts()或EpiDISH等方法估算细胞组成,并将估计结果作为协变量纳入线性模型。
4. 富集分析不校正探针数量偏倚¶
问题: 大基因(如neuronal genes)上的探针数量多,更容易包含显著DMP,导致标准超几何检验过度富集这些基因相关的通路。 解决: 使用missMethyl的gometh()函数,它使用Wallenius非中心超几何分布来校正这种偏倚。
5. 不做性染色体过滤就分析混合性别样本¶
问题: X染色体在女性中有一条被大面积甲基化(X-inactivation),男女性染色体甲基化模式差异巨大。如果不过滤且组间性别比例不均,会产生大量假阳性。 解决: 混合性别样本应过滤X和Y染色体探针(filterXY = TRUE),或将性别作为协变量纳入模型。如果专门研究性别差异的甲基化,则需保留但特殊处理。
6. 归一化方法选择不当¶
问题: 不同归一化方法有不同适用场景。如对含有大量差异的肿瘤vs正常样本使用Quantile normalization,可能抹掉真实的生物学差异。 解决: 大的生物学差异(如肿瘤vs正常)推荐Funnorm(利用控制探针);组间差异较小的研究推荐BMIQ或Quantile。始终检查归一化前后的密度图和MDS图。
7. DMR分析参数设置不合理¶
问题: DMR检测的lambda(平滑窗口)和minProbes(最少探针数)参数对结果影响大。lambda过大会合并不相关区域,过小则碎片化。 解决: DMRcate默认lambda=1000bp,C=2通常合适。minProbes建议≥3。始终可视化top DMR验证结果合理性,并与DMP结果交叉验证。
补充知识¶
芯片vs WGBS vs RRBS¶
| 方法 | 覆盖CpG数 | 成本 | 分辨率 | 适用场景 |
|---|---|---|---|---|
| 450K芯片 | ~485K | 低 | 单碱基 | 大队列研究 |
| EPIC芯片 | ~850K | 中 | 单碱基 | 大队列+enhancer |
| RRBS | ~1-3M | 中 | 单碱基 | CpG岛为主 |
| WGBS | ~28M | 高 | 单碱基 | 全基因组无偏覆盖 |
甲基化与基因表达的关系¶
- 启动子区甲基化:通常与基因沉默相关(负相关)
- 基因体甲基化:与活跃转录相关(正相关)
- 增强子甲基化:影响远端基因调控
- 这种关系不是绝对的,需要整合表达数据验证
常用公共数据资源¶
- GEO:大量甲基化芯片数据集
- TCGA:肿瘤样本的450K/EPIC数据
- ENCODE:甲基化参考图谱
- GEO数据处理:可用GEOquery包下载已处理的Beta值矩阵
下游整合分析¶
- 甲基化+表达联合分析:鉴定受甲基化调控的基因
- 甲基化+突变数据:研究体细胞突变对甲基化的影响
- 甲基化生物标志物开发:利用LASSO/随机森林筛选诊断/预后标志物
- Mendelian Randomization:利用mQTL研究甲基化的因果效应
EPIC v2分析注意事项¶
- 推荐使用SeSAMe:SeSAMe对EPIC v2有完整支持,推荐作为预处理前端(sesame包 + sesameData包)
- EPICv2 manifest包:Bioconductor提供
IlluminaHumanMethylationEPICv2manifest和IlluminaHumanMethylationEPICv2anno.20a1.hg38注释包 - 探针去重:EPIC v2存在replicate probes(同一CpG位点有多个探针),分析前需处理
- Liftover:SeSAMe提供
mLiftOver函数,支持EPIC v2与EPIC v1之间的探针映射转换 - DMRcate:最新版本DMRcate已支持EPIC v2,可与SeSAMe搭配使用
新兴技术趋势¶
- 单细胞甲基化测序(scBS-seq):揭示细胞异质性
- 长读长甲基化检测(Nanopore):直接检测修饰,不需亚硫酸氢盐转化
- 空间甲基化组学:结合空间位置信息
- 多组学整合:甲基化+转录组+染色质可及性联合分析