有很多关于阅读多个文件和内存管理的问题。我正在寻找能够解决这两个问题的信息。
我经常需要将多个数据部分作为单独的文件读取,将它们绑定到一个数据集中然后进行处理。直到现在我一直在使用类似下面的东西 -
rbinideddataset <- do.call("rbind", lapply(list.files(), read.csv, header = TRUE))
我担心在每种方法中都可以观察到这种情况。这可能是rbindeddataset和not-yet-rbindeddatasets一起存在于内存中的实例,但我不太清楚。有人可以证实这一点吗?
我是否可以通过某种方式将预分配原则扩展到此类任务?还是其他人知道的其他技巧可能有助于避免碰撞?我还对rbindlist
的结果尝试了lapply
,但没有显示出碰撞。这是否意味着rbindlist
足够智能来处理这个问题?
data.table和Base R解决方案优于某些软件包的产品。
根据与@Dwin和@mrip
的讨论编辑于2013年10月7日编辑> 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显示了最小的内存开销,我并不完全确定原因。
答案 0 :(得分:4)
看起来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
则是线性的。
答案 1 :(得分:2)
试试这个:
require(data.table)
system.time({
test3 <- do.call("rbind", lapply(filenames, fread, header = TRUE))
})
你提到了预分配。 fread
确实有一个'nrows'参数,但如果您事先知道行数(因为它会自动为您自动计算行数,这非常快),它不会加快其操作速度)。