由于實驗人員、技術、環境、時間點、芯片處理等各種因素原因,得到的芯片表達矩陣是含有非生物學因素差異的。
有時候想分析兩組間差異,但是一個數據集下面的樣本數量略少,因此想整合多個樣本的數據進行大樣本分析。但是在一個數據集里面,即使是統一平臺的芯片數據之間都是有批次效應和偏差的,整合在一起就更加放大了。(對于seq數據來說好像影響沒這么大,TPM、FPKM等歸一方法效果拔群,因此經常有TCGA+GTEx的聯合分析)
這里記錄芯片數據的批次效應及多數據集整合。參考資料包括:http://www.lxweimin.com/p/454fbb13af28、http://www.lxweimin.com/p/d4ff373d3d3f、http://www.lxweimin.com/p/dce79ecf52ea和https://blog.csdn.net/yayiling/article/details/113664434
有一篇18年的sci report,用的sva來batch normalize,一言難盡,這真的能篩出來嗎?anyway,先記錄,再比較,看結果。(也許包的功能很好,只是文章讓人懷疑)
什么是批次效應?
Leek et. al (2010) define batch effects as follows: Batch effects are sub-groups of measurements that have qualitatively different behaviour across conditions and are unrelated to the biological or scientific variables in a study. For example, batch effects may occur if a subset of experiments was run on Monday and another set on Tuesday, if two technicians were responsible for different subsets of the experiments, or if two different lots of reagents, chips or instruments were used.
批次效應是測量時在條件間有質量差別行為的,在實驗中與生物學或科學變量不相關的一小群。舉個例子,如果一部分實驗在周一進行,另一部分在周二進行,批次效應可能發生。或者兩個技術員分別做同一實驗的不同部分,或者使用了不同的試劑、芯片或者使用手冊都會參數batch effect。
如何判斷批次效應?
1、數據集種明確說明
2、Boxplot圖
3、聚類樹
4、PCA分析
下面代碼節選自JIMMY的教程,有興趣的可以自行學習
## hclust
colnames(exprSet)=paste(group_list,1:ncol(exprSet),sep='_')
# Define nodePar
nodePar <- list(lab.cex = 0.6, pch = c(NA, 19),
cex = 0.7, col = "blue")
hc=hclust(dist(t(exprSet)))
par(mar=c(5,5,5,10))
png('hclust.png',res=120)
plot(as.dendrogram(hc), nodePar = nodePar, horiz = TRUE)
dev.off()
## PCA
library(ggfortify)
df=as.data.frame(t(exprSet))
df$group=group_list
png('pca.png',res=120)
autoplot(prcomp( df[,1:(ncol(df)-1)] ), data=df,colour = 'group',frame.type = 'norm')+theme_bw()
dev.off()
芯片數據的處理
1.批次效應不能被消除,只有盡可能的降低。
2.是否會矯枉過正?
① 聯合分析正常組為一個數據集(一種芯片);
② 實驗組為另一個數據集(另一種芯片);
批次因素和分組因素可能重疊,所以直接對原數據矯正批次可能會抵消一部分真實生物學因素
3.使用removeBatchEffect (limma) 或者ComBat (sva) 函數后得到的表達數據,僅可用于銜接可視化(如聚類、PCA等),可視化展示,不能將去批次后的數據用于差異分析!
問:那我去批次效應整合數據集有什么意義呢?
4.如果想要在鑒定差異基因的過程中降低批次效應,將批次加入到design中
情況1 【數據集(1個)明確給出批次效應】
使用sva包的ComBat函數
有些GEO數據里面包含了實驗的批次,就可以用這個包來去除。
情況2 【數據集(1個)壓根沒提批次效應】
僅判斷是否需要log2和normalization
使用limma包中normalizeBetweenArrays函數 (僅在同一個數據集里面使用)
boxplot(rt)
rt <- normalizeBetweenArrays(rt)
boxplot(rt)
問:這些所謂的去除批次效應的方法,和quantile normalization的區別在哪里呢?是否目的是一致的?
情況3 【多數據集合并】
##########################合并數據#########################
#了解到芯片數據每個都平臺保留的基因不一樣,要全都有的才留下
#setwd('path_to_data_you_download')
files <- unlist(lapply(strsplit(list.files(pattern = 'matrix'),'_'),function(x) {x[[1]]}))
exprslist <- list()
pdatalist <- list()
count <- c()
for (i in files){
#批量生成數據框
#assign(paste0(i,'_exprs'),read.table(paste0(i,'_matrix.txt'),sep='\t',row.names = 1))
#assign(paste0(i,'_exprs'),mutate(get(paste0(i,"_exprs")),ID=rownames(get(paste0(i,"_exprs")))))
#生成一個列表
rt <- read.table(paste0(i,'_matrix.txt'),sep='\t',row.names = 1)
count <- c(count,ncol(rt))
rt <- as.data.frame(normalizeBetweenArrays(rt))
rt$ID <- rownames(rt)
exprslist[[i]] <- rt
#assign(paste0(i,'_pdata'),read.delim(paste0(i,'_pdata.txt'),sep='\t',row.names = NULL))
rt <- read.delim(paste0(i,'_pdata.txt'),sep='\t',row.names = NULL)
pdatalist[[i]] <- rt
}
rm(rt)
in_jo <- function(x,y){
inner_join(x,y,by='ID')
}
merged_exprs <- Reduce(in_jo,exprslist)
rownames(merged_exprs) <- merged_exprs$ID
merged_exprs <- select(merged_exprs,-ID)
#有些行里面包含了na,需要去掉
merged_exprs <- merged_exprs[complete.cases(merged_exprs),]
Listrb <- function(x,y){
rbind(x,y)
}
merged_pdata<- Reduce(Listrb,pdatalist)
merged_pdata$group <- ifelse(grepl('Primary',merged_pdata$title),'Primary','Metastasis')
rm(exprslist,pdatalist,i,in_jo,Listrb)
#最終我們得到了合并后的exprs和pdata,可以用來batch了,count包含每個batch的數量
(1)可去除:使用sva包/limma包;
①limma包removeBatchEffect函數
library(limma)
boxplot(merged_exprs)
batch <- rep(files,times=count)
batch <- as.factor(batch)
rm(files,count)
design <- model.matrix(~0 + batch)
batchremove_limma <- removeBatchEffect(merged_exprs,
batch = batch)
boxplot(batchremove_limma)
library(ggfortify)
limma <- as.data.frame(t(batchremove_limma))
limma$group <- merged_pdata$group
limma$batch <- batch
autoplot(prcomp(limma[,1:(ncol(originaldata)-2)] ),
data=limma,colour = 'group',
frame.type = 'norm')+
theme_bw()
autoplot(prcomp(limma[,1:(ncol(originaldata)-2)] ),
data=limma,colour = 'batch',
frame.type = 'norm')+
theme_bw()
②sva包ComBat函數
library(sva)
batchremove_combat <- ComBat(dat = as.matrix(merged_exprs), batch = batch)
boxplot(batchremove_combat)
③MatchMixeR
坑
(2)不可去除:RobustRankAggreg包整合分析
RobustRankAggreg不再需要合并原始數據,只需要按照流程得到每個數據集的差異表達基因,按照logFC從大到小的順序排列好即可。
padj=0.05
logFC=1
if(TRUE){
files=list.files()
upList=list()
downList=list()
allFCList=list()
for(i in 1:length(files)){
inputFile=files[i]
rt=read.table(inputFile,header=T,sep = '\t') # 注意文件讀取
rt <- rt[order(rt$logFC),]
header=unlist(strsplit(unlist(strsplit(inputFile,"_"))[[2]],".txt"))# 新數據需要修改
downList[[header[1]]]=as.vector(rt[,1])
upList[[header[1]]]=rev(as.vector(rt[,1]))
fcCol=rt[,1:2]
colnames(fcCol)=c("Gene",header[[1]])
allFCList[[header[1]]]=fcCol
}
rm(fcCol,rt,files,header,i,inputFile)
mergeLe=function(x,y){
merge(x,y,by="Gene",all=T)}
newTab=Reduce(mergeLe,allFCList)
#newTab = na.omit(newTab)
newTab <- newTab[!duplicated(newTab$Gene),]
rownames(newTab)=newTab[,1]
newTab=newTab[,2:ncol(newTab)]
newTab[is.na(newTab)]=0
}
library(RobustRankAggreg)
if(TRUE){
upMatrix = rankMatrix(upList)
#upMatrix = rankMatrix(upList,full=T)
upAR = aggregateRanks(rmat=upMatrix)
colnames(upAR)=c("Name","Pvalue")
upAdj=p.adjust(upAR$Pvalue,method="bonferroni")#
upXls=cbind(upAR,adjPvalue=upAdj)
upFC=newTab[as.vector(upXls[,1]),]
upXls=cbind(upXls,logFC=rowMeans(upFC))
#write.table(upXls,file="up.xls",sep="\t",quote=F,row.names=F)
upSig=upXls[(upXls$adjPvalue<padj & upXls$logFC>logFC),]
#upSig=upXls[ upXls$logFC>logFC,]
#write.table(upSig,file="upSig.xls",sep="\t",quote=F,row.names=F)
downMatrix = rankMatrix(downList)
#downMatrix = rankMatrix(downList,full=T)
downAR = aggregateRanks(rmat=downMatrix)
colnames(downAR)=c("Name","Pvalue")
downAdj=p.adjust(downAR$Pvalue,method="bonferroni")
downXls=cbind(downAR,adjPvalue=downAdj)
downFC=newTab[as.vector(downXls[,1]),]
downXls=cbind(downXls,logFC=rowMeans(downFC))
#write.table(downXls,file="down.xls",sep="\t",quote=F,row.names=F)
downSig=downXls[(downXls$adjPvalue<padj & downXls$logFC< -logFC),]
#downSig=downXls[downXls$logFC< -logFC,]
#write.table(downSig,file="downSig.xls",sep="\t",quote=F,row.names=F)
allSig = rbind(upSig,downSig)
colnames(allSig)
allSig = allSig[,c("Name","logFC")]
#write.table(allSig,file = 'allSign.xls',sep = '\t',quote = F)
}
hminput=newTab[c(as.vector(upSig[1:20,1]),as.vector(downSig[1:20,1])),]
library(pheatmap)
pheatmap(hminput,display_numbers = TRUE,
fontsize_row=10,
fontsize_col=12,
color = colorRampPalette(c("green", "white", "red"))(50),
cluster_cols = FALSE,cluster_rows = FALSE, )
write.table(downSig,sep='\t',quote=F,row.names = F,file='downSig.txt')
write.table(upSig,sep='\t',quote=F,row.names = F,file='upSig.txt')
總結
本文以代碼形式提供了整合芯片數據獲得差異基因的幾種方法,具體的算法流程需要各位親自理解和摸索(因為我也不會)