关于读取多个文件和内存管理存在很多问题。我正在寻找可以同时解决这两个问题的信息。
我经常不得不将数据的多个部分读取为单独的文件,将它们重新绑定(bind)到一个数据集中,然后进行处理。到目前为止,我一直在使用如下所示的内容-rbinideddataset <- do.call("rbind", lapply(list.files(), read.csv, header = TRUE))
我担心在每种方法中都可以观察到的那种颠簸。可能是rbindeddataset和not-rbindeddatasets一起存在于内存中的实例,但我还不知道。有人可以确认吗?
有什么方法可以将预分配原则扩展到此类任务?还是有人知道的其他一些技巧可能有助于避免这种情况的发生?我还尝试了rbindlist
而不是lapply
的结果,但这并没有显示出颠簸。这是否意味着rbindlist
足够聪明来处理此问题?
data.table和Base R解决方案优于某些软件包的产品。
根据与@Dwin和@mrip的讨论在2013年10月10日编辑
> library(data.table)
> filenames <- list.files()
>
> #APPROACH 1 #################################
> starttime <- proc.time()
> test <- do.call("rbind", lapply(filenames, read.csv, header = TRUE))
> proc.time() - starttime
user system elapsed
44.60 1.11 45.98
>
> rm(test)
> rm(starttime)
> gc()
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 350556 18.8 741108 39.6 715234 38.2
Vcells 1943837 14.9 153442940 1170.7 192055310 1465.3
>
> #APPROACH 2 #################################
> starttime <- proc.time()
> test <- lapply(filenames, read.csv, header = TRUE)
> test2 <- do.call("rbind", test)
> proc.time() - starttime
user system elapsed
47.09 1.26 50.70
>
> rm(test)
> rm(test2)
> rm(starttime)
> gc()
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 350559 18.8 741108 39.6 715234 38.2
Vcells 1943849 14.9 157022756 1198.0 192055310 1465.3
>
>
> #APPROACH 3 #################################
> starttime <- proc.time()
> test <- lapply(filenames, read.csv, header = TRUE)
> test <- do.call("rbind", test)
> proc.time() - starttime
user system elapsed
48.61 1.93 51.16
> rm(test)
> rm(starttime)
> gc()
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 350562 18.8 741108 39.6 715234 38.2
Vcells 1943861 14.9 152965559 1167.1 192055310 1465.3
>
>
> #APPROACH 4 #################################
> starttime <- proc.time()
> test <- do.call("rbind", lapply(filenames, fread))
> proc.time() - starttime
user system elapsed
12.87 0.09 12.95
> rm(test)
> rm(starttime)
> gc()
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 351067 18.8 741108 39.6 715234 38.2
Vcells 1964791 15.0 122372447 933.7 192055310 1465.3
>
>
> #APPROACH 5 #################################
> starttime <- proc.time()
> test <- do.call("rbind", lapply(filenames, read.csv, header = TRUE))
> proc.time() - starttime
user system elapsed
51.12 1.62 54.16
> rm(test)
> rm(starttime)
> gc()
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 350568 18.8 741108 39.6 715234 38.2
Vcells 1943885 14.9 160270439 1222.8 192055310 1465.3
>
>
> #APPROACH 6 #################################
> starttime <- proc.time()
> test <- rbindlist(lapply(filenames, fread ))
> proc.time() - starttime
user system elapsed
13.62 0.06 14.60
> rm(test)
> rm(starttime)
> gc()
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 351078 18.8 741108 39.6 715234 38.2
Vcells 1956397 15.0 128216351 978.3 192055310 1465.3
>
>
> #APPROACH 7 #################################
> starttime <- proc.time()
> test <- rbindlist(lapply(filenames, read.csv, header = TRUE))
> proc.time() - starttime
user system elapsed
48.44 0.83 51.70
> rm(test)
> rm(starttime)
> gc()
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 350620 18.8 741108 39.6 715234 38.2
Vcells 1944204 14.9 102573080 782.6 192055310 1465.3
不出所料,节省时间最多。但是,方法4,6和7显示了最小的内存开销,我不确定为什么。
最佳答案
好像rbindlist
一次分配了内存并构造了新的数据帧,而do.call(rbind)
一次将添加一个数据帧,并在每次复制它。结果是rbind
方法的运行时间为O(n^2)
,而rbindlist
的运行时间为线性时间。另外,rbindlist
应该避免内存增加,因为在每次迭代或n
迭代期间不必分配新的数据帧。
一些实验数据:
x<-data.frame(matrix(1:10000,1000,10))
ls<-list()
for(i in 1:10000)
ls[[i]]<-x+i
rbindtime<-function(i){
gc()
system.time(do.call(rbind,ls[1:i]))[3]
}
rbindlisttime<-function(i){
gc()
system.time(data.frame(rbindlist(ls[1:i])))[3]
}
ii<-unique(floor(10*1.5^(1:15)))
## [1] 15 22 33 50 75 113 170 256 384 576 864 1297 1946 2919 4378
times<-Vectorize(rbindtime)(ii)
##elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed
## 0.009 0.014 0.026 0.049 0.111 0.209 0.350 0.638 1.378 2.645
##elapsed elapsed elapsed elapsed elapsed
## 5.956 17.940 30.446 68.033 164.549
timeslist<-Vectorize(rbindlisttime)(ii)
##elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed
## 0.001 0.001 0.001 0.002 0.002 0.003 0.004 0.008 0.009 0.015
##elapsed elapsed elapsed elapsed elapsed
## 0.023 0.031 0.046 0.099 0.249
rbindlist
不仅速度更快(尤其是对于长输入而言),而且运行时间仅线性增加,而do.call(rbind)
大约二次增加。我们可以通过对每个时间拟合对数-对数线性模型来确认这一点。> lm(log(times) ~ log(ii))
Call:
lm(formula = log(times) ~ log(ii))
Coefficients:
(Intercept) log(ii)
-9.73 1.73
> lm(log(timeslist) ~ log(ii))
Call:
lm(formula = log(timeslist) ~ log(ii))
Coefficients:
(Intercept) log(ii)
-10.0550 0.9455
因此,从实验上讲,
do.call(rbind)
的运行时间随n^1.73
的增长而增长,而rbindlist
则是线性的。关于r - 读取多个文件时的内存管理,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19164120/