单细胞组学数据分析接触到的项目大都使用压缩文件(rds,txt,tsv,csv,mtx)进行数据存储。有时候,我们会操作相当大的压缩数据对象,而超大型的数据集如(一个包含约 100 万个细胞和约 3 万个基因的表达矩阵)在进行数据类型转换等处理的时候会遇到异常 Error in asMethod(object) : Cholmod error 'problem too large' ,指的是其中 as.matrix()
转换 Dense矩阵,导致内存溢出。这个问题意味着处理数据的维度超过 as.matrix()
支持的最大元素容量 $(2147483647 (2^{31}-1))$。
本文提出一种在R里面将超大数据集切块后用
as.matrix()
转换,再转换超大数据框和稀疏矩阵方法(as.spM_DF
,as.DF_spM
)。在R中 Chunk数据是一种有效处理大型数据集的方法,该方法虽然一定程度上牺牲了一些效率,但能有效避免内存溢出、程序崩溃等严重问题。
0、测试数据生成
getData <- function(n=100,is.str=FALSE,data.class="data.frame"){
data.retro = data.frame(
value = ceiling(runif(n = n,min = -1,max = 1)),
norm = rnorm(n=n,mean=10,sd=1),
expr = rexp(n),
No = seq(n)
)
if (is.str) {data.retro = cbind(data.retro,strings = stringi::stri_rand_strings(n, 5, pattern = "[A-Za-z0-9]"))}
switch (data.class,
data.frame = {data.frame},
matrix = {as.matrix(data.frame)}
)
return(data.retro)
}
1、 超大型稀疏矩阵转换出数据框
as.spM_DF <- function(data.use,chun_size=20000000) {
lapply( split(seq(nrow(data.use)), (seq(nrow(data.use))-1) %/% as.numeric(chun_size) ) , function(nx) {
if ( is.null(nrow(data.use[nx,])) ) {
as.data.frame(t(as.matrix(data.use[nx,])))
}else{
as.data.frame(as.matrix(data.use[nx,]))
}
}) %>% dplyr::bind_rows()
}
方法调用: as.spM_DF(as(as.matrix(getData(100)),"dgCMatrix"))
2、 超大型数据框转换出稀疏矩阵
as.DF_spM <- function(data.use,chun_size="20000000",sparseClass="dgCMatrix") {
lapply( split(seq(nrow(data.use)), (seq(nrow(data.use))-1) %/% as.numeric(chun_size) ) , function(nx) {
switch(sparseClass,
dgTMatrix = {as(as.matrix(data.use[nx,]),"dgTMatrix")},
dgCMatrix = {as(as.matrix(data.use[nx,]),"dgCMatrix")},
dgRMatrix = {as(as.matrix(data.use[nx,]),"dgRMatrix")},
stop("Enter one of the three types of sparse matrix(\"dgTMatrix\",\"dgCMatrix\",\"dgRMatrix\")")
)
}) %>% Matrix.utils::rBind.fill()
}
方法调用 : as.DF_spM(getData(100),sparseClass = "dgTMatrix")
3、 分块写出超大型数据框
write_big_DF <- function(data.use,chun_size="20000000",file = "text.tsv",sep="\t",col.names=F,row.names=T,quote=F){
### first write colnames
if (col.names) {write.table(x = data.use[0,],file = file,sep = sep,quote = quote,append = F,row.names = row.names,col.names = T)}
lapply( split(seq(nrow(data.use)), (seq(nrow(data.use))-1) %/% as.numeric(chun_size) ) , function(nx) {
### append content
write.table(x = data.use[nx,],file = file,sep = sep,quote = quote,append = T,row.names = row.names,col.names = F)
}) -> . ;rm(.)
}
方法调用 :write_big_DF(getData(10,is.str = T),chun_size = 3,file = "text.tsv",col.names=T,row.names = F)