跳转至

171_表观时钟分析

一句话概述

DNA甲基化时钟(Epigenetic Clocks)利用特定CpG位点的甲基化水平通过训练好的回归模型预测生物学年龄,包括第一代(Horvath/Hannum预测实际年龄)和第二代(GrimAge/DunedinPACE预测死亡风险和衰老速率)时钟,年龄加速(Age Acceleration)与疾病和死亡风险相关。


核心知识点总览

知识点说明
Horvath时钟353个CpG,泛组织适用,预测实际年龄
Hannum时钟71个CpG,基于血液,预测实际年龄
GrimAge基于血浆蛋白和吸烟Pack-years的复合时钟
DunedinPACE衰老速率(Pace of Aging)估计
年龄加速DNAm年龄 - 实际年龄(正值=加速衰老)
450K/EPIC芯片Illumina甲基化芯片(探针覆盖CpG位点)
Beta值甲基化比例(0-1)
临床应用衰老研究、疾病预后、干预效果评估

各步骤详解

第一步:理解表观时钟

白话解释: 随着年龄增长,DNA上某些位置的甲基化会有规律地增加或减少。科学家们发现了一组"时钟CpG位点",只要测量这些位点的甲基化程度,就能准确预测一个人的生物学年龄。如果你的"DNA年龄"比实际年龄大5岁(年龄加速),说明你的身体老得比同龄人快,患病和死亡风险也更高。

时钟对比: | 时钟 | CpG数 | 训练数据 | 预测目标 | 临床价值 | |------|--------|---------|---------|---------| | Horvath (2013) | 353 | 多组织 | 实际年龄 | 泛组织生物年龄 | | Hannum (2013) | 71 | 血液 | 实际年龄 | 血液衰老 | | PhenoAge (2018) | 513 | 血液+表型 | 死亡风险 | 疾病预后 | | GrimAge (2019) | ~1000 | 血浆蛋白+吸烟 | 死亡时间 | 最强预后价值 | | DunedinPACE (2022) | 173 | 纵向队列 | 衰老速率 | 干预效果 |


第二步:甲基化数据处理

代码示例:

# === 从IDAT文件处理甲基化芯片数据 ===
library(minfi)
library(IlluminaHumanMethylationEPICanno.ilm10b4.hg19)

# 读取IDAT文件
rgSet <- read.metharray.exp("idat_directory/")

# 质控
qc <- getQC(preprocessRaw(rgSet))
plotQC(qc)

# 归一化 (推荐BMIQ或Noob)
mSet <- preprocessNoob(rgSet)  # 背景校正+染料偏差校正

# 获取Beta值矩阵
beta_matrix <- getBeta(mSet)
# Beta值范围[0,1]: 0=未甲基化, 1=完全甲基化

# 探针过滤
# 去除检测P值>0.01的探针
detP <- detectionP(rgSet)
failed_probes <- rownames(detP)[rowMeans(detP > 0.01) > 0.05]
beta_filtered <- beta_matrix[!rownames(beta_matrix) %in% failed_probes, ]

# 去除SNP探针和cross-reactive探针
# (使用已发表的列表)
beta_clean <- beta_filtered[!rownames(beta_filtered) %in% cross_reactive_probes, ]

cat(sprintf("保留探针数: %d\n", nrow(beta_clean)))


第三步:计算各表观时钟

代码示例:

# === 使用methylclock包 ===
# BiocManager::install("methylclock")
library(methylclock)

# 计算多个时钟
clock_results <- DNAmAge(
  beta_clean,
  clocks = c("Horvath", "Hannum", "skinHorvath", "PhenoAge", "Zhang")
)

# === 使用Horvath在线计算器格式 ===
# https://dnamage.clockfoundation.org/
# 需要上传beta值矩阵(行=CpG探针ID, 列=样本)

# === 手动计算Horvath时钟(概念) ===
# Horvath时钟 = 反变换(截距 + Σ(系数_i × 甲基化_i))
# 353个CpG的系数已发表
horvath_coefs <- read.csv("horvath_coefficients.csv")  # cpg_id, coefficient

# 匹配CpG
common_cpgs <- intersect(horvath_coefs$cpg_id, rownames(beta_clean))
beta_horvath <- beta_clean[common_cpgs, ]
coefs <- horvath_coefs$coefficient[match(common_cpgs, horvath_coefs$cpg_id)]

# 线性预测
linear_pred <- as.numeric(t(coefs) %*% beta_horvath) + horvath_intercept

# 反变换(Horvath使用的变换)
horvath_transform_inv <- function(x) {
  ifelse(x < 0, (1 + adult_age) * exp(x) - 1,
         (1 + adult_age) * x + adult_age)
}
dnam_age <- horvath_transform_inv(linear_pred)

# === GrimAge和DunedinPACE ===
# 使用PC-based时钟 (更推荐)
# devtools::install_github("danbelsky/DunedinPACE")
library(DunedinPACE)

# DunedinPACE计算
pace_results <- PACEProjector(beta_clean)
# 输出: 衰老速率(1.0=正常速率; >1=加速衰老; <1=减缓衰老)

# === PC Clocks (Higgins-Chen et al. 2022) ===
# 更鲁棒的主成分版本
# devtools::install_github("MorganLevineLab/PC-Clocks")


第四步:年龄加速分析

代码示例:

library(ggplot2)

# === 计算年龄加速 ===
results <- data.frame(
  SampleID = colnames(beta_clean),
  Chronological_Age = sample_metadata$age,
  Horvath_Age = clock_results$Horvath,
  Hannum_Age = clock_results$Hannum,
  PhenoAge = clock_results$PhenoAge
)

# 方法1: 简单差值
results$AgeAccel_Horvath <- results$Horvath_Age - results$Chronological_Age

# 方法2: 残差法(推荐) - 去除年龄本身的影响
lm_fit <- lm(Horvath_Age ~ Chronological_Age, data = results)
results$AgeAccel_residual <- residuals(lm_fit)
# 正残差=比同龄人"老"; 负残差=比同龄人"年轻"

# === 与疾病/表型关联 ===
# 比较病例vs对照的年龄加速
results$Disease <- sample_metadata$disease_status

ggplot(results, aes(x = Disease, y = AgeAccel_residual, fill = Disease)) +
  geom_boxplot() +
  geom_jitter(width = 0.2, alpha = 0.3) +
  stat_compare_means(method = "wilcox.test") +
  theme_minimal() +
  labs(title = "Epigenetic Age Acceleration by Disease Status",
       y = "Age Acceleration (years)")

# === 生存分析 ===
library(survival)
library(survminer)

results$AgeAccel_group <- ifelse(results$AgeAccel_residual > 0, "Accelerated", "Normal")
fit <- survfit(Surv(OS_time, OS_event) ~ AgeAccel_group, data = results)
ggsurvplot(fit, pval = TRUE, risk.table = TRUE,
           title = "Survival by Epigenetic Age Acceleration")

# Cox回归(连续变量)
cox_fit <- coxph(Surv(OS_time, OS_event) ~ AgeAccel_residual + age + sex, data = results)
summary(cox_fit)


第五步:干预效果评估

代码示例:

# === DunedinPACE评估抗衰老干预效果 ===
# 干预前后配对比较

pre_pace <- pace_results[metadata$timepoint == "Baseline"]
post_pace <- pace_results[metadata$timepoint == "Post_intervention"]

# 配对t检验
t_test <- t.test(pre_pace, post_pace, paired = TRUE)
cat(sprintf("干预前PACE: %.3f ± %.3f\n", mean(pre_pace), sd(pre_pace)))
cat(sprintf("干预后PACE: %.3f ± %.3f\n", mean(post_pace), sd(post_pace)))
cat(sprintf("变化: %.3f (p = %.4f)\n", mean(post_pace - pre_pace), t_test$p.value))

# DunedinPACE解读:
# 1.0 = 每年老1年(正常)
# 1.2 = 每年老1.2年(加速20%)
# 0.8 = 每年老0.8年(减缓20%)
# 有效干预应使PACE降低

# === 效应量可视化 ===
ggplot(data.frame(pre = pre_pace, post = post_pace), aes(x = pre, y = post)) +
  geom_point() +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "red") +
  labs(x = "Baseline DunedinPACE", y = "Post-intervention DunedinPACE",
       title = "Intervention Effect on Pace of Aging") +
  theme_minimal()


实战命令

# === 甲基化时钟分析流程 ===
# 1. IDAT处理
Rscript process_idat.R --idat_dir data/ --output beta_matrix.rds

# 2. 计算时钟
Rscript calculate_clocks.R --beta beta_matrix.rds \
  --clocks "Horvath,Hannum,PhenoAge,GrimAge" --output clock_ages.csv

# 3. 年龄加速分析
Rscript age_acceleration.R --clock_ages clock_ages.csv \
  --metadata clinical.csv --output results/

面试常问点

Q1:Horvath时钟和GrimAge有什么本质区别?

A: Horvath(2013)直接用甲基化预测实际年龄(chronological age)——训练目标是年龄本身。GrimAge(2019)是两步法:先用甲基化预测7个血浆蛋白水平和吸烟Pack-years,再用这些预测值构建死亡风险模型。本质区别:Horvath测量"时间流逝"(生物时钟);GrimAge测量"损伤积累"(死亡风险)。GrimAge对全因死亡、心血管病、癌症的预测力远强于Horvath。

Q2:DunedinPACE与其他时钟有什么不同?

A: DunedinPACE测量的是衰老速率(pace)而非生物年龄(age)。其他时钟给出一个"年龄"(如DNAm age=55岁);DunedinPACE给出一个"速度"(如1.2 = 每年老化1.2年)。DunedinPACE从Dunedin纵向队列的20年追踪数据训练,捕捉的是真实的纵向变化率。优势:(1) 对短期干预更敏感;(2) 不受出生年代效应影响;(3) 与未来健康结局的关联更强。

Q3:如何正确计算"年龄加速"?

A: 推荐残差法(intrinsic/extrinsic age acceleration):将DNAm年龄对实际年龄做线性回归,取残差作为年龄加速指标。原因:简单差值(DNAm Age - Chrono Age)与实际年龄相关(年轻人倾向正残差,老年人倾向负残差)。残差法消除了这种年龄相关偏差。进一步可以校正细胞组成(IEAA)或不校正(EEAA)来区分细胞内在衰老和免疫衰老。

Q4:表观时钟在疾病中有什么临床应用?

A: (1) 癌症预后:肿瘤组织的年龄加速与侵袭性和预后相关;(2) HIV/感染:HIV感染导致5-7年的表观年龄加速;(3) 神经退行性疾病:AD患者脑组织年龄加速;(4) 衰老干预评估:运动/热量限制/Metformin试验用DunedinPACE作为endpoint;(5) 儿科:发育延迟检测;(6) 法医:从DNA估计未知个体年龄。

Q5:450K和EPIC芯片的时钟兼容性问题?

A: Horvath时钟353个CpG中约95%在EPIC芯片上有探针,少数丢失的CpG可以用imputation处理或使用适配版本。GrimAge需要的CpG大部分在两个平台都有。DunedinPACE专门针对EPIC设计。注意:(1) 不同芯片版本的beta值可能有系统偏差,跨平台比较需谨慎;(2) 使用EPIC v2芯片时检查探针覆盖;(3) PC-based时钟(PC Clocks)对缺失探针更鲁棒。


易错点

1. 未对beta值矩阵做适当归一化

错误: 原始beta值直接输入时钟计算。 正确做法: 必须做背景校正和归一化(如Noob、BMIQ)。不同归一化方法会影响时钟预测5-10年。推荐使用Noob(与多数时钟训练数据一致)。

2. 细胞组成未校正

错误: 比较不同免疫状态组的年龄加速而不校正血液细胞比例。 正确做法: 血液中不同细胞类型有不同的甲基化谱。使用Houseman方法估计细胞比例,在年龄加速分析中纳入细胞比例作为协变量(得到IEAA)。

3. 混淆不同时钟的含义

错误: 用Horvath时钟评估干预效果。 正确做法: Horvath/Hannum时钟对短期干预不够敏感(因为训练目标是chronological age)。评估干预效果推荐DunedinPACE;评估死亡风险推荐GrimAge。

4. 小样本下过度解读

错误: 20个样本就声称发现5年年龄加速。 正确做法: 时钟预测有误差(MAE ~3-5年)。样本量小时个体误差大。建议≥50人/组才有足够统计力检测2-3年的组间差异。


补充知识

计算工具

  • methylclock(R): 多时钟计算包
  • DNAmAge Calculator: Horvath在线工具
  • DunedinPACE(R): 衰老速率计算
  • PC-Clocks(R): PC-based鲁棒时钟
  • EpiDISH: 细胞组成估计

前沿发展

  • 单细胞表观时钟: 从scBS-seq数据估计单细胞年龄
  • 器官特异时钟: 脑/肝/肾特异性衰老时钟
  • AlphaAge: 基于深度学习的新一代时钟
  • 端粒时钟: 结合甲基化和端粒长度的复合衰老指标