ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

GO/KEGG富集分析完全指南:clusterProfiler从ID转换到可视化

GO/KEGG富集分析完全指南:clusterProfiler从ID转换到可视化 做转录组、蛋白组或者单细胞数据分析的人几乎都绕不开这么一道工序差异分析做完手里攥着几百个显著基因下一步就要回答这些基因到底在干什么。这时候就需要把这一整串基因列表批量丢进GO和KEGG注释流程让它们自动归类到生物学过程和信号通路里方便你快速判断主要的功能方向。这篇文章就是一套可以直接抄作业的完整流程从数据库逻辑、工具选型、ID转换、富集分析、可视化到常见问题排查全部按实操路径拆开讲。适合刚接触生信、准备用R跑功能富集的新手也适合想把手动网页点击换成可复现流程的进阶用户。先提醒一句容易踩的坑这里说的GO是Gene Ontology的全大写缩写跟编程圈那个Go语言完全两码事。我当初第一次搜GO注释时搜索引擎全是Golang教程差点以为自己查错方向。确认清楚之后再开始搭流程。1. 动手之前先把两个数据库的逻辑吃透1.1 GO注释的三层结构以及它为什么会冗词那么多GO全称Gene Ontology本质上是一套标准化的功能词典。它把基因功能描述拆成三个维度生物学过程Biological ProcessBP、分子功能Molecular FunctionMF、细胞组分Cellular ComponentCC。BP回答这个基因参与什么过程比如细胞周期、炎症反应、DNA修复MF回答这个基因在分子层面干的是什么活比如结合ATP、催化磷酸化反应CC回答这个基因产物在哪里工作比如线粒体内膜、核仁、溶酶体。这三个维度不是简单的并列关系而是从宏观到微观的视角差异。GO内部用有向无环图DAG组织所有术语每个term可以有父项和子项子项比父项更具体。比如细胞周期调控下面还挂着G1/S期转换调控有丝分裂检查点等更细的子term。这种层级结构带来的直接后果是跑完富集分析你会得到一长串语义重叠的术语而且很多term级别高得吓人比如protein bindingmetabolic process。为什么会出现这种情况因为富集分析本质上是对每个term单独做统计检验term之间互相独立不会因为我已经报告了父项子项就不该出现。所以后期一定需要简化处理4.3节我会专门讲simplify的用法。1.2 KEGG通路的核心逻辑不是注释词典是基因导航图KEGGKyoto Encyclopedia of Genes and Genomes和GO完全不同它是以通路为中心的数据库。KEGG不关心这个基因属于什么功能类而是把基因放进代谢图、信号通路图里直接展示基因与基因之间的上下游关系。比如TCA循环、p53信号通路、自噬通路在KEGG里都有唯一编号像hsa00020、hsa04115、hsa04140。KEGG内部有一套KOKEGG Orthology体系每个KO对应一个保守的功能单元。通路图上的每个方框就是一个KO条目你的基因映射到KO之后再看这个KO落在哪些通路上。这里有个很实际的影响不同物种在KEGG里的通路覆盖度差异很大人类和小鼠这种模式物种覆盖很全但部分非模式植物、昆虫可能通路图不全注释结果自然少这不一定是代码问题。顺带解释一个常见困惑为什么KEGG富集结果经常比GO少很多因为GO本来就是一个注重全面覆盖的数据库几乎所有注释过的基因都能映射到至少一个term而KEGG只收录有明确通路关系的基因没进过任何通路的基因就会直接被跳过。两者结果数量差距大是正常现象不说明分析出错了。1.3 为什么GO和KEGG必须搭配使用GO覆盖面广但它给出的是一堆功能标签不直接告诉你通路上下游怎么衔接KEGG能画出具体的通路图但覆盖窄、注释量小。做项目报告的时候你既需要这些基因显著富集在免疫应答过程这种宏观结论也需要它们主要落在NF-κB信号通路里这种具体指向。拿我自己的经验举例有一批差异基因富集到GO的炎症反应term看起来方向很明确但KEGG一跑出来发现它们同时落在TNF signaling pathway和NF-kappa B signaling pathway两个通路上这才意识到炎症反应是由多条通路共同驱动的。如果只看GO很可能把结论写成基因参与炎症反应但有了KEGG就能进一步指出是通过TNF信号通路参与炎症反应这个信息量直接提升了一个档次。2. 工具选型和前期准备先想明白再写代码2.1 主流注释方案横向对比现在做GO/KEGG注释的主流方案分成四类放一起对比更直观。方案类型代表工具优点缺点网页工具DAVID、KOBAS、Metascape、g:Profiler零代码点几下出结果无法批量处理多组数据参数不透明结果不可复现R包clusterProfiler、topGO、gage生态完善结果对象统一可视化强可批量化需要一点R基础Python工具gseapyPython用户友好数据库和可视化生态比R弱一些本地脚本自己下载注释文件写超几何检验完全可控可离线开发成本高对多数人不划算如果只做一个比较组、就想着快速看一眼结果用Metascape或者DAVID完全没问题。但只要你有两个以上比较组或者要反复调整参数、出图用clusterProfiler是更理智的选择。我现在的习惯是网页工具拿来快速验证方向正式分析全部用R脚本跑保证每一步都能复现。2.2 按物种选对OrgDb注释包clusterProfiler做GO富集时基因注释信息来自OrgDb包。这是Bioconductor的一套注释体系把不同物种的基因ID映射关系、GO注释等整合在一个数据库里。选错物种是整个分析里最常见的错误之一我见过不止一次有人拿到人类样本的数据跑出来的term全是小鼠的就是因为加载了org.Mm.eg.db。常用物种对应的OrgDb包列表如下物种OrgDb包人org.Hs.eg.db小鼠org.Mm.eg.db大鼠org.Rn.eg.db斑马鱼org.Dr.eg.db果蝇org.Dm.eg.db线虫org.Ce.eg.db酿酒酵母org.Sc.sgd.db拟南芥org.At.tair.db水稻org.Os.eg.db鸡org.Gg.eg.db猪org.Ss.eg.db牛org.Bt.eg.db如果列表里找不到你的物种就需要去GO官网下载该物种的注释文件再用AnnotationForge包自己构建OrgDb过程比较折腾。非模式物种的另一个备选方案是直接用网页工具KOBAS它内置的物种范围广很多。2.3 基因ID转换整个流程里第一个也是最大的坑为什么要单独花一节讲ID转换因为不同数据库用不同ID体系。你手里的差异基因列表最常见的是gene symbol比如TP53、EGFRGO富集时需要把symbol映射到OrgDb内部的IDKEGG富集时则需要转换成Entrez Gene IDKEGG通路里大量以ncbi-geneid作为索引。直接拿symbol去跑enrichKEGG大概率得到的结果是匹配率极低甚至直接报错。正确做法是用clusterProfiler自带的bitr函数做转换library(clusterProfiler) library(org.Hs.eg.db) gene_symbols - c(TP53, EGFR, BRCA1, MYC, CDK2) bitr(gene_symbols, fromType SYMBOL, toType c(ENTREZID, ENSEMBL), OrgDb org.Hs.eg.db)bitr转换后有几点特别容易出问题一个symbol可能对应多个Entrez ID。这是历史注释版本导致的bitr会把所有结果都返回转换结果行数会比输入基因数多。处理方式是先duplicated检查一遍再看要不要保留所有ID。Ensembl ID如果带版本号比如ENSG00000141510.17需要先用sub或gsub把小数点及后面的版本号去掉。匹配率如果低于60%先别急着往下跑回头查一查是不是ID类型选错了或者基因列表里混了别的物种的ID。3. 完整实操用clusterProfiler跑通GO和KEGG富集3.1 环境安装与输入数据准备在R环境里执行以下命令安装所需包install.packages(BiocManager) BiocManager::install(c(clusterProfiler, org.Hs.eg.db, enrichplot, pathview, DOSE))如果网络比较慢可以在install.packages时指定国内镜像BiocManager也可以设置镜像参数这个基础问题不展开。实际操作时我习惯把流程写成脚本输入文件就是一列gene symbol。假设你有一份上调基因列表gene_symbols - readLines(up_genes.txt) head(gene_symbols)如果你是从DESeq2或limma的结果里筛基因一般是先按padj和log2FC过滤再取出基因列比如padj 0.05且|log2FC| 1。阈值根据项目情况调整没有绝对标准。3.2 GO富集分析核心代码和参数逐一拆解直接上完整流程library(clusterProfiler) library(org.Hs.eg.db) # 第一步ID转换 gene_entrez - bitr(gene_symbols, fromType SYMBOL, toType ENTREZID, OrgDb org.Hs.eg.db) # 第二步GO富集 ego - enrichGO( gene gene_entrez$ENTREZID, OrgDb org.Hs.eg.db, keyType ENTREZID, ont ALL, pAdjustMethod BH, pvalueCutoff 0.05, qvalueCutoff 0.2 ) # 第三步把Entrez ID转换回symbol方便阅读结果 ego_sym - setReadable(ego, OrgDb org.Hs.eg.db, keyType ENTREZID) head(as.data.frame(ego_sym))这里有几个参数需要展开说清楚。ont参数控制注释类别ALL表示同时输出BP、MF、CC三个维度如果你只想做BP改成ont BP就行。pvalueCutoff是P值的过滤阈值qvalueCutoff是q值的过滤阈值qvalue是Storey方法估计的FDR比p.adjust更严格一点两个都设置可以起到双重过滤的效果。pAdjustMethod默认是BH也就是Benjamini-Hochberg校正这是目前最常用的多重检验校正方法。这里还要特别提醒一个容易忽略的参数universe背景基因。enrichGO默认把所有在OrgDb中有注释的基因作为背景但做转录组差异分析时更严谨的做法是把你自己表达矩阵中检测到的基因经过表达量过滤后剩下来的那些作为背景。因为一个基因在你实验里压根没被测到它当然不可能成为显著差异基因把它留在背景里会稀释富集信号。背景基因选得不同富集结果会产生明显差异这是很多项目复现失败背后隐藏的原因之一。# 指定背景基因的写法 all_genes - readLines(all_detected_genes.txt) all_entrez - bitr(all_genes, fromType SYMBOL, toType ENTREZID, OrgDb org.Hs.eg.db) ego - enrichGO( gene gene_entrez$ENTREZID, universe all_entrez$ENTREZID, OrgDb org.Hs.eg.db, keyType ENTREZID, ont ALL, pAdjustMethod BH, pvalueCutoff 0.05, qvalueCutoff 0.2 )结果表怎么看这里列几个关键列的含义。ID和Description分别是GO编号和术语描述GeneRatio是富集到这个term的基因数占输入基因总数的比例比如10/150BgRatio是背景基因中注释到这个term的基因占背景总数的比例pvalue、p.adjust、qvalue是显著性指标geneID是富集到这个term的具体基因列表Count是富集到该term的基因数。富集因子可以用GeneRatio除以BgRatio估算比值越大说明这个term在输入基因里相对更集中是筛选时一个很有用的参考。3.3 KEGG富集分析注意物种缩写和keyTypeKEGG富集的核心代码非常简洁ekegg - enrichKEGG( gene gene_entrez$ENTREZID, organism hsa, keyType kegg, pvalueCutoff 0.05, qvalueCutoff 0.2 )这里的organism是KEGG的物种三字母缩写也是出问题最多的地方。人类是hsa小鼠是mmu大鼠是rno斑马鱼是dre果蝇是dme线虫是cel酿酒酵母是sce大肠杆菌是eco。很多人会把小鼠写成mus大鼠写成rat这些都不对KEGG官网的organism页面可以查到正式的缩写列表不确定时先查再跑。keyType参数默认就是kegg大多数情况下用Entrez Gene ID传入并保持默认即可。如果你输入的是其他类型的ID需要先通过bitr转换到Entrez ID再跑这一点和GO一致。另一个实际问题是网络。enrichKEGG会请求KEGG的在线REST API一旦网络环境不给力就会报connection error或者404。遇到这种情况第一步是确认organism缩写是不是写错了缩写错误会直接导致请求不存在的物种页面第二步是换网络重试。如果长期离线就得考虑备选方案比如用KEGGREST包把目标物种的通路基因集下载到本地再自己写超几何检验或者直接用下载好的通路基因集文件做静态富集。这个方案麻烦一点但对复现性要求高的项目很值得。3.4 结果可视化气泡图、条形图、网络图和通路图富集分析不配图说不过去这节给出几个最常用的出图方案。library(enrichplot) # 气泡图最推荐 dotplot(ego, showCategory 15) # 按GO类别分面的条形图 barplot(ego, showCategory 10, split ONTOLOGY) facet_grid(ONTOLOGY ~ ., scale free) # 基因-通路网络图 cnetplot(ego, showCategory 5) # 通路间相似性网络图 emapplot(pairwise_termsim(ego))dotplot是首选方案横轴是GeneRatio纵轴是term点的大小映射Count颜色映射p.adjust一眼就能看到最显著的term。cnetplot展示的是基因和通路之间的关系网络能直观看到哪些基因同时出现在多条通路上这种基因往往是关键节点。emapplot展示term之间的重叠关系重叠多的term在功能上通常高度相关适合用来发现功能模块。KEGG通路图的绘制要单独讲因为它需要表达变化信息才有意义library(pathview) # 假设deg是差异分析结果表包含log2FoldChange和entrez_id两列 logFC - setNames(deg$log2FoldChange, deg$entrez_id) pathview(gene.data logFC, pathway.id hsa04110, species hsa, limit list(gene 5, cpd 1))pathview会把你的基因标到KEGG通路图上默认红色代表上调、绿色代表下调。注意gene.data是一个有名字的数值向量名字是Entrez ID值是log2FC或统计量。如果你手里只有基因列表、没有表达变化值也能出图但所有基因都是同一个颜色看不出上下调趋势信息量会差很多。所以我每次做KEGG可视化之前都会先把差异表达结果和Entrez ID对应好宁可在这一步多花十分钟。4. 常见问题与排查技巧实录4.1 ID映射失败、匹配率低该怎么定位这个问题在所有实操里出现频率最高。常见现象是bitr转换后可用基因只剩一半或者enrichGO直接报no gene can be mapped。我建议按以下顺序排查确认fromType是否写对了。SYMBOL、ENSEMBL、ENTREZID这些拼写都有固定写法大小写和缩写都不容出错。确认物种是否一致。人的symbol配org.Mm.eg.db就会出这种完全映射不上的诡异结果因为两个物种的symbol体系虽然相似但不等同。检查基因类型。基因列表里如果有非编码RNA、未注释的预测基因或者是线粒体、叶绿体编码基因在标准OrgDb里可能没有对应条目。检查Ensembl ID是否带版本号。带版本号的ENSG开头的ID要先去版本号再转换。最后看看是不是symbol太旧。基因组注释更新后部分旧symbol被废弃或改了名字可以用alias2Symbol或者org.Hs.egSYMBOL2ALIAS这类映射查一下。排查思路是要有层次的先怀疑ID类型再怀疑物种再怀疑基因类型最后才想到注释版本。不要一上来就怀疑自己的数据有问题。4.2 enrichKEGG连不上KEGG API怎么办在线请求失败时报错信息通常有两类。一类是连接超时、connection error这类多半是网络问题换网络或者换个时间段重试就行。另一类是HTTP 404基本可以断定organism缩写写错了KEGG按照你给的缩写找不到对应的物种页面。如果网络条件长期不行又必须跑KEGG备选方案是用KEGGREST手动抓数据library(KEGGREST) hsa_pathways - keggList(pathway, hsa) # 取出通路对应的基因集组织成list之后再自己写超几何检验这个思路是把目标物种的所有通路基因集下载到本地保存成RData或者文本文件以后每次都离线读取再配合自己的超几何检验代码跑富集。好处是完全离线、可复现坏处是需要自己处理基因集文件格式代码量会增加。对普通项目来说这个投入不划算但如果是需要长期在线析、复现性要求高的项目早点把本地基因集建好是值得的。4.3 富集结果全是protein binding这种大而空的term怎么办只要跑过几次富集你就一定会遇到这种现象p.adjust都很显著但看Description全是一些涵盖面极广的词比如protein binding、metabolic process、cellular process。这其实是超几何检验的天然缺陷——term本身包含的基因数越多即使随机扰动也容易累积出显著结果。处理手段有几个按推荐顺序排列# 1. 语义相似性简化合并冗余term ego_simple - simplify(ego, cutoff 0.7, by p.adjust, select_fun min)simplify的原理是基于GO term之间的语义相似性把相似度超过阈值的term合并只保留p.adjust最小的那一个。cutoff一般取0.7这个值可以在0.5到0.9之间调调得越大合并越激进保留的term越少。第二个思路是设置更严格的阈值比如pvalueCutoff降到0.01或者把Count的过滤条件加上比如要求至少富集到5个基因才保留。这样能过滤掉很多靠单个基因撑起来的term。第三个思路是换GSEA。GSEA不需要设定基因列表的硬阈值它利用基因在整个表达谱上的排序信息一般是log2FC降序排列能在通路层面上给出更敏感的富集判断还能避免只看显著基因导致的信息丢失。clusterProfiler里gseGO和gseKEGG直接可用代码结构也很类似这里不展开。4.4 结果表那么长怎么筛选和解读才不跑偏跑完得到几百行term很正常关键是怎么从里面提炼出对项目有用的信息。我自己的习惯是四步走。第一步先按p.adjust升序排序过滤p.adjust 0.05且Count 5。第二步算一个简单富集因子也就是GeneRatio除以BgRatio重点关注那些富集因子高、但又不是因为term本身包含几千个基因而显著的项目。第三步用dotplot把top 15拉出来看人工检查一次有些term虽然统计显著但跟你研究的背景完全无关比如做肿瘤研究时出现一堆嗅觉受体活性这种要么是背景基因污染要么是数据来源有问题需要注意。第四步把保留的term手动归类写成几组功能描述报告里就是主要富集在炎症应答、TNF信号通路、细胞凋亡调控这类结论。这里列一个常见问题速查表方便以后快速定位现象可能原因解决办法bitr匹配率低于60%ID类型或物种选错重新核对fromType和OrgDbenrichGO报no gene can be mappedEntrez ID格式不对或keyType设错检查转换结果确认keyTypeenrichKEGG报错网络问题或organism缩写错误重试、换网络、查KEGG官网缩写富集term大而空GO层级过高、term本身基因数多simplify合并、调严阈值、换GSEA两次分析结果不一致包版本或数据库更新固定包版本记录分析sessionInfo结果里全是同一类termont参数设置问题检查ontALL或按ONTOLOGY拆分看再说一个容易被忽略但很重要的点在论文或者报告里放富集结果时一定要记录精确的软件版本和数据库版本。clusterProfiler升级一个版本、OrgDb更新一次注释跑出来的结果就有细微差别。把每次分析的sessionInfo()保存下来是对自己项目负责的基本操作。从我自己的实操经验来说富集分析这套流程真正需要花心思的不是代码本身而是每一步决策背后对数据库逻辑的理解。ID转换为什么必须做、背景基因怎么选、为什么term需要简化这些想清楚了剩下的事情就是执行。分析结果最终还是要回到你的生物学问题里验证富集出来的通路要能和实验现象呼应上这个结果才有真正的说服力。
返回列表