数据挖掘学习笔记——GEO数据库:芯片数据分析
虽然目前市场上高通量测序成为了主流,不过芯片测序也发展了十几年,在公共数据库中留下了大量的值得被数据挖掘的数据集,所以学习一下GEO数据挖掘也是有意义的。
意义:基因芯片分析就是为了通过生物信息学方法从这些芯片数据中发现可能对生物效应起作用的关键基因,从中寻找特定模式并对每个基因给予注释,从而挖掘出隐含的生物学过程并抽提出生物学的或功能层面上的意义。
根据芯片的使用目的,一张芯片可能包含数十、数百甚至数十万的不同序列。
芯片实验过程:样本mRNA首先被反转录成cDNA(在过程中同时被荧光标记),后与芯片上的核酸探针混合,互补杂交的cDNA就结合到芯片上,而未被杂交的样本被洗脱掉。芯片被一个荧光扫描仪扫描后,芯片上某个位置探针结合上了样本中互补的核酸,就在该位置显出了一个荧光点,此位置提示基因的身份,而荧光强度则提示了原始样本中该mRNA水平的高低。
芯片技术中的两种基本方法:单染色技术和双染色技术。
主流平台:Affymetric公司、Agilent公司和Illumina公司。
数据下载:NCBI的GEO和EBI的ArrayExpress是目前最大的公开资源数据库,用于存储和发布与MIAME相容的芯片数据。
页面:
检索:
情况一:只能确定大体研究方向(如肺癌lung cancer)
情况二:阅读文献后获得具体的GSE号(如GSE42872)
soft和miniml都是表示该platform的基础信息,比如GPL编号,上传日期等。
由于针对原始数据使用的不同芯片,分析处理的方法也不同,所以不推荐这种方法。
gset <- read.table("GSE42872_series_matrix.txt.gz",comment.char = "!",header = T,row.names = 1)
利用GEOquery包读取GEF号。和第二种方法的结果是一样的,都是将压缩包下载下来。
getGEO(
GEO = NULL, # 输入GDS/GPL/GSE号
destdir = tempdir(), # 指定下载地址
GSEMatrix = TRUE, # 默认为 TRUE,表示不会进行 SOFT 格式解析
AnnotGPL = FALSE, # 表示是否使用注释 GPL 信息
getGPL = TRUE, # 是否下载 GPL 信息(SOFTT文件),指定F可以节省时间(文件大)
parseCharacteristics = TRUE # 是否解析 GSE 矩阵文件的特征信息
)
1、根据GDS号来下载数据,下载soft文件
gds858 <- getGEO(‘GDS858’, destdir=“.”,getGPL = F)
2、根据GPL号下载的是芯片设计的信息!
gpl96 <- getGEO(‘GPL96’, destdir=“.”,getGPL = F)
3、根据GSE号下载数据,下载series_matrix.txt.gz
gse1009 <- getGEO(‘GSE1009’, destdir=“.”,getGPL = F)
# 加载R包,suppressPackageStartupMessages函数取消了加载R包时的输出内容
suppressPackageStartupMessages(library(GEOquery))
# 下载数据,如果文件夹中有会直接读入(这边以GSE42872为例)
gset = getGEO('GSE42872', destdir=".",getGPL = F)
# 此时的gset 还是一个对象(list),需要将里面的东西取出来才能进行下一步分析。
# gset[[1]]就是list的第一层,也就是第一个GPL,由于该GSE只有一个GPL所以也只有一层list。
gset=gset[[1]]
# 获取表达矩阵
expr <- exprs(gset)
—般分析芯片数据都需要把探针的ID切换成基因名
我将常用的GPL平台对应的BIOconductor的R包整理为了表格,直接进行导入就可以了。
# 根据GPL查询R包
platformMap <- read.csv("platformMap.csv",header = T,row.names = 1)
index <- "GPL6244" # 改GPL号
package <- platformMap$bioc_package[grep(index,platformMap$gpl)]
package_db <- paste0(package,".db")
# 下载并调用R包
BiocManager::install(package_db)
library(hugene10sttranscriptcluster.db) # 改library包
# 导出探针ID与基因名的对照矩阵
probe2symbol_df <- toTable(get(paste0(package,"SYMBOL")))
不是所有的平台数据都被包装为了R包,剩下的这些需要我们手动绘制ID转换表格。
# 下载GPL数据(也就是soft文件)
library(GEOquery)
GPL6244 <-getGEO('GPL6244',destdir =".")
# 读取对象
GPL6244_anno <- Table(GPL6244)
library(dplyr) # 函数filter
library(tidyr) # 函数separate
probe2symbol_df1 <- GPL6244_anno %>% # 用管道进行连续操作
select(ID,gene_assignment) %>% # select函数将需要的列提取出来
filter(gene_assignment != "---") %>% # filter函数将gene_assignment列中= "---"的行全部过滤掉
separate(gene_assignment,c("drop","symbol"),sep="//") %>% # separate函数将gene_assignment按照//作为分隔符,保留前两项并命名
select(-drop)# 基因名在第二列,将第一列删除,并将结果传递给probe2symbol_df
GPL6244_anno <-data.table::fread("GSE42872_family.soft.gz",skip ="ID")
# skip ="ID"不能漏
library(dplyr) # 函数filter
library(tidyr) # 函数separate
probe2symbol_df2 <- GPL6244_anno %>% # 用管道进行连续操作
select(ID,gene_assignment) %>% # select函数将需要的列提取出来
filter(gene_assignment != "---") %>% # filter函数将gene_assignment列中= "---"的行全部过滤掉
separate(gene_assignment,c("drop","symbol"),sep="//") %>% # separate函数将gene_assignment按照//作为分隔符,保留前两项并命名
select(-drop)# 基因名在第二列,将第一列删除,并将结果传递给probe2symbol_df
GPL6244_anno1 <-data.table::fread("GPL6244-17930.txt")
library(dplyr) # 函数filter
library(tidyr) # 函数separate
probe2symbol_df3 <- GPL6244_anno1 %>% # 用管道进行连续操作
select(ID,gene_assignment) %>% # select函数将需要的列提取出来
filter(gene_assignment != "---") %>% # filter函数将gene_assignment列中= "---"的行全部过滤掉
separate(gene_assignment,c("drop","symbol"),sep="//") %>% # separate函数将gene_assignment按照//作为分隔符,保留前两项并命名
select(-drop)# 基因名在第二列,将第一列删除,并将结果传递给probe2symbol_df
这一个例子中,利用R包导入的基因量少于平台数据整理出来的数据量
现在我们希望将表达矩阵中的探针ID改成基因名
只需要将两个数据框按照探针名称合并就可以了, 同时在这里,我们还需要把重复的探针去掉,方法就是计算每一个探针的平均值,留下最大的那个。
# 调整两个数据框的探针名字和类型,方便合并
colnames(exprSet)[1] <- colnames(probe2symbol_df)[1]
exprSet <- as.data.frame(exprSet)
library(dplyr)
exprSet1 <- exprSet %>%
inner_join(probe2symbol_df1,by="probe_id") %>% #合并探针的信息
select(-probe_id) %>% #去掉多余信息
select(symbol, everything()) #重新排列,
#求出平均数(rowMean),并创建新的列(mutate函数)
a <- exprSet1[,c(grep("GSM", colnames(exprSet1)))]
for (i in 1:ncol(a)) {
a[,i] <- as.numeric(unlist(a[,i]))
}
a <- cbind(rowMeans(a),exprSet1)
exprSet <- a %>%
arrange(desc("rowMeans(a)")) %>% #把表达量的平均值按从大到小排序
distinct(symbol,.keep_all = T) %>% # symbol留下第一个
select(-"rowMeans(a)") %>% #反向选择去除rowMean这一列
tibble::column_to_rownames(colnames(.)[1]) # 把第一列变成行名并删除
此时得到的表达矩阵已经可以用于后续任何需要的分析了。
一般文章中会将差异基因的数量控制在200-300的范畴(当然越少越好)
这边介绍一下最适合分析芯片数据的差异化分析包——limma
# 表达矩阵(exprSet)
# BiocManager::install('CLL')
suppressPackageStartupMessages(library(CLL))
data(sCLLex) # sCLLex是CLL包中的一个对象(例子)
sCLLex
samples=sampleNames(sCLLex)
exprSet=exprs(sCLLex)# 获取表达矩阵,列是各个样本,行是每个探针ID
pdata=pData(sCLLex)# 获取分组信息
group_list=as.character(pdata[,2])# 描述了样本的状态,需要根据它来进行样本分类
# 分组矩阵(design)
suppressMessages(library(limma))
design <- model.matrix(~0+factor(group_list))
colnames(design)=levels(factor(group_list))
rownames(design)=colnames(exprSet)
# 差异比较矩阵(contrast)
# 比较矩阵的组之间用-连接
contrast.matrix<-makeContrasts(paste0(unique(group_list),collapse = "-"),levels = design)
contrast.matrix ##这个矩阵声明,我们要把progres.组跟stable进行差异分析比较
# 如果分组时有三个及三个以上,进行两两对比时,比较矩阵内就需要注明对象与顺序了
# contrast.matrix <- makeContrasts(group2-group1, group3-group2, group3-group1, levels=design)
# limma差异化分析
# step1,lmFit()
fit <- lmFit(exprSet,design)
fit2 <- contrasts.fit(fit, contrast.matrix)
# step2,eBayes()
fit2 <- eBayes(fit2)
# step3,topTable()
tempOutput = topTable(fit2, coef=1, n=Inf)
# 使用coef参数,这里设为1,也就是表示根据上面比较矩阵的第一个比较信息来提取结果,由于这个例子中只有两个分组,所以只会有一个比较信息
# n=Inf:展示的最大值设为无限大,可以不需要该参数
# 参数adjust="BH"表示对p值的校正方法,包括了:"none", "BH", "BY" and "holm",默认为BH,适用与多数情况,所以这边没有修改这个参数。
nrDEG = na.omit(tempOutput) # 删除NA值
write.csv(nrDEG,"C:/Users/16748/Desktop/limma_notrend.results.csv",quote = F) # 输出差异分析矩阵
内容包括AveExpr值(所有样本的平均表达量)、logFC值(差异倍数)、t值、P值、q值(即adj.P.Val值)和B值。一般logFC值、P值、q值和AveExpr值用来作为差异表达的判断标准,比如差异倍数在2倍以上、平均表达在10以上、P值小于0.01等。
t值是统计量,
• 第一列:“logFC”是两组表达值之间以2为底对数化的的变化倍数(Fold change, FC),由于基因表达矩阵本身已经取了对数,这里实际上只是两组基因表达值均值之差;
• 第二列:“AveExpr”是该探针组所在所有样品中的平均表达值;
• 第三列:“t”是贝叶斯调整后的两组表达值间 T 检验中的 t 值,根据t值可以判断p值进而判断模型是否显著;
• 第四列:“P.Value”是贝叶斯检验得到的 P 值(越小越好);
• 第五列:“adj.P.Val”是调整后的 P 值(也称为q值,越小越好).
• 第六列:“B”是经验贝叶斯得到的标准差的对数化值。
上述提到差异分析需要三个矩阵,表达矩阵包含样本信息,而样本必须进行分组,才能分析,所以这两个矩阵肯定需要,但比较矩阵则不一定需要。
先看下示例
library(CLL)
data(sCLLex)
library(limma)
design <- model.matrix(~factor(sCLLex$Disease))
fit <- lmFit(sCLLex,design)
fit <- eBayes(fit)
topTable(fit,coef=2,adjust='BH')
# 结果
logFC AveExpr t P.Value adj.P.Val B
39400_at 1.0285 5.621 5.836 8.341e-06 0.03344 3.234
36131_at -0.9888 9.954 -5.772 9.668e-06 0.03344 3.117
33791_at -1.8302 6.951 -5.736 1.049e-05 0.03344 3.052
1303_at 1.3836 4.463 5.732 1.060e-05 0.03344 3.044
36122_at -0.7801 7.260 -5.141 4.206e-05 0.10619 1.935
36939_at -2.5472 6.915 -5.038 5.362e-05 0.11283 1.737
41398_at 0.5187 7.602 4.879 7.824e-05 0.11520 1.428
32599_at 0.8544 5.746 4.859 8.207e-05 0.11520 1.389
36129_at 0.9161 8.209 4.859 8.212e-05 0.11520 1.389
37636_at -1.6868 5.697 -4.804 9.355e-05 0.11811 1.282
library(CLL)
data(sCLLex)
library(limma)
design <- model.matrix(~0+factor(sCLLex$Disease))
colnames(design) <- c('progres.','stable')
fit <- lmFit(sCLLex,design)
cont.matrix <- makeContrasts('stable-progres.',levels = design)
fit2 <- contrasts.fit(fit,cont.matrix)
fit2 <- eBayes(fit2)
topTable(fit2,adjust='BH')
# 结果
logFC AveExpr t P.Value adj.P.Val B
39400_at 1.0285 5.621 5.836 8.341e-06 0.03344 3.234
36131_at -0.9888 9.954 -5.772 9.668e-06 0.03344 3.117
33791_at -1.8302 6.951 -5.736 1.049e-05 0.03344 3.052
1303_at 1.3836 4.463 5.732 1.060e-05 0.03344 3.044
36122_at -0.7801 7.260 -5.141 4.206e-05 0.10619 1.935
36939_at -2.5472 6.915 -5.038 5.362e-05 0.11283 1.737
41398_at 0.5187 7.602 4.879 7.824e-05 0.11520 1.428
32599_at 0.8544 5.746 4.859 8.207e-05 0.11520 1.389
36129_at 0.9161 8.209 4.859 8.212e-05 0.11520 1.389
37636_at -1.6868 5.697 -4.804 9.355e-05 0.11811 1.282
差异分析的一个使用难点就是构建分组矩阵。具体使用方法在帮助文档的第九章(p42)都有讲解,这边就不过多赘述了。
# 加载R包
suppressPackageStartupMessages(library(GEOquery))
# 下载数据,如果文件夹中有会直接读入
gset = getGEO('GSE42872', destdir=".",getGPL = F)
# 获取ExpressionSet对象,包括的表达矩阵和分组信息
gset=gset[[1]]
# 获取分组信息
pdata <- pData(gset)
SampleID <- row.names(pdata)
Type <- c(rep("Control",3),rep("Experiment",3))
pdata <- cbind(SampleID,Type)
# 获取表达矩阵
exprSet <- exprs(gset)
# QC检测
opar <- par(no.readonly = T)
par(cex = 0.7)
n.sample <- ncol(exprSet)
if(n.sample>40) par(cex = 0.5)
cols <- rainbow(n.sample)
boxplot(exprSet,main="expression value",las=2)
par(opar)
# 数据校正
library(limma)
exprSet <- normalizeBetweenArrays(exprSet)
# 自动判断是否需要log转换
ex <- expr
qx <- as.numeric(quantile(ex, c(0., 0.25, 0.5, 0.75, 0.99, 1.0), na.rm=T))
LogC <- (qx[5] > 100) ||
(qx[6]-qx[1] > 50 && qx[2] > 0) ||
(qx[2] > 0 && qx[2] < 1 && qx[4] > 1 && qx[4] < 2)
if (LogC) { ex[which(ex <= 0)] <- NaN
exprSet <- log2(ex)
print("log2 transform finished")}else{print("log2 transform not needed")}
platformMap <- read.csv("platformMap.csv",header = T)
index <- gset@annotation
package <- platformMap$bioc_package[grep(index,platformMap$gpl)]
package_db <- paste0(package,".db")
# 下载并调用R包
BiocManager::install(package_db)
library(hugene10sttranscriptcluster.db)
# 导出探针ID与基因名的对照矩阵
probe2symbol_df1 <- toTable(get(paste0(package,"SYMBOL")))
colnames(probe2symbol_df1)[1] <- "ID"
# 调整两个数据框的探针名字和类型,方便合并
exprSet <- cbind(rownames(exprSet),exprSet)
colnames(exprSet)[1] <- colnames(probe2symbol_df1)[1]
exprSet <- as.data.frame(exprSet)
exprSet[,1] <- as.numeric(unlist(exprSet[,1]))
probe2symbol_df1[,1] <- as.numeric(unlist(probe2symbol_df1[,1]))
library(dplyr)
exprSet <- exprSet %>%
inner_join(probe2symbol_df1,by="ID") %>% #合并探针的信息
select(-ID) %>% #去掉多余信息
select(symbol, everything()) #重新排列,
#求出平均数(rowMean),并创建新的列(mutate函数)
a <- exprSet[,c(grep("GSM", colnames(exprSet)))]
for (i in 1:ncol(a)) {
a[,i] <- as.numeric(unlist(a[,i]))
}
a <- cbind(rowMeans(a),exprSet)
exprSet <- a %>%
arrange(desc("rowMeans(a)")) %>% #把表达量的平均值按从大到小排序
distinct(symbol,.keep_all = T) %>% # symbol留下第一个
select(-"rowMeans(a)") %>% #反向选择去除rowMean这一列
tibble::column_to_rownames(colnames(.)[1]) # 把第一列变成行名并删除
# 查看内参表达量来判定ID转化是否正确
# 常见的内参有GAPDH,ACTB等(QC检测时显示了总体数据的数值水平,内参值应该高于实验组值)
exprSet["GAPDH",]
group_list=as.character(pdata[,2])# 描述了样本的状态,需要根据它来进行样本分类
# 分组矩阵(design)
suppressMessages(library(limma))
design <- model.matrix(~0+factor(group_list))
colnames(design)=levels(factor(group_list))
rownames(design)=colnames(exprSet)
# 差异比较矩阵(contrast)
# 比较矩阵的组之间用-连接
contrast.matrix<-makeContrasts(paste0(unique(group_list),collapse = "-"),levels = design)
contrast.matrix ##这个矩阵声明,我们要把progres.组跟stable进行差异分析比较
# 如果分组时有三个及三个以上,进行两两对比时,比较矩阵内就需要注明对象与顺序了
# contrast.matrix <- makeContrasts(group2-group1, group3-group2, group3-group1, levels=design)
# limma差异化分析
# step1,lmFit()
for (i in 1:ncol(exprSet)) {
exprSet[,i] <- as.numeric(unlist(exprSet[,i]))
}
fit <- lmFit(exprSet,design)
fit2 <- contrasts.fit(fit, contrast.matrix)
# step2,eBayes()
fit2 <- eBayes(fit2)
# step3,topTable()
tempOutput = topTable(fit2, coef=1, n=Inf)
# 使用coef参数,这里设为1,也就是表示根据上面比较矩阵的第一个比较信息来提取结果,由于这个例子中只有两个分组,所以只会有一个比较信息
# n=Inf:展示的最大值设为无限大,可以不需要该参数
# 参数adjust="BH"表示对p值的校正方法,包括了:"none", "BH", "BY" and "holm",默认为BH,适用与多数情况,所以这边没有修改这个参数。
nrDEG = na.omit(tempOutput) # 删除NA值
# write.csv(nrDEG,"C:/Users/16748/Desktop/limma_notrend.results.csv",quote = F)
# 输出差异分析矩阵