芯片数据分析步骤5 过滤探针

匿名 (未验证) 提交于 2019-12-03 00:18:01

表达谱芯片上的探针往往能够覆盖到所有人类基因,也就是说,能够同时检测所有人类基因的表达。但先前的实验表明,一个细胞中不可能所有基因都同时表达,能够同时表达的基因反而是少数。同时表达的基因约占总基因的40%左右。

由于探针与目标之间一定存在着非特异性结合,所以所有的探针均会产生信号。如果不加以过滤,认为这些探针对应的基因都表达,即不符合事实,也会对后续的分析产生影响。因此,过滤掉表达量低的探针是十分必要的。

注意,limma包的说明里面提供了两点建议。一,如果要进行探针过滤(filter),最好在进行标准化之后再过滤。二,如果要在后续分析中使用limma包,请不要进行基于方差(variance)的过滤,否则会影响方差分布,从而导致limma包处理产生糟糕的结果(poor results)。

bioconductor提供了两种过滤探针的方法,一种是使用专门进行探针过滤的genefilter包进行过滤,另一种是使用affy包中的mas5calls函数进行探针过滤。下面会详细解释如何进行探针过滤。

根据genefilter包的技术文档介绍,genefilter包设计的出发点是进行independent filtering。我们知道,检测差异基因表达是表达谱芯片最重要的功能之一。而检测差异基因表达的方法是进行对每一个基因进行统计检验。

所谓的independent filtering,意思是在进行统计检验之前,筛掉那些不能或是很可能不能通过显著性检验的探针。independent filtering不依赖于统计检验,在保持一类错误率(即假阳性率)不变的条件下,能够增加检验效能。

一个好的filtering criterion拥有下面三个特点:

1、在零假设的条件下,独立于统计检验

2、在备择假设的条件下,与统计检验相关联

3、在联合检验中不显著改变依赖结构

genefilter包中有两个常用的函数能够进行探针过滤,一个是genefilter函数,另一个是nsFilter函数。

使用genefilter函数进行探针过滤

genefilter函数需要用户指定筛选检验方式。用法如下。

genefilter(expr, flist) 

expr是表达矩阵,需要用expr(ExpressionSet)expr(AffyBatch)对ExpressionSet或AffyBatch进行提取。flist是包含检验方法的list,可以同时包含多个检验方法,同时进行多个检验,取交集。

举个例子。例子来源于genefilter包的技术支持文档。sample.ExpressionSet来自Biobase包。

library("Biobase")  library("genefilter") data(sample.ExpressionSet) f1 <- kOverA(5, 200) ffun <- filterfun(f1) wh1 <- genefilter(exprs(sample.ExpressionSet), ffun) sum(wh1) [1] 159 

f1函数的意思是指筛选出至少在5个样本中表达量超过200的基因。最终筛选到了159个基因。

再给一个例子。

f2 <- ttest(sample.ExpressionSet$type, p=0.1) wh2 <- genefilter(exprs(sample.ExpressionSet), filterfun(f2)) sum(wh2) [1] 88 

f2函数的意思是进行 t.test,显著性水平 p=0.1,筛选出 t.test 中 p < 0.1 的基因,最终筛选到88个基因。

将两个筛选方法合并。

ffun_combined <- filterfun(f1, f2) wh3 <- genefilter(exprs(sample.ExpressionSet), ffun_combined) sum(wh3) [1] 35 

满足f1和f2的基因只有35个。

过滤探针

sample.filter <- sample.ExpressionSet[wh3,]  nrows(sample.filter) [1] 35 

用户可以按照上述例子的方式,用genefilter包内整合的检验方法,构造自己的检验标准进行探针过滤。详情请见bioconductor网页的genefilter包技术支持文档

使用nsFilter函数进行探针过滤

虽然使用genefilter函数能够自由地指定筛选标准,但很多时候我们并不需要对筛选标准进行改变,,而是希望使用同一套标准,或是懒得去调参数。

nsFilter函数提供了一站式的探针过滤,能够一步对ExpressionSet的探针进行过滤,返回一个listlist中的eset为过滤后的ExpressionSetfilter.log为每一步过滤到多少探针的记录。nsFilter函数也可以用于oligo包读取的对象。

使用方法如下。

nsFilter(eset, require.entrez=TRUE, remove.dupEntrez=TRUE, var.func=IQR, var.cutoff=0.5) 

一般只对以下几个参数进行调整。

参数 注释
require.entrez 若为TRUE,过滤掉没有ENTREZ ID的探针。默认为TRUE。
remove.dupEntrez 若为TRUE,当几个探针对应统同一ENTREZ ID的时候,留下 var.func 值最大的探针,其余过滤。默认为TRUE。
var.func 用于过滤的统计参数。默认为IQR。
var.cutoff 截断值。默认为0.5,即过滤到50%的基因。

这里要说明一下。由于探针过滤只能对 ExpressionSet 进行,不能对 AffyBatch 进行,因此要先进行标准化,再过滤。默认 var.cutoff = 0.5, 即过滤掉50%的探针。

nsFilter 函数还提供了合并探针的功能,不过只针对 EntrezID。因此,如果要使用 nsFilter 函数提供的合并探针功能,就必须先对探针进行EntrezID注释。如果不使用 nsFilter 函数的合并探针功能,那先注释或后注释甚至不注释都没关系。

我喜欢用 gene symbol 注释探针而不喜欢用 EntrezID,因此也就不使用 nsFilter 函数合并重复探针的功能。而且,nsFilter函数只能对ExpressionSet对象进行,而我们一般不会对ExpressionSet对象进行注释,所以合并重复探针的功能用不着。我一般习惯使用如下代码。

eset.filter <- nsFilter(eset, require.entrez=F, remove.dupEntrez=F) #不使用函数的合并探针功能  eset.filter$filter.log #查看每一步筛掉多少探针  eset.filter <- eset.filter$eset #只留下ExpressionSet对象 

这样就能得到过滤掉低表达探针后的ExpressionSet了。

如果想要使用nsFilter函数的合并探针功能,需设置require.entrez=TRUE, remove.dupEntrez=TRUE 然后设置var.func 为自己想要用来合并探针的统计参数即可,默认为 IQR。

mas5calls 函数整合在 affy 包中。 mas5calls 函数的算法是 Wilcoxon signed rank-based gene expression presence/absence detection(不翻译反而意思更清楚)。这个算法整合与 mas5 算法中。这个函数的功能是对 AffyBatch 的探针状态进行评估,分类为”P”,”M”和”A”。我们可以只提取其中分类为”P”的探针,认为其他探针不表达;亦或是只删去分类为”A”的探针。由此过滤掉大部分表达量低的探针。

注意,由于 mas5calls 函数只能针对 AffyBatch 对象,因此只能用于标准化前的数据。而且不能用于oligo包读取的数据。

以下代码是使用 mas5calls 函数过滤探针的例子,数据来自于 affydata 包的 Dilution 数据。代码的功能是过滤掉在所有样本中均不表达的探针。

library(affy) library(affydata) data("Dilution") eset <- rma(Dilution) calls <- mas5calls(Dilution) # 获得探针的PMA信息 calls <- exprs(calls) # 提取包含探针的PMA信息的表达矩阵 head(calls) absent <- rowSums(calls == "A") # 统计每个探针在所有样本中分类为"A"的个数 absent <- which(absent == ncol(calls)) # 提取在所有样本中分类均为"A"的探针,也就是在所有样本中都不表达的探针。 rmaFiltered <- eset[-absent,] # 删去不表达的探针 nrow(exprs(eset)) # 过滤前的总探针数目 nrow(exprs(rmaFiltered)) # 过滤后的总探针数目 

稍微更改一下条件。提取在所有样本中均表达的探针。代码如下。

present <- rowSums(calls == "P") # 统计每个探针在所有样本中分类为"P"的个数 present <- which(present == ncol(calls)) # 提取在所有样本中分类均为"P"的探针,也就是在所有样本中都表达的探针。 rmaFiltered <- eset[present,] # 只留下在所有样本中均表达的探针 

再更改一下条件,提取至少在一个样本中表达的探针。代码如下。

present <- rowSums(calls == "P") # 统计每个探针在所有样本中分类为"P"的个数 present <- which(present != 0) # 提取至少在一个样本中表达的探针。 rmaFiltered <- eset[present,] # 留下至少在一个样本中表达的探针 

大家可以按照自己的需求修改上述代码,达到过滤探针的目的。

使用 genefilter 函数过滤探针的好处在于能够自定义筛选条件,但多数情况下用不到。而使用 nsFilter 函数或 mas5calls 函数过滤探针的方法虽然固定,但操作方便。大家根据自己的需求使用相对应的函数就可以了。

mas5calls函数只能针对AffyBatch对象进行,因此无法用于oligo包读取的。oligo包读取的数据对象如下。可以看到无论哪种类型的对象都不能用于mas5calls函数。

oligo包提供了paCalls函数用于过滤探针。对于Whole Transcript arrays (Exon/Gene),可选择的method有”DABG” (p-values for
each probe) 和 “PSDABG” (p-values for each probeset)。对于Expression arrays 来说,只能选”MAS5”。

使用oligoData包中的affyExpressionFS数据作为例子。

首先载入数据。

library(oligo) library(oligoData) data("affyExpressionFS") 

使用oligo包提供的rma函数进行标准化。

注意,affy包中也有rma函数。但affy包提供的rma函数不能处理oligo包读取的对象。因此若同时加载了affy包,直接使用rma函数可能会报错。可以选择不加载affy包,或是使用oligo::rma()命令使用oligo包中的rma函数。

> data.eset <- rma(affyExpressionFS) Background correcting Normalizing Calculating Expression 

使用paCalls函数。

calls <- paCalls(affyExpressionFS) #返回calls view(calls) 

观察calls的结构,发现calls是一个list,包含callsp两个元素。calls是包含探针PMA信息的matrix,而p是包含p值信息的matrix。所谓的p值,是指探针表达量与背景相同的概率,越小说明探针与背景的差异越显著,也就是指探针表达的可能性越大。

接下来的步骤与方法2相同。详情请看方法2.

pmas <- calls$calls absent <- rowSums(pmas == "A") absent <- which(absent == ncol(pmas)) rmaFiltered <- data.eset[-absent,] nrow(data.eset) nrow(rmaFiltered) 

这里提供另一个有趣的例子。GSE72154 。这个例子中paCalls函数的返回值比较特别。下面将会详细介绍使用paCalls函数过滤该研究。为了省时间和突出重点,芯片质量控制就不做了。

首先在ftp下载GSE72154_RAW.tar,解压缩到工作目录。然后用oligo包读取数据。

celfiles <- list.files(path = ".", pattern = ".CEL", full.names = T) data.raw <- read.celfiles(celfiles) #读取数据 sampleNames(data.raw) <- c("ler1","ler2","wrky1","wrky2") data.eset <- rma(data.raw) #标准化 calls <- paCalls(data.raw) #获得calls class(calls) #查看calls类型 head(calls) #查看calls信息 

注意到,和上文不同,paCalls函数在这个例子中返回值callsmatrix对象。同时要注意到,探针名称发生了改变。

data.raw和calls的探针名称一致,而data.eset的探针名称发生了改变。如果要对data.eset进行探针过滤,就势必要构建两种探针名称的对应关系。

观察calls的结构,发现这个例子中的calls,其实相当于上个例子中的calls$p,也就是包含p值的矩阵。因此,我们可以根据p值来过滤探针。

P <- apply(calls, 1, function(x) any(x < 0.05)) #选择在所有样本中至少有一个探针p值<0.05的探针名 pids <- as.numeric(names(P[P])) #将筛选后的转换探针名称转化为numeric对象,因为后续分析提取的转换探针名是numeric格式。 head(pids) 

获得了符合条件”在所有样本中至少有一个探针p值<0.05”的探针名。接下来要将转换探针名称与原始探针名称对应起来。

pinfo <- getProbeInfo(data.raw) #获取转换探针名称与原始探针名称的对应关系 head(pinfo) fids <- pinfo[pinfo$fid %in% pids, 2] #获取筛选后的原始探针名称。第二列为原始探针名称。 

过滤探针,返回ExpressionSet对象。

data.filter <- data.eset[rownames(data.eset) %in% fids, ] #保留符合筛选条件的探针,返回ExpressionSet对象 nrow(data.eset) #查看过滤前探针数目 nrow(data.filter) #查看过滤后探针数目 

其实方法3的第一个例子也可以用第2个例子的方法来做,只是比较麻烦而已,所以如果能用第一个方法做就不要用第二个方法。如果都嫌麻烦的话,可以用nsFilter函数来做,一步到位。

上面的例子过滤前有38408个探针,过滤后有35697个探针。只过滤了10%的探针。可以考虑更加严格的条件,比如apply(calls, 1, function(x) any(x<0.01))(即p < 0.01), 或是 apply(calls, 1, function(x) sum(x < 0.05) > 2)(即至少在3个样本中p < 0.05).

参考:

1.用affy包读取affymetix的基因表达芯片数据-CEL格式数据

2.使用oligo软件包处理芯片数据

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!