我正在开发一个生态生理学模型,我使用一个名为S
的参考类列表来存储模型输入/输出所需的每个对象(例如,meteo,生理参数等...)。
此列表包含5个对象(请参见下面的示例):
- 两个数据帧,S$Table_Day
(模型的输出)和S$Met_c
(输入中的meteo),它们在列中都有变量,在行中有观察(输入或输出)。
- 参数列表S$Parameters
- 矩阵
- 矢量
该模型以每日时间步长运行许多功能。每天在从第一天i = 1到最后一天i = n的for循环中计算。此列表将传递给通常从输入中的S$Met_c
和/或S$Parameters
获取数据的函数,并使用索引(第i天)计算存储在S$Table_Day
中的内容。 S
是一个引用类列表,因为它们避免了修改时的复制,考虑到计算次数,这是非常重要的。
由于模型非常慢,我试图通过对不同解决方案进行微观基准测试来缩短计算时间 今天我在比较两种存储数据的解决方案时发现了一些令人惊讶的事情通过索引在一个预分配的数据帧中存储数据比将其存储到未声明的矢量中要长。在阅读this之后,我认为预分配内存总是更快,但似乎R在通过索引修改时执行更多操作(可能比较长度,类型等...)。
我的问题是:有没有更好的方法来执行此类操作?换句话说,有没有办法让我更有效地使用/存储输入/输出(在data.frame,矢量列表或其他),以跟踪每一天的所有计算?例如,最好使用多个向量(每个变量一个),然后在更复杂的对象(例如数据帧列表)中重新组合它们吗?
顺便说一下,我是否正确使用引用类来避免复制S
中的大对象,同时将其传递给函数并从中修改它们?
比较的可重复示例:
SimulationClass <- setRefClass("Simulation",
fields = list(Table_Day = "data.frame",
Met_c= "data.frame",
PerCohortFruitDemand_c="matrix",
Parameters= "list",
Zero_then_One="vector"))
S= SimulationClass$new()
# Initializing the table with dummy numbers :
S$Table_Day= data.frame(one= 1:10000, two= rnorm(n = 10000), three= runif(n = 10000),Bud_dd= rep(0,10000))
S$Met_c= data.frame(DegreeDays= rnorm(n=10000, mean = 10, sd = 1))
f1= function(i){
a= cumsum(S$Met_c$DegreeDays[i:(i-1000)])
}
f2= function(i){
S$Table_Day$Bud_dd[(i-1000):i]= cumsum(S$Met_c$DegreeDays[i:(i-1000)])
}
res= microbenchmark(f1(1000),f2(1000),times = 10000)
autoplot(res)
如果有人有编程模型的经验,我对模型开发的任何建议都非常感兴趣。
答案 0 :(得分:1)
我阅读了更多关于这个问题的内容,我将在这里写一些关于其他帖子提出的解决方案的繁荣。
显然,在尝试减少按索引分配给data.frame的计算时间时,阅读和值得考虑。 这些消息来源都在其他讨论中找到:
几种解决方案似乎相关:
matrix
而不是data.frame来利用就地修改(Advanced R)。list
代替data.frame,因为[<-.data.frame
不是原始函数(Advanced R)。Rcpp
(from this source).subset2
代替[
(third source)data.table
,如果使用{{1},请set
使用data.frame
或:=
不是问题。data.table
代替[[
(仅按一个值索引)。这个不是非常有效,而且非常严格,所以我从以下比较中删除了它。以下是使用不同解决方案的性能分析:
[
# Loading packages :
library(data.table)
library(microbenchmark)
library(ggplot2)
# Creating dummy data :
SimulationClass <- setRefClass("Simulation",
fields = list(Table_Day = "data.frame",
Met_c= "data.frame",
PerCohortFruitDemand_c="matrix",
Parameters= "list",
Zero_then_One="vector"))
S= SimulationClass$new()
S$Table_Day= data.frame(one= 1:10000, two= rnorm(n = 10000), three= runif(n = 10000),Bud_dd= rep(0,10000))
S$Met_c= data.frame(DegreeDays= rnorm(n=10000, mean = 10, sd = 1))
# Transforming data objects into simpler forms :
mat= as.matrix(S$Table_Day)
Slist= as.list(S$Table_Day)
Metlist= as.list(S$Met_c)
MetDT= as.data.table(S$Met_c)
SDT= as.data.table(S$Table_Day)
# Setting up the functions for the tests :
f1= function(i){
S$Table_Day$Bud_dd[i]= cumsum(S$Met_c$DegreeDays[i])
}
f2= function(i){
mat[i,4]= cumsum(S$Met_c$DegreeDays[i])
}
f3= function(i){
mat[i,4]= cumsum(.subset2(S$Met_c, "DegreeDays")[i])
}
f4= function(i){
Slist$Bud_dd[i]= cumsum(.subset2(S$Met_c, "DegreeDays")[i])
}
f5= function(i){
Slist$Bud_dd[i]= cumsum(Metlist$DegreeDays[i])
}
f6= function(i){
set(S$Table_Day, i=as.integer(i), j="Bud_dd", cumsum(S$Met_c$DegreeDays[i]))
}
f7= function(i){
set(S$Table_Day, i=as.integer(i), j="Bud_dd", MetDT[i,cumsum(DegreeDays)])
}
f8= function(i){
SDT[i,Bud_dd := MetDT[i,cumsum(DegreeDays)]]
}
i= 6000:6500
res= microbenchmark(f1(i),f3(i),f4(i),f5(i),f7(i),f8(i), times = 10000)
autoplot(res)
引用基础分配,f1
使用f2
代替matrix
,data.frame
使用f3
和.subset2
,matrix
使用f4
和list
,.subset2
使用两个f5
(包括阅读和写作),list
使用f6
,data.table::set
使用f7
和data.table::set
获取累积金额,data.table
使用f8
data.table
。
我们可以看到最佳解决方案是使用列表进行读写。令人惊讶的是,:=
是最糟糕的解决方案。我相信我做错了,因为它应该是最好的。如果你能改进它,请告诉我。