May 09 2008

用R和BioConductor进行基因芯片数据分析(六):差异表达基因

分类: programming, studyazalea @ 4:19 am 555次阅读

经过一系列的预处理,包括缺失值填充,中位数计算以及归一化,我们的数据终于可以用啦。。

下面我们就来分析一下new population和old population的个体是否有差异表达基因。

判断一个基因是否差异表达有许多方法,最早使用的就是看log ratio的绝对值是否大于2,这种方法早已废弃。

下一个想到的也许是t-test,诚然t-test可以统计地判断一个基因是否差异表达,但是对于有数千数万基因的芯片来说,会有很高的错误发现率(False Discovery Rate, FDR),如果 p value < 0.05,则10000个基因里有500个基因实际没有差异表达而被误认为是差异表达。因此t-test方法需要改进。

于是 Westfall & Young (1993) 提出了Step-down maxT and minP multiple testing procedures,大意就是比较几个group间有没有差异基因表达,就通过随机置换这些group的标记,相当于随机互换group的成员,模拟一个空分布(null distribution),以此计算调整后的p value,这个方法可以极大的减小Family-wise Error Rate (FWER).

以下分析就使用Step-down maxT and minP multiple testing procedures,需要求助于Bioconductor的multtest package的mt.maxT()函数。具体用法可通过help(mt.maxT)查看。

library(multtest)
classlabel<-c(0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1)
maxttt<-mt.maxT(norm_log_btw_array,classlabel,B=100000)

默认随机置换次数B=10000,对于microarray来说B应该比10000大很多,所以这里取B=100000
以下是画图:
rawp<-maxttt$rawp[order(maxttt$index)]
plot(sort(rawp),type=’p',col=1,ylim=c(-0.05,1.00),ylab=’p value’)
lines(sort(maxttt$adjp),type=’p',col=’red’)
#min adj-p: sort(maxttt$adjp)[1] 0.0163
#rawp: >sort(rawppp)[170] [1] 0.0493 > sort(rawppp)[171] [1] 0.0502 170个raw p小于0.05
abline(h=0.05,col=’blue’)
text(1000,c(0.6,0.7),labels=c(’raw p-value’,'adjusted p-value’),col=c(’black’,'red’))
text(1000,0.08,labels=’p=0.05′,col=’blue’)

可见调整后只有一个基因的p value小于0.05,而未调整的有170个基因的p value小于0.05,可以说虽然此方法降低了错误发现率,但是也导致了很高的False negative.

此外可以考虑使用multtest package的mt.rawp2adjp()函数,这个函数可以通过”Bonferroni”, “Holm”, “Hochberg”, “SidakSS”, “SidakSD”, “BH”, “BY”等方法调整p value,不过对我们的数据来说都过于严格了。

procs<-c(”Bonferroni”,”Holm”,”Hochberg”,”SidakSS”,”SidakSD”,”BH”,”BY”)
adjps<-mt.rawp2adjp(rawp,procs)
plot(sort(adjps$adjp[,1]),ylab=’p value’)
for (i in 2:8){
points(sort(adjps$adjp[,i]),col=i)
}
abline(h=0.05,col=’blue’)
text(1000,0.08,labels=’p=0.05′,col=’blue’)

因此可以考虑不这么严格的SAM (Significance Analysis of Microarrays)分析。具体懒得写了,有兴趣的请看参考资料。。

参考资料:

课堂讲义:Differential expression. Identifying differentially expressed genes — notions on multiple testing and p-value adjustments.

 

Dudoit, S., Yang, Y.H., Speed, T.P., and Callow, M.J. (2002), Statistical methods for identifying differentially expressed genes in replicated cDNA microarray experiments, Statistica Sinica 12(1):111-139.

 

Dudoit S., Shaffer J.P., Boldrick J.C. (2003). Multiple hypothesis testing in microarray experiments, Statistical Science, 18(1): 71-103.

 

Efron B., Tibshirani, R., Storey J.D., and Tusher V. (2001), Empirical Bayes analysis of a microarray experiment, Journal of the American Statistical Association 96:1151-1160.

SAM (Significance Analysis of Microarrays)相关:Tusher, V.G., Tibshirani, R., and Chu, G. (2001) Significance analysis of microarrays applied to the ionizing radiation response, PNAS 98:5116-5121.

SAM 网站

FDR相关:Storey J.D. (2002), A direct approach to false discovery rates, JRSS-B 64(3):479-498.

相关文章



May 05 2008

用R和BioConductor进行基因芯片数据分析(五):芯片间归一化

分类: programming, studyazalea @ 3:58 am 400次阅读

上次进行了芯片内的归一化,但是我们的数据来自于10张芯片,为了让这10张芯片之间有可比性,需要进行芯片间归一化。

由于我比较懒,具体原理就不介绍了。

这里用到Bioconductor的一个package,叫做limma,以及其中的函数normalizeBetweenArrays()

由于normalizeBetweenArrays()需要log intensity或log ratio作为输入,于是先进行log转化:

#log transformation
norm_log<-matrix(data = NA, nrow =dim(normed)[1], ncol = dim(normed)[2], byrow = TRUE, dimnames = NULL)
for (i in 1:dim(normed)[1]){
for (j in 1:dim(normed)[2]){
norm_log[i,j]<-log(normed[i,j])/log(2)
}
}

然后利用函数进行芯片间归一化:

library(limma)
norm_log_btw_array<-normalizeBetweenArrays(norm_log,method=’scale’)

normalizeBetweenArrays()函数有许多方法,具体请看帮助。

下面看看效果吧

plot(density(norm_log[,1]),ylim=c(0,1.35),xlab=’log intensity’)
for (i in 2:20){
lines(density(norm_log[,i]),type=’l')
}
lines(density(norm_log_btw_array[,1]),type=’l',col=’green’)
for (i in 2:20){
lines(density(norm_log_btw_array[,i]),type=’l',col=’green’)
}
text(1.5,c(0.8,1.0),labels=c(’BEFORE normalization’,'AFTER normalization’),col=c(’black’,'green’))

相关文章



May 05 2008

用R和BioConductor进行基因芯片数据分析(四):芯片内归一化

分类: programming, studyazalea @ 2:04 am 640次阅读

归一化是从normalization翻译过来的,google了半天,还是不知道是否恰当。归一化的目的是使各次/组测量或各种实验条件下的测量可以相互比较,消除测量间的非实验差异。非实验差异可能来源于样品制备,点样,杂交过程,杂交信号处理等,在此不详述了。

归一化的方法有很多,对于寡聚核苷酸芯片(单通道,以Affymetrix为代表)和cDNA芯片(双通道,红绿染料)也有所不同。以下讨论针对双通道芯片进行,当然也可能适用于单通道,请读者自辨。

归一化通常分为”bulk” normalization和”control-based” normalization. 前者假定仅有一小部分基因表达值在不同条件下有差异,因此使用所有的基因作为标准进行归一化;而后者使用表达值被认为是不变的那些对照基因作为标准进行归一化。这2种方法的假设前提都未必时时成立,因此需要具体情况具体分析。

“bulk” normalization又细分为许多方法,最简单的就是global normalization,这种方法假设红色染料的信号强度与绿色染料的信号强度是正相关的,即R=kG (R:红色信号强度,G:绿色信号强度,k:常数),因此表达信号强度的以2为底的对数比log ratio在归一化之后相当于平移了一个常量c=logk:log(R/G) → log(R/G) - c = log(R/G) - logk = log[R/(kG)].
c的计算方法通常是用所有红色信号强度和除以所有绿色信号强度和然后取以2为底的对数,即c=log[total(R)/total(G)]

Intensity-dependent normalization通常比global normalization更好,因为后者的假设通常并不完全正确,通常情况下,log ratio是和信号强度值有关的,即log(R/G) → log(R/G) - c(A),这里A=1/2*log(R*G),是log product intensity或简称log intensity。

对于我们的数据mediandata,我们可以直观的看到log ratio和信号强度的关系:

上图叫做MA-plot(MA图),纵坐标是M=log(R/G)=log(new/old),横坐标是A=1/2*log(R*G)=1/2*log(new*old)。
其中的蓝色曲线是lowess回归函数(什么是lowess)。(注:由于原始数据有5行有0值,导致有些M或A=Inf或-Inf,无法进行Lowess回归,因此人工删除了这5行,处理后的中位数数据mediandata在这里下载。当然你也可以用原始数据求出M和A值,自己删除Inf值对应的mediandata中的行。

在R中画出上图的代码:
mediandata<-read.table(’mediandata.txt’,header=TRUE)
mediandata<-mediandata[,-1] #移除第一列ID列
MA<-matrix(data = NA, nrow =dim(mediandata)[1], ncol = dim(mediandata)[2], byrow = TRUE, dimnames = NULL)
new<-0
old<-0
for (i in 1:dim(mediandata)[1]){
for (j in 1:(dim(mediandata)[2]/2)){
new<-mediandata[i,2*j-1]
old<-mediandata[i,2*j]
MA[i,2*j]<-log(new/old)/log(2) #M=log(new/old)/log2
MA[i,2*j-1]<-1/2*log(new*old)/log(2) #A=1/2*log(new*old)/log2
}
}
plot(MA[,1],MA[,2],xlab=’A',ylab=’M')
lines(lowess(MA[,1],MA[,2],f=0.2,iter=2),lwd=2,col=’blue’)
#仅画出第一张芯片上2个杂交样本的MA图,使用MA[,3],MA[,4]可以画出第2张芯片的图,依此类推。

可以看出原始数据的log ratio受到log intensity的影响,因此需要intensity-based normalization.
R的lowess函数返回一个$y对象,储存每个A值(lowess返回的$x对象)对应的~M值,而归一化后的M’=M-~M=M-$y($x)

这样归一化后得到10个芯片的log ratio即10列数据,但是为了后面分析的方便,我想得到10个new的强度值和10个old的强度值共20列数据,那怎么办呢?

答案很简单,就是假设new的强度值在归一化后不变,而仅仅改变old的强度值,得到old’=old*2^($y($x)) 注:$y($x)是2的指数,推导很简单,想不明白的可以留言讨论。

下面是R代码:
normed<-matrix(data = NA, nrow =dim(MA)[1], ncol = dim(MA)[2], byrow = TRUE, dimnames = NULL) #new—奇数列; old—偶数列
for (j in 1:(dim(MA)[2]/2)){
out_lowess<-lowess(MA[,2*j-1],MA[,2*j],f=0.2,iter=2)
#A=MA[,1],M=MA[,2]
loc_lowess<-cbind(out_lowess$x,out_lowess$y)
for (i in 1:dim(MA)[1]){
normed[i,2*j-1]<-mediandata[i,2*j-1] #归一化后的new’强度值
normed[i,2*j]<-mediandata[i,2*j]*2^(loc_lowess[,2][loc_lowess[,1]==MA[i,2*j-1]][1]) #归一化后的old’强度值
}
}

搞定,看看效果吧:
MAnormed<-matrix(data = NA, nrow =dim(MA)[1], ncol = 2, byrow = TRUE, dimnames = NULL)
MAnormed[,2]<-log(normed[,1]/normed[,2])/log(2) #M=log(new/old)/log2
MAnormed[,1]<-1/2*log(normed[,1]*normed[,2])/log(2) #A=1/2*log(new*old)/log2
plot(MAnormed[,1],MAnormed[,2],xlab=’A',ylab=’M')
lines(lowess(MAnormed[,1],MAnormed[,2],f=0.2,iter=2),lwd=2,col=’blue’)

现在的lowess回归曲线是一条直线了,说明归一化后的log ratio与强度值无关了。

可以从另一个角度看归一化的效果:

plot(density(normed[,1]),type=’line’,col=’red’,xlab=’intensity’)
points(density(normed[,2]),type=’line’,col=’green’)
points(density(mediandata[,1]),type=’line’,col=’blue’)
points(density(mediandata[,2]),type=’line’,col=’black’)
text(2.2,c(0.09,0.11,0.13,0.15),labels=c(’BEFORE normalization black’,'BEFORE normalization blue’,'AFTER normalization green’,'AFTER normalization red’),col=c(’black’,'blue’,'green’,'red’))

关于芯片内归一化,可以参考以下资料:

cDNA双通道芯片:

Yang Y.H., Dudoit S., Luu P., Speed T.P. (2001) Normalization for cDNA microarray data, SPIE BiOS 2001, San Jose CA;

Yang Y.H., Dudoit S., Luu P., Lin D.M., Peng V., Ngai J., Speed T.P. (2002), Normalization for cDNA microarray data: a robust composite method addressing single and multiple slide systematic variation, Nucleic Acids Research 30(4);

寡聚核苷酸单通道芯片:

Bolstad B.M., Irizarry R. A., Astrand M., Speed T.P. (2003) A comparison of normalization methods for high density oligonucleotide array data based on bias and variance, Bioinformatics 19(2): 185-193

相关文章



May 03 2008

用R和BioConductor进行基因芯片数据分析(三):计算median

分类: programming, studyazalea @ 12:52 am 426次阅读

我们已经知道要分析的数据对每个基因有3个重复测定值,经过缺失值填充后,每个基因都有3个可用值。

这一步很简单,就是取这3个值的中位数,即median。

方法很多,在excel中可以用median函数;

在R中我写了以下代码进行操作:

get_median<-function(i,j){
num_vec<-c(imputeddata[i*3-2,j],imputeddata[i*3-1,j],imputeddata[i*3,j])
median(num_vec)
}
#A simple function to calculate median value of three replicates

dimrow<-(dim(imputeddata)[1])/3
mediandata<-matrix(data = NA, nrow =dimrow, ncol = dim(imputeddata)[2], byrow = TRUE, dimnames = NULL)
#Create a blank matrix to store median values

for (i in 1:dimrow){
for (j in 1:dim(imputeddata)[2]){
mediandata[i,j]<-get_median(i,j)
}
}
#Assign median value using the function get_median()

可能有更好的方法,欢迎留言讨论

现在我们得到了中位数的数据,储存在mediandata对象里,行数是缺失值填充数据imputeddata的1/3,double check一下:

> dim(imputeddata)
[1] 11571 20
> dim(mediandata)
[1] 3857 20

相关文章



May 02 2008

用R和BioConductor进行基因芯片数据分析(二):缺失值填充

分类: programming, studyazalea @ 2:49 am 656次阅读

以下分析用到的数据可以在这里下载,这个数据来自关于基因对蝴蝶迁移性的研究,样本是20个蝴蝶个体,其中10个是当地固有个体(old),另外10个是新迁入的个体(new),old和new个体两两随机配对,分别用不同颜色染料(波长分别为555和647nm)标记后,在同一张基因芯片上杂交;此外,每个基因在每张芯片上都重复点样3次,因此此数据是有3个replicates及10张芯片的双通道芯片。数据是样点的信号强度值,没有经过标准化处理的。

拿到数据你会看到许多”NA”,这是因为我把缺失的空白值替换成NA了, 以便用R进行缺失值填充。

说到缺失值填充,通常有3种方法:

A. 用此基因的平均表达值填充;如果有多张重复芯片,可以取不同芯片上的平均值;对于时间序列芯片,可以通过差值法填充。—此方法很简单,也比较常用,但是效果不及下面2种方法

B. 基于SVD(即主成分分析)方法的填充:简单地讲,此方法是通过描述基因表达谱的几个基本模式来对缺失值进行填充。

C. 基于KNN(最近邻)方法的填充: 此方法是寻找和有缺失值的基因的表达谱相似的其他基因,通过这些基因的表达值(依照表达谱相似性加权)来填充缺失值。KNN法是这3种方法里效果最好的,因此对本数据的缺失值用KNN法填充。

对以上3种方法的比较,这篇paper提供了清晰的说明: Troyanskaya, O., Cantor, M., Sherlock, G., Brown, P., Hastie, T., Tibshirani, R., Botstein, D., and Altman, R. B. (2001), Missing value estimation methods for DNA microarrays, Bioinformatics 17(6):520-525. 推荐大家看看

铺垫了这许多,下面开工分析数据啦

首先要安装最新版本的R 2.7.0,上面一篇里有下载地址。我之前用的2.5.1版本,安装下面的package有错误,所以强烈推荐最新版本

然后下载安装叫做impute的package,下载地址: http://bioconductor.fhcrc.org/packages/1.9/bioc/html/impute.html

impute是专门用KNN法进行缺失值填充的R package,它的安装如前文所述:

如果是Linux下,就在shell输入: sudo R CMD INSTALL impute_1.6.0.tar.gz

设置好当前工作目录(Windows是在R的菜单栏->工作目录…设置,Linux下用setwd()函数)

然后在R控制台输入以下代码:


library(impute)
#导入impute package
raw<-read.table(’raw_data_3_replicates.txt’,header=TRUE)
rawexpr<-raw[,-1]
#移除第一列ID列
if(exists(”.Random.seed”)) rm(.Random.seed)
#必须,如果没有这句话会出错,原因不知-,-请高手指教
imputed<-impute.knn(as.matrix(rawexpr) ,k = 10, rowmax = 0.5, colmax = 0.8, maxp = 1500, rng.seed=362436069)
#impute.knn() 使用一个矩阵作为第一个参数,其他参数这里使用的是默认值
write.table(imputed$data,file=’imputed_data.txt’)
#write.table() 把数据保存在当前工作目录下的文件中,文件名用file=’ ‘指定,这一步不是必须的
imputeddata<-imputed$data
#imputed$data是在R中储存imputed后的数据的矩阵

现在在R里输入imputed,即填充好的数据矩阵,是不是NA值全都没了?

OK,这一步就这样搞定啦

关于impute package的详细Documentation在这里

不放心,于是在下面再贴一遍,如果你不求甚解就不用看啦

继续阅读 “用R和BioConductor进行基因芯片数据分析(二):缺失值填充”

相关文章



第 1 页,共 2 页12>