使用索引

时间:2018-02-09 10:30:15

标签: r performance data.table

对问题的一点介绍:

我正在开发一个生态生理学模型,我使用一个名为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)

结果: Autoplot from Microbenchmark

如果有人有编程模型的经验,我对模型开发的任何建议都非常感兴趣。

1 个答案:

答案 0 :(得分:1)

我阅读了更多关于这个问题的内容,我将在这里写一些关于其他帖子提出的解决方案的繁荣。

显然,在尝试减少按索引分配给data.frame的计算时间时,阅读值得考虑。 这些消息来源都在其他讨论中找到:

几种解决方案似乎相关:

  1. 如果可能,请使用matrix而不是data.frame来利用就地修改(Advanced R)
  2. 使用list代替data.frame,因为[<-.data.frame不是原始函数(Advanced R)
  3. 用C ++编写函数并使用Rcppfrom this source
  4. 使用.subset2代替[third source
  5. 根据@JulienNavarre和@ Emmanuel-Lin以及不同来源的推荐使用data.table,如果使用{{1},请set使用data.frame:=不是问题。
  6. 尽可能使用data.table代替[[(仅按一个值索引)。这个不是非常有效,而且非常严格,所以我从以下比较中删除了它。
  7. 以下是使用不同解决方案的性能分析:

    代码:

    [

    由此产生的自动映射:

    Autoplot

    # 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代替matrixdata.frame使用f3.subset2matrix使用f4list.subset2使用两个f5(包括阅读和写作),list使用f6data.table::set使用f7data.table::set获取累积金额,data.table使用f8 data.table

    我们可以看到最佳解决方案是使用列表进行读写。令人惊讶的是,:=是最糟糕的解决方案。我相信我做错了,因为它应该是最好的。如果你能改进它,请告诉我。