1. 套件名称:
bigmemory 4.5.19
2. 套件主要用途:
处理较吃内存的资料,尤其当资料大小逼近或超过实体内存,使load资料很慢时..
但资料限定为单一资料型态,不能同时混杂character, numeric
它这种资料结构 big.matrix 其实只是一个 R object但实际指向 C++资料结构的指标
可以memory或档案形式 share (shared.big.matrix, filebacked.big.matrix),实现
在 multip processes or cluster共用的机制
可以做简单的资料操作,如取符合条件的子集合资料出来
配合其他 big 系列的套件如 biganalytics, bigtabultae等做其他处理、modeling
3. 套件主要函数列表:
a. read.big.matrix: 读取一份.csv 并创建成file-backing形式的big.matrix
格式比如
read.big.matrix("test.csv", header = TRUE,
type = "double",
backingfile = "test.bin",
descriptorfile = "test.des"))
你提供test.csv, 执行后会多两个供bigmemory使用的descriptorfile .bin, .des
b. attach.big.matrix: 读取一份 file-backing big.matrix的descriptor file,
提供套件可以抓到这个 big.matrix object所需的资讯
c. mwhich: 如同base所提供的which,可以对各字段做筛选
d. write.big.matrix: 将 big.matrix object写入 file
4. 分享内容:
之前看一些朋友发问有较大容量资料要吃进来,而绝大部分都可以由data.table套件的
fread解决。
bigmemory处理资料当然没有data.table又快又方便,但它有个好处,就是一开始只放
资料的内存指标,不会把所有资料都放进内存。
所以我把它应用在网络server上需要供人查询的较大笔资料(如shiny建构的查询接口)
资料本身较少更动,而供公众使用的linux server资源不多(有时VM只开4GB)
当我把资料备妥(.csv),先建好file-backing方式所需要的descriptor file,
之后只要attach上去,资料就可以在web-based application中读取到。
使用者以接口查询、筛选资料范围,透过 mwhich 方式缩小真正加载内存的资料大小,
再转换到data.table做其他运算。
所以我用bigmemory的方式、函数超简单:attach %>% mwhich (%>% data.table())
这也能用数据库完成,但上述流程可能比 (connect Database -> SQL query -> return
query)来得快一点(后有简单测试)
但如果资料本身常常更新,或资料各字段型态复杂,数据库有它难以取代的优势。
bigmemory也可以 write, flush,但我本身很少用它,我主要应用在一份很大的历史资料
(数值资料,少更动),这当然仅只是个人选择。
bigmemory另一个不错的优点也在于它和Rcpp(, RcppArmadillo..)等的配合,比如这个
简单清楚的例子
http://www.r-bloggers.com/using-rcpparmadillo-with-bigmemory/
其他应用或参考资料 可在R-blogger上搜寻 bigmemory
另外google 这份文件也颇有参考价值,虽然已是2010年...
Taking R to the Limit, Part II: Working with Large Datasets
我之前曾对data.table, bigmemory, 和PostgreSQL做简单的测试,以下是部分内容(可run
library(data.table)
library(magrittr)
library(bigmemory)
library(RPostgreSQL)
library(microbenchmark)
# ========================== Test data preparation
tstL <- 1e6
wr.first <- TRUE
bigmf<- file("bigm_sample01.csv", open = "w")
DT <- data.table(lat=numeric(), lng=numeric(), date=as.Date(character()),
grp=numeric(), pick=numeric())
idxf <- function(x, idx) {
x[-idx] <- 0; x[idx] <- 1; return(x)
}
pconn <- dbConnect(dbDriver("PostgreSQL"), # change to your configuration
user="xx", password="xx", dbname="xxx", host="localhost")
print("write big.mat start")
print(format(Sys.time(), "%Y%m%d %H:%M:%S"))
# randomly prepare data, can arbitrarily chang loop iteration
for (i in 1:20) {
dt <- data.table(lat = runif(tstL,0,90),lng = runif(tstL,0,180),
yr = as.integer(runif(tstL,2000,2015)),
mo = as.integer(runif(tstL,1,12)),
day = as.integer(runif(tstL,1,28))) %>%
.[,date:=as.Date(paste(yr,mo,day,sep=" "),"%Y %m %d")] %>%
.[,c("yr","mo","day","grp"):=list(NULL,NULL,NULL,i)] %>% setkey(date) %>%
.[,pick:=idxf(seq_along(lat),sample(seq_along(lat),1)), by=.(date)]
print(format(Sys.time(), "%Y%m%d %H:%M:%S"))
print("combine DT")
DT <- rbindlist(list(DT, dt))
print(format(Sys.time(), "%Y%m%d %H:%M:%S"))
print("write to PostgreSQL")
dbWriteTable(pconn, value=dt, name= "bigmdb", append=!wr.first, row.names=F)
print(format(Sys.time(), "%Y%m%d %H:%M:%S"))
print("write.table")
# Big.matrix cannot have mixed-type data, change charater 'date' to int 'datei'
write.table(dt[,datei:=as.integer(gsub("-","",date))] %>% .[,date:=NULL],
file = bigmf, sep = ",", row.names = FALSE, col.names = wr.first)
print(i)
wr.first <- FALSE
}
close(bigmf)
#========================== ? read.big.matrix
system.time(db <- read.big.matrix("bigm_sample01.csv", header = TRUE,
type = "double",
backingfile = "bigm_sample01.bin",
descriptorfile = "bigm_sample01.des"))
# user system elapsed
# 69.21 3.21 72.54
#========================== ? attach.big.matrix
system.time(db <- dget("bigm_sample01.des") %>% attach.big.matrix())
# user system elapsed
# 0.01 0.00 0.01
nrow(db) ## 2e+07 rows
nrow(DT)
#========================== Indexing PostgreSQL data
dbSendQuery(pconn, "CREATE INDEX date_index ON bigmdb USING btree (date)")
#========================== simple benchmark
microbenchmark(
'DT' = DT[date>='2003-01-01' & date <='2014-12-01' & pick==1,],
'Bigm' = db[mwhich(db,c(5,5,4),list(c(20030101,20141201,1)),
list(c('ge','le','eq')),'AND'),],
'SQL'= dbGetQuery(pconn, statement=paste0("SELECT * FROM bigmdb WHERE date >=
'2003-01-01' AND date <= '20141201'
AND pick = 1;")),
times=10
) ######## Note: SQL statement should be in single line ######################
#Unit: milliseconds
#expr min lq mean median uq max neval
# DT 325.0868 347.0721 367.5553 360.3873 389.9555 440.2354 10
#Bigm 404.6935 416.9812 452.5594 441.7622 462.6059 591.9303 10
# SQL 3221.8961 3226.8496 3303.9357 3274.3635 3377.8906 3521.6359 10
format(object.size(DT),"Mb")
#[1] "762.9 Mb"
format(object.size(out2),"Mb")
#[1] "2.7 Mb"
lapply(dbListConnections(PostgreSQL()), dbDisconnect)
5. 备注
没有特别分享到什么bigmemory套件高深功能,也不是来骗钱的XDD 只是抛砖引玉,
自己也有困惑~~ 也许data字段不能mixed-type 某方面侷限了它的发展,bigmemory
在网络的讨论度很低,但套件作者默默在维持、不时小更新,只是未来的发展走向不明。
不知道它在愈来愈多更快、更方便的资料处理套件选择下,未来性如何??~~
bigmemory只是R资料处理中的其一选择,小小心得供参,也请多多指教,更希望引来
有趣的其他应用或使用方式,谢谢