今天我想要给ChAMP写一个import程序,因为目前大部分DNA Methylation领域的研究软件都是给予minfi程序提供的读取IDAT文件的程序,但是最近minfi似乎崩溃了,整个研究领域都快挂了……所以最好还是有自己的一条“供应链”
首先,甲基化分析需要对应的注释文件,主流是EPIC和450K的,我先分析450K的Manifest,首先原有的Manifest包含了从BeadChip到最终的文件的对应号,但是有一部分信息应该要提前过滤掉:一部分是开头的Header,另一部分是结尾的Control Probe
从illumina官网下载到对应的450K注释文件,打开是这样子的……(切记不要随便点开,你的电脑可能会挂掉)
如果把Header,Control Probe和SNP全部删掉,450K数据的行数正好就是:485512。这就是450K甲基化注释的所有Probe数量,每一个Probe对应一个CpG位点。不是说人体的全基因组上只有这写位点,而是说illumina公司决定只将这些位点涉及到芯片中完成测序。
值得注意的是,最后的Control Probe还是挺重要的,主要是用来评估测序质量,只是我目前没有太多的涉及那些领域,有时间我还是应该认真了解一下。
上图中有几列是很重要的:
AddressA_ID与AddressB_ID是对应的CpG ID和芯片数据位点。
后边的颜色是针对Type-I Probe的,监狱Type-I Probe是通过两种颜色测量出来的数据。
Infinium_Design_Type是用来指示Type-I和Type-II Probe的东西。
基本上就是需要上述的三列信息,将芯片的颜色数据,转换为可以分析的beta或者M数据。
开始写import程序:
首先需要做的是载入phenotype文件,该文件包含了每一个样本对应的芯片号,需要那个文件,程序才能知道什么样本对应了那两片芯片。
pd <- read.csv(csvfile,skip=which(substr(readLines(csvfile),1,6) == "[Data]"),stringsAsFactor=FALSE,head=TRUE)
if("Sentrix_Position" %in% colnames(pd))
{
colnames(pd)[which(colnames(pd)=="Sentrix_Position")] <- "Array"
message("<< Replace Sentrix_Position into Array>>")
} else
{
message("Your pd file contains NO Array(Sentrix_Position) information.")
}
if("Sentrix_ID" %in% colnames(pd))
{
colnames(pd)[which(colnames(pd)=="Sentrix_ID")] <- "Slide"
message("<< Replace Sentrix_ID into Slide>>")
} else
{
message("Your pd file contains NO Slide(Sentrix_ID) information.")
}
很无语的一点就是,minfi之前强行将“Sentrix_Position”命名为“Array”,将“Sentrix_ID”命名为“Slide”,搞得我只能这样做。
IDAT文件是illumina的测序仪直接输出的文件,不能用文本编辑器打开的,所以只能用illumina公司提供的illuminaio
R包才能读取,我也没有办法,其实代码很简单:
G.idats <- lapply(GrnPath, function(x){ message("Loading:",x);readIDAT(x)})
R.idats <- lapply(RedPath, function(x){ message("Loading:",x);readIDAT(x)})
这样,就把所有的绿色芯片和红色芯片读进来了,完成了这一步,整个DNA Methylation研究领域就完成了从血淋淋地细胞湿实验,到纯数据的干实验的转换,这也是分子生物学和计算生物学结合的地方。
这两个矩阵分别表征着数据中,被甲基化和没有被甲基化的比例,人体内的甲基化数值其实就是一个比例,加入测序了100条序列,其上边有一个CpG位点,如果有90%都有被甲基化,10%没有,那么就是0.9(其实不是,具体计算公式更详细一些,但是大致是这个)。M 矩阵就是表征被甲基化
的矩阵,而U矩阵就是表征未被甲基化
的矩阵。
非常重要的部分就是,CpG ID与芯片位置是如何对应的,在研究以后,我才知道,这其中的对应关系极为复杂:
Type_II <- AnnoLoader450K[which(AnnoLoader450K$Infinium_Design_Type=="II"),]
Type_I.Red <- AnnoLoader450K[which(AnnoLoader450K$Infinium_Design_Type == "I" & AnnoLoader450K$Color_Channel=="Red"),]
Type_I.Grn <- AnnoLoader450K[which(AnnoLoader450K$Infinium_Design_Type == "I" & AnnoLoader450K$Color_Channel=="Grn"),]
M.name <- c(Type_II[,1],Type_I.Red[,1],Type_I.Grn[,1])
M.index <- c(paste("G",Type_II[,2],sep="-"),paste("R",Type_I.Red[,3],sep="-"),paste("G",Type_I.Grn[,3],sep="-"))
U.index <- c(paste("R",Type_II[,2],sep="-"),paste("R",Type_I.Red[,2],sep="-"),paste("G",Type_I.Grn[,2],sep="-"))
可以看出,M矩阵和U矩阵都包含了三个部分:Type-II的芯片,Type-I的红色芯片,Type-I的绿色芯片,三种芯片对应的数据值位置是不一样的,比如说,Type-II芯片,绿色的Type-II芯片的AddressA就是M,但是对于Type-I的红色芯片,就是红色的Type-I红色芯片的AddressB对应M,而红色的Type-I红色芯片的AddressA对应U。
这一过程简直混乱不堪……我不知道各种具体原因是什么,但Type-I和Type-II技术是illumina公司推出过的两种技术,两种技术居然交叉在一张芯片里,实在是让人匪夷所思!
这一部分其实很简单,公式就一行:
beta.value <- M / (M + U + offset)
那个offset是用来防止当M和U都太小,导致分母是0导致数据不正确。
到目前位置,整个从illuminaIDAT文件中提取beta数据的过程就完成了。当然还有很多工作需要做,比如说,需要继续写程序读取detect P value以及intensity等等。