如何使用R语言管理基于代理的建模中的内存

11

我正在使用R构建基于代理的模型,但是由于尝试使用大型对象而出现了内存问题。特别是,在初始化时创建了8个三维数组,并且在每个时间步长中,每个三维数组都由不同的函数填充。

目前,ABM在1825天内运行,并模拟2500个个体在景观上移动。景观中有1000个单元格。使用这种配置,我没有内存问题。

在初始化时,

  • 1 3D array is like:

    h <- array(NA, dim=c(1825, 48, 2500),
               dimnames=list(NULL, NULL, as.character(seq(1, 2500, 1))))
               ## 3th dimension = individual ID
    
  • 1 3D array is like:

    p <- array(NA, dim=c(1825, 38, 1000),
               dimnames=list(NULL, NULL, as.character(seq(1, 1000, 1))))
               ## 3th dimension = cell ID
    
  • 6 3D arrays are like:

    t <- array(NA, dim=c(1825, 41, 2500),
               dimnames=list(NULL, NULL, as.character(seq(1, 2500, 1))))
               ## 3th dimension = individual ID
    

这些数组包含字符/字符串数据类型。

理想情况下,我希望增加个体数量和/或补丁数量,但由于内存问题,这是不可能的。似乎有一些可用的工具,如bigmemorygc来管理内存。这些工具有效吗?我是编程新手,没有管理内存和高性能计算的经验。非常感谢您的建议和时间。

sessionInfo() R版本3.5.3(2019-03-11) 平台:x86_64-w64-mingw32/x64(64位) 运行在:Windows 7 x64(build 7601)Service Pack 1


数组的第二维是什么?个体/单元的属性是什么?你需要将所有时间点保存在内存中,还是可以在每个步骤后将状态保存到文件中,并仅保留当前状态在内存中?(这将使内存需求减少2/1825倍) - Jan van der Laan
很难说,没有看到这些函数中具体发生了什么。也许可以减少维度,因为仅初始化数组就需要大约4GB的内存来填充它们。我不确定bigmemory包是否能处理多维数组,但我也会考虑使用ff包。 - SeGa
@Jan van der Laan非常感谢您的回答。是的,第二维对应于个体的属性。我无法仅在内存中保留当前状态,因为我需要t-1和1-tf-1处的数组值,其中tf是持续时间参数。 - Nell
2个回答

4
据我了解,bigmemory 只能用于矩阵而不能用于多维数组,但是你可以将多维数组保存为矩阵列表。 gc 是垃圾回收器,你不必调用它,因为它会自动被调用,但手册还指出:

在删除大型对象后调用 gc 可能很有用,因为这可能会促使 R 将内存返回给操作系统。

我认为对于您的任务来说最有用的软件包应该是 ff。 下面是一个简短的示例,以说明该软件包的优点: ff 几乎不影响内存,因为它将数据存储在磁盘上使用基本 R 初始化数组:
p <- array(NA, dim=c(1825, 38, 1000),
           dimnames=list(NULL, NULL, as.character(seq(1, 1000, 1))))

format(object.size(p), units="Mb")

"264.6 Mb"

因此,您的初始数组已经占用了将近5GB的内存,这会在进行大量计算时给您带来麻烦。


使用"ff"初始化数组:

library(ff)
myArr <- ff(NA, dim=c(1825, 38, 1000), 
            dimnames=list(NULL, NULL, as.character(seq(1, 1000, 1))),
            filename="arr.ffd", vmode="logical", overwrite = T)

format(object.size(myArr), units="Mb")

[1] "0.1 Mb" (0.1兆字节)


测试相等性:

euqals <- list()
for (i in 1:dim(p)[1]) {
  euqals[[i]] <-  all.equal(p[i,,],
                            myArr[i,,])
}
all(unlist(euqals))

[1] 真


3
你必须坚持使用数组数据类型吗?如果你的数组中有很多NAs,那么这意味着你正在使用比你实际需要的更多的内存。这是R中数组的缺点。如果你执行的操作不一定需要数据成为数组,那么通过将其重新建模为data.frame,你可以节省一些内存。下面的示例展示了从数组转换后数据框可能看起来像什么。请注意,我必须明确地使用na.rm=FALSE,否则结果将是0行数据。
devtools::install_github("Rdatatable/data.table@as.dt.array.null.dimnames")
library(data.table)

p <- array(NA, dim=c(1825, 38, 1000),
           dimnames=list(NULL, NULL, as.character(seq(1, 1000, 1))))
as.data.table(p, na.rm=FALSE)
#             V1    V2     V3  value
#          <int> <int> <char> <lgcl>
#       1:     1     1      1     NA
#       2:     1     1     10     NA
#       3:     1     1    100     NA
#       4:     1     1   1000     NA
#       5:     1     1    101     NA

另一种方法是使用 data.cube 包。在幕后,它基本上会为您完成我上面所写的操作。您仍然可以使用数组的 [ 操作符,但是 data.cube 对象将不适用于期望输入数组的 R 函数,因为它们会将 data.cube 强制转换为数组,从而损失所有内存优势。内存优势可以是显着的,例如在 data.cube vignette 中有介绍:

array: 34.13 GB
data.cube: 0.01 GB

改用矩阵而不是数据框架,效果会更好,不是吗?尺寸几乎相同,但计算速度通常更快。或者直接使用data.table - SeGa
@SeGa 直接使用 data.table :) - jangorecki
1
感谢您的回答。我使用一个数组来保存每个个体的数据(对应于第三维)。然而,我将数组转换为data.frame以在每个时间步骤(第一维)应用函数,并填充数组。我正在尝试使用data.cube包,但我在安装时遇到了问题:Warning in install.packages: unable to access index for repository https://jangorecki.gitlab.io/data.cube/src/contrib: cannot open URL 'https://jangorecki.gitlab.io/data.cube/src/contrib/PACKAGES'.3) - Nell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接