ARTICLE DETAIL

资讯详情

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

R语言提取GEO数据全流程:从GSE编号到表达矩阵与ID转换

R语言提取GEO数据全流程:从GSE编号到表达矩阵与ID转换 简介面向生物信息学研究者的R语言GEO数据提取源码包解决从基因表达综合数据库批量下载GSE芯片数据、提取表达矩阵与临床信息等问题。资源涵盖GEOquery、AnnoProbe等主流R包的程序化下载方式也兼顾直接网页获取的流程并对多GPL平台数据、列名错位等实操难点给出处理思路。资源共7个文件以R脚本、Markdown说明文档为主另有HTML预览与配置文件压缩包约12KB。R脚本覆盖依赖安装、数据下载、表达矩阵与临床信息提取、CSV导出等完整流程说明文档便于快速上手。目前已有247人学习适合需要系统处理GEO数据的生信入门与进阶用户。 干生物信息分析这几年我被身边朋友问得最多的一个问题不是某个算法怎么调参而是“R语言到底怎么把GEO数据提取出来”。问的人往往已经有了一个GSE编号也去GEO页面看过但一打开网页就懵了一堆GSM、GPL、Supplementary文件到底下载哪个下载回来又怎么读进R这篇文章我打算把R语言提取GEO数据的完整链路讲清楚从理解GEO的数据组织方式到用GEOquery包拿到表达矩阵再到探针ID转基因Symbol、数据清洗和标准化全程给出可以直接复制的源码同时把我在实际项目中踩过的坑一并讲出来。适合刚接触生信、准备用公共数据做分析的R使用者也适合已经会点GEO2R、但想更自主控制数据处理的同学。1. 先搞懂GEO的数据结构再谈提取很多人的问题其实不是不会写代码而是不知道自己要提取什么。GEO数据下载回来是一堆文件但真正进入分析流程时你需要关心的是其中三类信息表达矩阵、样本信息和平台注释。先把这三样东西搞明白后续所有代码都围绕它们展开。1.1 从GSE到GSM到GPL这些编号到底怎么排列GEO全称Gene Expression Omnibus是NCBI下的公共基因表达数据库里面既有芯片数据也有测序数据。平时我们看到的编号有四种第一眼看上去像乱码但理清楚后非常简单编号前缀含义说明GDSGEO DataSet已经处理好的数据集合新数据里已经很少见到GSEGEO Series一个研究项目包含多个样本是最常用的分析对象GSMGEO Sample单个样本的检测结果GPLGEO Platform芯片或测序平台决定了探针ID的体系举个容易理解的例子GSE相当于一个课题组的整批实验结果里面包含许多GSM每个GSM就是一例样本而这一批样本是在哪种芯片上测的由GPL决定。GSE是文章里最常见的数据编号也是我们提取数据时的主要入口。有一个非常容易忽略的点一个GSE可能对应多个GPL。比如有的研究既做了mRNA芯片又做了miRNA芯片或者一部分样本用了U133A、一部分用了U133 Plus 2.0那么getGEO返回的结果就不是一个对象而是一个列表每个平台对应一个元素。后面我会专门讲这种情况怎么处理。1.2 我们提取GEO数据最终要拿到哪三样东西做下游分析的时候最核心的数据就三类缺一不可。表达矩阵行是探针或基因列是样本里面的值是表达量。差异分析、聚类、热图全靠它。样本信息也叫临床信息每个样本对应的分组、组织类型、处理条件等。没有它连实验组和对照组都分不出来。平台注释探针ID和基因Symbol的对应关系。芯片数据的行名通常是探针ID不转成基因名后面做生物学解释和富集分析根本没法继续。在R的GEOquery包里这三样东西分别通过exprs()、pData()和fData()三个函数取出来。我最初接触的时候也绕了一圈以为下载完数据就结束了实际上下载只是开始后面的数据整理才是真正花时间的部分。2. GEOquery包安装与数据下载镜像、超时和缓存GEOquery是Bioconductor生态的包不在CRAN上很多人第一次装的时候就卡住了。安装思路正确之后还要处理网络下载的问题尤其是大文件下载时R的默认超时设置这个坑几乎每个人都会踩到。2.1 安装GEOquery的正确姿势直接install.packages(GEOquery)是装不上的因为Bioconductor的包有自己的仓库和安装方式标准做法是先安装BiocManager再用BiocManager::install()来装if (!requireNamespace(BiocManager, quietly TRUE)) install.packages(BiocManager) BiocManager::install(GEOquery)如果你在下载Bioconductor包时速度很慢或者经常下载到一半中断可以在安装前设置镜像:options(BioC_mirror https://mirrors.tuna.tsinghua.edu.cn/bioconductor) options(repos c(CRAN https://mirrors.tuna.tsinghua.edu.cn/CRAN/))这两行设置我每次配新环境都会先写上。很多人卡在安装这步并不是代码写错了而是包文件太大下载超时被中断。R的默认下载超时时间只有60秒遇到几百MB的包基本必断。2.2 getGEO的常用参数和下载缓存机制安装好GEOquery后提取数据最核心的命令就是getGEO。我日常项目里最常用的写法是这样的library(GEOquery) gse - getGEO( GSE42872, GSEMatrix TRUE, AnnotGPL TRUE, destdir ./GEO_data )逐个解释这几个参数的实际用途。GSEMatrix TRUE表示下载GEO官方处理好的表达矩阵而不是原始CEL文件或测序的fastq日常分析用这个就足够了。AnnotGPL TRUE是指在下载表达矩阵的同时把平台注释信息也一起获取这样后面fData()里就能直接看到探针ID和基因Symbol的对应关系省掉手动查注释表的功夫。destdir ./GEO_data是把下载的文件缓存到本地目录GSE数据动辄几百MB如果不缓存每次重新运行脚本都要从头下载一遍非常耽误时间。下载完成之后gse可能是ExpressionSet对象也可能是一个list建议用class(gse)和str(gse)确认一下结构再决定下一步怎么取数。3. 核心源码拆解从GSE编号到表达矩阵、样本信息和平台注释这一节把从GSE编号到写入本地CSV文件的完整代码写出来每一步都附上说明。代码不复杂但有几个细节如果没注意到很容易在运行时报奇怪的错误。3.1 一段完整的提取代码library(GEOquery) gse - getGEO(GSE42872, GSEMatrix TRUE, AnnotGPL TRUE, destdir ./GEO_data) # 处理返回类型如果gse是list取第一个平台的数据 if (is.list(gse)) { eset - gse[[1]] } else { eset - gse } # 表达矩阵 expr_df - as.data.frame(exprs(eset)) # 样本信息 sample_info - pData(eset) # 平台注释 platform_anno - fData(eset) # 写出到本地 write.csv(expr_df, expression_matrix.csv) write.csv(sample_info, sample_info.csv) write.csv(platform_anno, platform_annotation.csv)这里有个容易被忽略的细节exprs()返回的是一个矩阵我习惯先用as.data.frame转成数据框再写文件。如果不转写出的CSV里行名可能丢失导致后面读入时探针ID全部变成默认的1、2、3到时候想映射基因名都找不到入口。另外getGEO在下载大GSE时控制台长时间没有任何输出是正常的。不要急着中断网络正常的话多等几分钟。如果你发现它报错Timeout那就是我前面说的超时问题在代码开头加上options(timeout 600)再重新运行。3.2 数据对齐关系行名和列名对应逻辑拿到三个对象后要搞清楚它们之间的对齐关系这是理解GEO数据结构的核心。表达矩阵的列名是GSM样本编号样本信息的行名也是GSM样本编号所以两者按行对齐。平台注释的行名是探针ID表达矩阵的行名也是探针ID所以fData和exprs按行对齐。简单说GEO数据结构就是一张大表嵌套一张小表外层是样本维度内层是探针维度。对齐的关键永远是行名和列名不要用数字索引去匹配那样很容易错位。如果后续要往表达矩阵里加样本分组列直接根据列名匹配sample_info中的对应信息就行。我通常会把样本信息的行名提取出来单独存一列防止后续排序后对应关系混乱。3.3 多个平台数据的处理策略前面提到GSE可能同时包含多个GPL平台。这种情况下gse是listexprs(gse)直接运行会报错需要先判断平台。我一般用一段循环快速查看每个元素的情况for (i in seq_along(gse)) { cat(i, annotation(gse[[i]]), ncol(gse[[i]]), \n) }annotation()返回的是平台编号ncol()是该平台下的样本数量。然后根据样本量是否合理选择对应的元素继续分析。一般来说一个研究中主要分析平台通常是样本量最大的那个但也不绝对还是要结合课题需要来选。4. 探针ID转基因Symbol的三种路径与去重合并芯片数据的表达矩阵行名默认是探针ID比如1007_s_at这种生物学意义不直观。画热图时想标基因名做富集分析时想用基因Symbol或Entrez ID都得先做ID转换。这是整个流程中最容易出错的环节也是最值得花时间搞清楚的部分。4.1 为什么要做ID转换从生物学意义上来说探针是芯片上的一段核苷酸序列一个基因可能被多个探针覆盖一个探针也可能同时匹配多个基因。直接用探针ID做差异分析不是不行但结果无法和文献、数据库里的基因名称对应起来后续富集分析也无从下手。所以拿到表达矩阵后第一步就是把探针ID换成基因级别的标识。4.2 从平台注释列直接映射最简单的方式是用平台注释文件。如果你下载时设置了AnnotGPL TRUEfData()里通常已经包含了Gene Symbol、Gene title、Entrez Gene ID等列。但不同GPL平台的注释列名非常不统一有的叫Gene Symbol有的叫gene_assignment后者是GEO官方注释里的复合字段格式很长需要自己切分。比如gene_assignment这一列经常长这样NM_005343 // HRAS // 3265 // 11p15.5 // 6407 // chr11 // 这时候用竖线分隔取第2个字段作为基因Symbolanno - fData(eset) symbol_vec - sapply(strsplit(anno$gene_assignment, // ), function(x) x[2])建议拿到fData后先执行colnames()打印所有列名看清楚有哪些可用字段再动手不要凭经验猜列名。不同GPL的数据列名差异比我预想的大得多。4.3 用org.Hs.eg.db做通用映射如果平台注释文件没有覆盖到你要的内容或者你想用统一的ID映射标准可以用Bioconductor的org.Hs.eg.dbBiocManager::install(org.Hs.eg.db) library(org.Hs.eg.db) probe_ids - rownames(expr_df) gene_symbols - mapIds( org.Hs.eg.db, keys probe_ids, keytype PROBEID, column SYMBOL )这里的keytype要根据平台ID来源来定。Affymetrix平台的探针ID对应PROBEIDIllumina平台可能是ILLUMINA_IDAgilent平台可能是AGILENT_ID。如果mapIds返回大量NA先检查一下keytype是否选对再考虑换用GPL注释文件。另外有些自定义芯片的探针ID在org.Hs.eg.db里根本查不到这时候平台注释文件反而是唯一可用的来源。4.4 一对多与多对一的处理思路ID转换最常见的坑就是探针和基因不是一一对应。一个探针对应多个基因说明这个探针本身特异性不够稳妥做法是删除该探针或者保留表达量最高的基因。多个探针对应一个基因则更常见做下游分析前需要合并表达量。我通常的做法是按基因分组后取中位数而不是取平均值因为中位数对个别异常探针不敏感。expr_df$GeneSymbol - gene_symbols[match(rownames(expr_df), names(gene_symbols))] expr_df - expr_df[!is.na(expr_df$GeneSymbol) expr_df$GeneSymbol ! , ] expr_agg - aggregate(. ~ GeneSymbol, data expr_df, FUN median) rownames(expr_agg) - expr_agg$GeneSymbol expr_agg$GeneSymbol - NULLaggregate之后行名就变成了基因Symbol表达矩阵变成了基因级别的矩阵。这一步做完才算真正具备做下游分析的基础。5. 数据清洗与标准化矩阵到手不能直接拿去分析很多人拿到表达矩阵后就直接跑差异分析结果跑出来一堆奇怪的结果最后才发现问题出在数据本身。GEO上下载的表达矩阵有的已经标准化过了有的还是原始信号值直接分析会引入系统性偏差。5.1 判断是否需要log2转换一个简单的经验判断方法如果表达矩阵里的数值普遍在几百到几万说明是线性信号强度通常需要log2转换如果数值普遍在个位数到十几之间说明已经log2过不要再重复转换。先看范围range(expr_df, na.rm TRUE)如果确认需要转换执行expr_df - log2(expr_df 1)这里加1的目的是防止值为0时取对数出现负无穷。这种做法在芯片数据里非常常见也适用于部分counts数据但如果是RNA-seq的原始counts后续最好使用专门的标准化方法比如DESeq2的variance stabilizing transformation而不是简单log2。5.2 缺失值和重复基因的处理探针级别的表达矩阵偶尔会出现缺失值处理方式我倾向于直接删除该行而不是填充0。填充0等于向数据分析流程里引入了“该基因不表达”的假设可能会对差异分析产生误导。如果缺失比例特别低比如小于1%也可以考虑用该行其他样本的中位数填充。具体怎么选要看数据量但原则是不要盲目填0。重复基因名的处理在第4章已经讲过了aggregate取中位数合并即可。这里要提醒一下合并前一定要把GeneSymbol为NA的空行删掉否则聚合时会多出一个NA组后面分析时这一组数据会干扰结果。5.3 样本分组信息的整理样本信息表里分组信息通常藏在characteristics_ch1列比如有的数据集是tissue: tumor和tissue: normal有的是disease state: control和disease state: AD格式因数据集而异。整理分组的常用办法是提取该列中的有效标签group - gsub(tissue: , , sample_info$characteristics_ch1) group - factor(group, levels c(normal, tumor))这里建议在项目一开始就统一分组命名规范不要改来改去。我吃过这个亏第一次跑limma时分组是“normal”和“tumor”第二次换数据后变成“Normal”和“Tumor”结果大小写不一致导致设计矩阵构建错误排查了很久才发现是分组标签的问题。整理好之后把表达矩阵、样本信息和分组信息保存成RDS后续分析直接加载不用每次重新下载重跑saveRDS(list(expr expr_agg, sample_info sample_info, group group), file GSE42872_clean.rds)6. 实测中的坑与GEO2R交叉验证代码写对只是第一步实际跑数据时遇到的问题远比想象的复杂。下面这些都是我亲测踩过的坑写出来供参考。6.1 下载大文件超时的三个信号和处理方案第一次下载大型GSE时控制台突然报错Timeout of 60 seconds was reached。这是R默认下载超时导致的。遇到这种情况先检查网络是否正常然后在代码开头设置options(timeout 600)把超时时间拉长到10分钟。如果还是失败检查destdir是否设置让下载文件能断点缓存。实在不行可以在浏览器里手动从GEO页面下载GSE的series matrix文件然后通过本地文件加载gse - getGEO(filename ./GSE42872_series_matrix.txt.gz)这种本地加载方式在服务器上不方便直接访问外网时非常好用相当于把下载和解析分离了。6.2 注释列和分组信息对不上的排查不同GPL平台的注释列差异很大我第一次处理GPL570时fData里面没有直接叫Gene Symbol的列只有一个gene_assignment复合字段花了不少时间才搞明白。建议拿到数据后先打印colnames(fData(eset))看清楚有哪些字段再动手不要凭之前的经验直接取列名。分组信息也是一样不同数据集的组织方式差异很大有的在characteristics_ch1有的在source_name_ch1保险起见把所有列名打印一遍。6.3 用GEO2R结果验证自己的R代码自己用R提取的数据和网页端GEO2R导出的结果对不上我从实践中总结的排查顺序是先看平台注释版本是否一致再查分组定义是否对应最后对照标准化方式。GEO2R默认会做一些数据处理它输出的表头也和直接下载的矩阵不完全一样。多数情况下不是代码写错了而是处理细节不一致。拿GEO2R的结果来做交叉验证很有价值但不能盲目认为网页端的结果就是标准答案理解它背后的处理逻辑更重要。下面这个表是我整理的最常见的几个问题与对应排查方向现象常见原因处理方式下载报错Timeout of 60 secondsR默认下载超时太短设置options(timeout 600)getGEO返回list导致exprs报错GSE包含多个平台用is.list判断后取指定元素注释列找不到Gene Symbol平台注释列名不统一打印colnames后按实际列名处理与GEO2R结果不一致分组定义或标准化方式不同对照数据源页面逐项排查6.4 单细胞GSE数据的特殊处理GEO上现在有大量单细胞数据集它们和普通芯片数据的提取方式不太一样。普通GSE用getGEO就能拿到表达矩阵但单细胞数据集很多时候提供的下载文件是10x格式的barcodes、features、matrix三个文件getGEO只能拿到metadata层面的信息真正的counts矩阵需要去GEO页面的Supplementary file区域下载解压后用Seurat或SingleCellExperiment包读入。如果你要处理单细胞数据拿到样本metadata之后要记得去附件区域找文件不要只盯着getGEO的返回值否则会以为数据缺失。把上面这些步骤完整走一遍一个新的GSE从下载到拿到干净矩阵我一般能控制在10分钟以内。整个过程最耗时间的往往不是写代码而是搞清楚这个数据集的分组信息藏在哪一列、平台注释用的什么格式。GEO数据本身就长这样不能指望所有数据集都按一个模板发布核心思路是把提取数据的流程固化成脚本遇到新数据集时先跑脚本打印结构再根据具体差异微调参数这样反复几次后处理公共数据的速度会快很多。本文还有配套的精品资源点击获取
返回列表