1. 为什么选择TCGAbiolinks和edgeR?一个生物信息学新手的真实心路
大家好,我是老张,一个在生物信息学领域摸爬滚打了十来年的“老油条”。今天,我想和你聊聊一个几乎所有癌症研究都绕不开的起点:从TCGA数据库里挖出那些关键的差异表达基因。你可能看过很多教程,但总觉得云里雾里,代码跑不通,结果看不懂。别担心,这篇文章就是为你准备的。我会把我自己踩过的坑、总结的经验,用最直白的话讲给你听,保证你跟着做一遍,就能拿到属于你自己的分析结果。
我们先来聊聊工具的选择。TCGA数据库就像一座巨大的金矿,里面藏着海量的癌症基因组数据。但怎么挖?徒手肯定不行。早期很多人用网页手动下载,那体验,简直是噩梦。几十上百个样本,每个样本一堆文件,下载慢不说,后续整理合并能让你怀疑人生。后来,R语言社区出现了TCGAbiolinks这个神器。它就像是官方给你配的“自动化采矿机”,你只需要告诉它你要什么矿(哪个癌种、什么数据类型),它就能帮你从TCGA的官方接口(GDC)里规规矩矩、整整齐齐地把数据给你搬回来,而且是带着完整的元数据信息。这不仅仅是省时间,更重要的是保证了数据的规范性和可追溯性,这对后续分析至关重要。
数据挖回来了,怎么分析?这时候就该edgeR上场了。在差异基因分析这个江湖里,edgeR、DESeq2、limma-voom是三大高手。为什么我偏爱edgeR?尤其是在处理像TCGA这样的RNA-seq计数(Count)数据时。edgeR是专门为计数数据设计的,它基于负二项分布模型,能很好地处理测序数据中普遍存在的过度离散问题。简单来说,就是基因的表达量波动很大,edgeR的统计模型能更准确地捕捉这种波动,从而更可靠地判断一个基因在癌组织和正常组织之间是不是真的“有差异”。它的结果稳健,速度也快,对于刚上手的朋友来说,文档和社区资源都非常丰富,遇到问题很容易找到解答。
所以,TCGAbiolinks + edgeR这个组合,对我来说就是一个“黄金搭档”。一个管高效、规范地获取数据,一个管精准、可靠地分析数据。这套流程我已经在膀胱癌、肺癌等多个项目中反复验证过,稳定、可重复。接下来,我就手把手带你走一遍这个全流程,从零开始,直到拿到那份闪闪发光的差异基因列表。
2. 实战第一步:用TCGAbiolinks优雅地获取TCGA数据
还记得我最早用脚本批量爬网页下载的日子吗?经常下到一半断掉,或者文件名乱七八糟。用了TCGAbiolinks之后,我才知道什么叫“优雅”。整个过程清晰可控,完全在R环境里完成。
2.1 环境搭建与包安装
工欲善其事,必先利其器。首先,我们需要安装必要的R包。这里我强烈推荐使用BiocManager来管理生物信息学相关的R包,它就像是R的“应用商店”特别版,能自动处理包之间的依赖关系,避免版本冲突。
# 如果还没安装BiocManager,就先安装它 if (!requireNamespace("BiocManager", quietly = TRUE)) install.packages("BiocManager") # 用BiocManager安装TCGAbiolinks和edgeR BiocManager::install("TCGAbiolinks") BiocManager::install("edgeR") BiocManager::install("limma") # edgeR的伴侣包,经常一起用 # 安装成功后,加载它们 library(TCGAbiolinks) library(edgeR) library(limma)安装过程可能会需要一点时间,因为要下载一些依赖包。如果遇到网络问题,可以尝试设置一下国内的CRAN镜像和Bioconductor镜像,速度会快很多。这一步是基础,确保你的R版本不要太旧(建议4.0以上),就能顺利安装。
2.2 构建查询:告诉GDC你想要什么
TCGA数据都存放在GDC(Genomic Data Commons)门户上。TCGAbiolinks的核心功能,就是帮你用代码的方式向GDC提交一个精准的查询。这比在网页上点点点高效多了。
假设我们现在想研究肾透明细胞癌(TCGA-KIRC),我们需要它的RNA-seq原始计数数据。下面这行代码就是构建查询的灵魂:
# 设置工作目录,所有下载的文件会放在这里 setwd("D:/TCGA_Analysis") # 构建GDC查询 query <- GDCquery( project = "TCGA-KIRC", # 项目ID,这里是肾透明细胞癌 data.category = "Transcriptome Profiling", # 数据类别:转录组分析 data.type = "Gene Expression Quantification", # 数据类型:基因表达定量 workflow.type = "HTSeq - Counts" # 流程类型:HTSeq生成的原始计数 )我来解释一下这几个关键参数,理解了它们,你就能举一反三下载其他数据:
project:指定癌症类型。TCGA-KIRC代表肾透明细胞癌。如果你要研究乳腺癌,可能就是TCGA-BRCA。你可以用TCGAbiolinks:::getGDCprojects()$project_id这个命令查看所有可用的项目ID。data.category:这是个大类。Transcriptome Profiling(转录组分析)是我们最常用的,里面包含了基因表达数据。除此之外,还有Copy Number Variation(拷贝数变异)、DNA Methylation(DNA甲基化)、Clinical(临床数据)等宝藏,等你后续挖掘。data.type:在Transcriptome Profiling这个大类下,有几种数据格式,我们选择Gene Expression Quantification,即基因表达定量结果。workflow.type:这是最关键的过滤项。对于表达数据,GDC提供了几种预处理流程的结果。HTSeq - Counts是原始的基因计数(raw counts),这是进行差异表达分析(比如用edgeR)所必须的输入数据。千万不要下成HTSeq - FPKM或HTSeq - FPKM-UQ,那些是标准化后的表达量,不适合直接用edgeR做差异分析。
运行这行代码后,R控制台会打印出查询摘要,告诉你匹配到了多少个病例、多少个文件。确认无误后,就可以下载了。
2.3 执行下载与数据整理
下载命令很简单:
# 下载数据(注意:数据量可能很大,确保网络通畅和磁盘空间充足) GDCdownload(query, method = "api") # 准备数据:将下载的分散文件整合成一个R可用的数据对象 kirc.data <- GDCprepare(query)GDCdownload会开始下载数据。这里我用了method = "api",这是官方推荐的稳定方法。下载时间取决于数据量大小和网速,肾癌的数据大概有几个G,耐心等待即可。
下载完成后,GDCprepare这个函数是真正的“魔术师”。它会自动做一堆事:读取所有分散的样本文件,根据附带的元数据(JSON文件)将样本名转换成我们熟悉的TCGA患者条形码(如TCGA-3A-A9I0-01A),并将所有数据整合成一个大的SummarizedExperiment对象。这个对象非常规整,包含了表达矩阵、样本信息和基因注释,后续操作极其方便。
你可以用assay(kirc.data)来查看表达矩阵,用colData(kirc.data)查看样本分组信息(比如哪些是肿瘤组织,哪些是癌旁正常组织)。TCGAbiolinks已经帮我们根据样本编号的第14-15位(01-09通常是肿瘤,10-19通常是正常)做好了初步的样本类型标记,这为我们后续的分组分析省去了大量手动整理的麻烦。
3. 数据预处理:为edgeR分析准备“干净”的食材
从TCGAbiolinks拿到数据,并不代表可以直接扔给edgeR。就像做菜前要洗菜切菜一样,我们需要对数据进行一些预处理,确保输入edgeR的是它最喜欢“吃”的格式。
3.1 提取计数矩阵和样本分组
首先,我们从准备好的kirc.data对象中提取出核心的表达计数矩阵和样本信息。
# 1. 提取原始计数矩阵(Raw Counts Matrix) # 这是edgeR分析的基础,必须是整数计数 count_matrix <- assay(kirc.data, "HTSeq - Counts") # 查看一下矩阵的维度:行是基因,列是样本 dim(count_matrix) # 2. 提取样本信息,并创建分组因子 sample_info <- colData(kirc.data) # 利用TCGA样本编号规则创建分组:01-09为肿瘤(Tumor),10-19为正常(Normal) group_list <- ifelse(as.numeric(substr(sample_info$sample, 14, 15)) < 10, "Tumor", "Normal") # 将分组转换为因子,并设定参考水平(通常将“Normal”设为参考组) group_list <- factor(group_list, levels = c("Normal", "Tumor")) table(group_list) # 查看一下肿瘤和正常样本各有多少这一步至关重要。count_matrix是一个巨大的数字矩阵,行名是基因的Ensembl ID(比如ENSG00000141510),列名是TCGA样本ID。group_list是一个向量,指明了每一列样本属于“Tumor”还是“Normal”。edgeR后续的统计模型,就是基于这个分组来比较两组的基因表达差异。
3.2 基因ID转换:从Ensembl ID到Gene Symbol
你有没有发现,矩阵的行名是一串像“天书”一样的Ensembl ID?这对于我们人类来说非常不友好。我们更熟悉的是像TP53、EGFR这样的基因符号(Gene Symbol)。所以,我们需要进行ID转换。
TCGAbiolinks准备的数据对象通常已经包含了基因注释信息。我们可以很方便地提取:
# 提取基因注释信息 gene_info <- rowData(kirc.data) # 查看一下注释信息里有什么 head(gene_info) # 通常,我们需要的基因符号在‘external_gene_name’这一列 # 我们将Ensembl ID替换为Gene Symbol作为行名 # 注意:有些Ensembl ID可能对应同一个Gene Symbol(比如不同转录本),需要做去重或合并 # 这里我们采用一种简单策略:对于多个Ensembl ID对应同一个Gene Symbol,取表达量平均值 gene_symbols <- gene_info$external_gene_name rownames(count_matrix) <- gene_symbols # 处理重复的基因名:将重复基因名的计数取平均值 # 这是一个常见的步骤,可以避免后续分析因重复行名而出错 require(dplyr) count_matrix_agg <- count_matrix %>% as.data.frame() %>% tibble::rownames_to_column(var = "Gene") %>% group_by(Gene) %>% summarise(across(everything(), mean, na.rm = TRUE)) %>% tibble::column_to_rownames(var = "Gene") %>% as.matrix() # 现在,count_matrix_agg的行名就是清晰的Gene Symbol了当然,有时候提取的基因名可能有很多空值(NA)。我还有另一个常用的备选方案,就是使用biomaRt包在线查询,或者用org.Hs.eg.db这样的注释包进行转换。方法很多,核心目的就是得到一个行名为可读基因符号、无重复行的计数矩阵。
3.3 初步过滤:去掉低表达基因
在正式进行差异分析前,还有一个重要的预处理步骤:过滤低表达基因。那些在所有样本里表达量都极低甚至为零的基因,它们不包含有意义的生物学信息,却会增加多重检验的负担,影响分析结果的准确性。edgeR官方推荐进行过滤。
# 创建一个DGEList对象,这是edgeR的标准数据容器 dge <- DGEList(counts = count_matrix_agg, group = group_list) # 计算每个基因的CPM值(每百万计数) cpm <- cpm(dge) # 设定一个过滤阈值:要求一个基因至少在部分样本中有一定表达量 # 例如:要求基因在至少20%的样本中,其CPM值大于1 keep <- rowSums(cpm > 1) >= 0.2 * ncol(dge) dge <- dge[keep, ] # 查看过滤掉了多少基因 table(keep)过滤后,dge对象里就只剩下那些“有声音”的基因了。我们还会重新计算库大小(library size),因为过滤基因后,每个样本的总计数会发生变化。
# 过滤后,重新计算库大小 dge$samples$lib.size <- colSums(dge$counts)至此,数据预处理就完成了。我们得到了一个干净的、行名为基因符号的计数矩阵dge,以及明确的分组信息group_list。食材已经备好,接下来可以下锅烹饪了。
4. 核心战役:使用edgeR进行差异表达分析
终于来到最激动人心的环节了!我们将使用edgeR来找出在肾癌组织和正常组织之间表达水平显著不同的基因。edgeR的分析流程像一条精心设计的流水线,每一步都有其明确的目的。
4.1 标准化与离散度估计
为什么需要标准化?因为不同样本的测序深度(总读数)可能差异很大。如果不进行标准化,一个在肿瘤样本中表达量高的基因,可能仅仅是因为那个样本测得更深,而不是真的上调了。edgeR使用TMM(Trimmed Mean of M-values)方法进行标准化,这是一种非常稳健的方法,能有效消除样本间测序深度的差异。
# 1. 计算标准化因子(TMM) dge <- calcNormFactors(dge) # 查看标准化因子,数值在1附近波动表示深度差异不大,偏离1则表示需要较大调整 dge$samples$norm.factors接下来是edgeR的精华部分:估计离散度。RNA-seq计数数据存在一个特点,就是方差往往大于均值,这被称为“过度离散”。edgeR使用负二项分布来模拟这种数据,而离散度参数(dispersion)就是这个分布的关键。我们需要估计三个层次的离散度:
- 共同离散度(Common dispersion):假设所有基因的离散度相同。
- 趋势离散度(Trended dispersion):认为离散度与基因的表达水平(均值)存在趋势关系。
- 基因特异性离散度(Tagwise dispersion):为每个基因估计一个独立的离散度,这是最精细的。
# 2. 创建设计矩阵,告诉模型我们的分组情况 design <- model.matrix(~0 + group_list) rownames(design) <- colnames(dge) colnames(design) <- levels(group_list) # 列名为“Normal”和“Tumor” # 3. 估计离散度(三步曲) dge <- estimateGLMCommonDisp(dge, design) dge <- estimateGLMTrendedDisp(dge, design) dge <- estimateGLMTagwiseDisp(dge, design) # 可以绘制一个BCV图(生物学变异系数图)来直观查看离散度估计 plotBCV(dge, main = "Biological Coefficient of Variation (BCV) Plot")BCV图能帮你判断离散度估计是否合理。通常,趋势线会随着平均表达量的增加而缓慢下降。如果图形看起来很奇怪,可能需要回头检查数据质量。
4.2 拟合模型与差异检验
离散度估计好后,我们就可以用广义线性模型(GLM)来拟合数据,并进行假设检验了。GLM比edgeR早期使用的精确检验(exact test)更灵活,尤其是在设计复杂实验(比如多组比较、有协变量)时优势明显。
# 4. 拟合GLM模型 fit <- glmFit(dge, design) # 5. 构建对比矩阵并进行似然比检验(LRT) # 我们想比较的是 Tumor vs Normal,所以对比向量是 c(-1, 1) # 意思是:-1 * Normal + 1 * Tumor,即 Tumor - Normal contrast <- makeContrasts(contrasts = "Tumor-Normal", levels = design) lrt <- glmLRT(fit, contrast = contrast) # 6. 提取所有基因的检验结果 DEGs_all <- topTags(lrt, n = nrow(dge$counts), adjust.method = "BH", sort.by = "PValue") DEGs_all <- as.data.frame(DEGs_all)DEGs_all这个数据框里,就包含了每一个基因的检验结果,最重要的几列是:
logFC:对数倍数变化。正值表示在肿瘤中上调(表达更高),负值表示下调。例如,logFC=2意味着表达量在肿瘤中是正常的4倍(因为2^2=4)。PValue:原始P值。FDR或adj.P.Val:经过错误发现率(如Benjamini-Hochberg方法)校正后的P值。这是更严格的指标,用于控制假阳性。通常我们以FDR < 0.05作为差异显著性的阈值。
4.3 筛选显著差异基因与结果解读
拿到了所有基因的统计结果,我们需要根据阈值筛选出那些我们真正关心的、显著差异表达的基因。
# 设定显著性阈值 fdr_cutoff <- 0.05 logfc_cutoff <- 1 # 通常认为 |logFC| > 1 有生物学意义(即表达量翻倍或减半) # 筛选差异表达基因 DEGs_sig <- subset(DEGs_all, FDR < fdr_cutoff & abs(logFC) > logfc_cutoff) # 添加上下调标签 DEGs_sig$change <- ifelse(DEGs_sig$logFC > 0, "UP", "DOWN") # 查看上下调基因的数量 table(DEGs_sig$change) # 将结果保存到CSV文件,方便后续使用和分享 write.csv(DEGs_sig, file = "TCGA-KIRC_DEGs_edgeR_FDR0.05_logFC1.csv", row.names = TRUE)现在,你手里就有一份肾透明细胞癌的差异表达基因列表了。你可以根据logFC从大到小排序,看看哪些基因上调最猛烈;也可以关注那些FDR极小、logFC也显著的基因,它们可能是驱动癌症发生的关键分子。
但故事还没完。一份成百上千个基因的列表,如何理解其背后的生物学意义?这就需要用到功能富集分析了。你可以把上调基因列表和下调基因列表分别提出来,用clusterProfiler包去做GO(基因本体论)或KEGG(京都基因与基因组百科全书)通路富集分析,看看这些差异基因主要富集在哪些生物学过程、细胞组分或信号通路上。比如,你可能会发现上调基因显著富集在“细胞周期”、“DNA复制”通路,而下调基因富集在“肾小管发育”、“离子转运”通路,这非常符合癌症的特征:增殖失控、功能丧失。
5. 避坑指南与高级技巧:来自实战的经验之谈
走通了整个流程,恭喜你!但作为过来人,我知道在实际操作中你肯定会遇到各种各样的问题。这里我分享几个最常见的“坑”和应对技巧,希望能帮你节省大量调试时间。
坑一:下载速度慢或失败。GDC的服务器在国外,网络不稳定是常态。TCGAbiolinks的GDCdownload函数提供了method = "api"和method = "client"两种方式。api方式更稳定,但速度可能慢。如果文件很大,可以尝试先小批量下载测试。另外,确保你的工作目录路径没有中文或特殊字符。
坑二:内存不足。TCGA的表达矩阵很大,尤其是全部样本一起分析时。如果你的R会话崩溃了,可以尝试:
- 在数据预处理阶段就进行更严格的低表达基因过滤(提高CPM阈值)。
- 分析时关闭不必要的软件,给R分配更多内存(在RStudio的Tools -> Global Options -> General里可以设置)。
- 考虑使用
DelayedArray或HDF5Array包来处理超大型矩阵,它们可以将数据放在硬盘上而不是全部读入内存。
坑三:分组信息错误。这是导致结果出错的“隐形杀手”。一定要反复确认group_list的生成是否正确。除了用样本编号的第14-15位判断,更严谨的做法是结合colData(kirc.data)里的sample_type或definition字段进行双重校验。我曾经就遇到过因为样本类型注释更新,导致用老规则分组出错的情况。
坑四:ID转换丢失或重复。用Gene Symbol替换Ensembl ID时,经常出现多个Ensembl ID对应一个Symbol(比如同一个基因的不同转录本),或者某些Ensembl ID没有对应的Symbol(NA)。对于重复问题,取平均值或最大值合并是常用策略。对于NA问题,可以先保留这些基因的Ensembl ID,或者用其他数据库(如AnnotationDbi)进行补充查询,不要简单地删除,以免丢失重要信息。
高级技巧:加入临床信息进行亚组分析。TCGAbiolinks下载的数据中包含了丰富的临床信息。你完全可以将差异分析与临床特征结合起来。例如,不是简单比较肿瘤vs正常,而是比较“高分期肿瘤 vs 低分期肿瘤”,或者“有转移 vs 无转移”。只需要在创建group_list和design矩阵时,利用临床数据重新定义分组即可。这能让你的分析更深入,更贴近临床问题。
最后,记住生物信息学分析的核心是可重复性。建议你把整个分析流程(从数据下载到出图)写成一个R Markdown文档或Jupyter Notebook。这样,不仅你自己以后可以一键复现,分享给同行时也显得非常专业和可靠。这套基于TCGAbiolinks和edgeR的流程,我已经封装成了好几个项目模板,每次新课题都能快速上手,效率提升的不是一点半点。希望它也能成为你手中的利器,在癌症数据的海洋里,发现属于你的那颗明珠。