gpt4 book ai didi

r - 以节省内存的方式增长 data.frame

转载 作者:IT王子 更新时间:2023-10-28 23:28:32 34 4
gpt4 key购买 nike

根据Creating an R dataframe row-by-row ,使用 rbind 附加到 data.frame 并不理想,因为它每次都会创建整个 data.frame 的副本。如何在 R 中累积数据,从而生成 data.frame 而不会产生这种惩罚?中间格式不需要是 data.frame

最佳答案

第一种方法

我尝试访问预先分配的 data.frame 的每个元素:

res <- data.frame(x=rep(NA,1000), y=rep(NA,1000))
tracemem(res)
for(i in 1:1000) {
res[i,"x"] <- runif(1)
res[i,"y"] <- rnorm(1)
}

但是 tracemem 变得疯狂(例如 data.frame 每次都被复制到一个新地址)。

替代方法(也不起作用)

一种方法(不确定它是否更快,因为我还没有进行基准测试)是创建一个 data.frames 列表,然后将它们stack在一起:

makeRow <- function() data.frame(x=runif(1),y=rnorm(1))
res <- replicate(1000, makeRow(), simplify=FALSE ) # returns a list of data.frames
library(taRifx)
res.df <- stack(res)

不幸的是,在创建列表时,我认为您将很难预先分配。例如:

> tracemem(res)
[1] "<0x79b98b0>"
> res[[2]] <- data.frame()
tracemem[0x79b98b0 -> 0x71da500]:

换句话说,替换列表中的元素会导致列表被复制。我假设整个列表,但它可能只是列表中的那个元素。我对 R 的内存管理的细节不是很熟悉。

可能是最好的方法

与当今许多速度或内存受限的进程一样,最好的方法可能是使用 data.table 而不是 data.frame。由于 data.table:= 赋值操作符,它可以更新而无需重新复制:

library(data.table)
dt <- data.table(x=rep(0,1000), y=rep(0,1000))
tracemem(dt)
for(i in 1:1000) {
dt[i,x := runif(1)]
dt[i,y := rnorm(1)]
}
# note no message from tracemem

但正如@MatthewDowle 指出的那样,set() 是在循环中执行此操作的适当方法。这样做会更快:

library(data.table)
n <- 10^6
dt <- data.table(x=rep(0,n), y=rep(0,n))

dt.colon <- function(dt) {
for(i in 1:n) {
dt[i,x := runif(1)]
dt[i,y := rnorm(1)]
}
}

dt.set <- function(dt) {
for(i in 1:n) {
set(dt,i,1L, runif(1) )
set(dt,i,2L, rnorm(1) )
}
}

library(microbenchmark)
m <- microbenchmark(dt.colon(dt), dt.set(dt),times=2)

(结果如下)

基准测试

循环运行 10,000 次,数据表几乎快了一个数量级:

Unit: seconds
expr min lq median uq max
1 test.df() 523.49057 523.49057 524.52408 525.55759 525.55759
2 test.dt() 62.06398 62.06398 62.98622 63.90845 63.90845
3 test.stack() 1196.30135 1196.30135 1258.79879 1321.29622 1321.29622

benchmarks

:=set() 的比较:

> m
Unit: milliseconds
expr min lq median uq max
1 dt.colon(dt) 654.54996 654.54996 656.43429 658.3186 658.3186
2 dt.set(dt) 13.29612 13.29612 15.02891 16.7617 16.7617

请注意,这里的 n 是 10^6 而不是 10^5,如上面绘制的基准所示。因此,工作量增加了一个数量级,结果以毫秒而不是秒为单位。确实令人印象深刻。

关于r - 以节省内存的方式增长 data.frame,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11486369/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com