R-需要帮助加速for循环吗

R-需要帮助加速for循环吗,r,for-loop,dataframe,vectorization,data.table,R,For Loop,Dataframe,Vectorization,Data.table,我有两个数据帧;其中一个有48行长,如下所示: name=Z31 Est.Date Site Cultivar Planting 1 24/07/2011 Birchip Axe 1 2 08/08/2011 Birchip Bolac 1 3 24/07/2011 Birchip Derrimut 1 4 12/08/2011 Birchip Eaglehawk 1 5 29/07/201

我有两个数据帧;其中一个有48行长,如下所示:

name=Z31

     Est.Date   Site    Cultivar   Planting
1   24/07/2011  Birchip Axe           1
2   08/08/2011  Birchip Bolac         1
3   24/07/2011  Birchip Derrimut      1
4   12/08/2011  Birchip Eaglehawk     1
5   29/07/2011  Birchip Gregory       1
6   29/07/2011  Birchip Lincoln       1
7   23/07/2011  Birchip Mace          1
8   29/07/2011  Birchip Scout         1
9   17/09/2011  Birchip Axe           2
10  19/09/2011  Birchip Bolac         2
另一个大于23000行,包含来自模拟器的输出。看起来是这样的:

name=pred

    Date        maxt    mint    Cultivar    Site    Planting    tt  cum_tt
1   5/05/2011   18       6.5    Axe        Birchip  1        12.25  0
2   6/05/2011   17.5     2.5    Axe        Birchip  1        10     0
3   7/05/2011   18       2.5    Axe        Birchip  1        10.25  0
4   8/05/2011   19.5       2    Axe        Birchip  1        10.75  0
5   9/05/2011   17       4.5    Axe        Birchip  1        10.75  0
6   10/05/2011  15.5    -0.5    Axe        Birchip  1        7.5    0
7   11/05/2011  14       5.5    Axe        Birchip  1        9.75   0
8   12/05/2011  19         8    Axe        Birchip  1        13.5   0
9   13/05/2011  18.5     7.5    Axe        Birchip  1        13     0
10  14/05/2011  16       3.5    Axe        Birchip  1        9.75   0
我想做的是,只有当pred DF中的日期等于或大于Z31 est.date时,cum_tt列才开始将当前行的tt列添加到前一行的cum_tt(累积加法)。我为循环编写了以下内容:

for (i in 1:nrow(Z31)){
  for (j in 1:nrow(pred)){
    if (Z31[i,]$Site == pred[j,]$Site & Z31[i,]$Cultivar == pred[j,]$Cultivar & Z31[i,]$Planting == pred[j,]$Planting &
        pred[j,]$Date >= Z31[i,]$Est.Date)
    {
      pred[j,]$cum_tt <- pred[j,]$tt + pred[j-1,]$cum_tt
    }
  }
}
更新

谢谢大家的帮助。我还不熟悉向量方法,我无法及时实现一些更复杂的解决方案。我有一些时间下面的方式潜艇建议。现在已经足够快了,可以做我需要的事情了。这些数字以秒为单位,用于Z对P的一次迭代

我的路:59.77

潜艇:14.62


SUB使用数字日期:11.12

快速解决方案是将循环外的向量定义为:

 temp_cumtt=c(rep(0,nrow(pred)))
然后用这个:

if (Z31[i,2] == pred[j,5] & Z31[i,3] == pred[j,4] & Z31[i,4] == pred[j,6] & pred[j,1] >= Z31[i,1]){
   temp_cumtt[j]=pred[j,7] + pred[j-1,8]
}
而不是直接更新data.frame列

退出循环后,可以更新列:

 pred$cum_tt = temp_cumtt  
另一件事是,在使用索引为
j
j-1
时,从
1
开始,必须小心。在您的示例中,它不会导致条件问题

编辑:

现在看看你的数据格式,我有以下建议

1) 不要将其转换为
Date
类,而是将其作为值的向量

2) 根据日期向量对数据帧进行排序:
Z31=Z31[带有(Z31,顺序(-Date)),]
(注意,由于您希望比较pred[,Date index]>=Z31[,Date index],因此按降序排列)

3) 将第一个循环用作
pred
。首先取pred->
pred[i,1]
的日期,尝试进行二进制排序,找出它在Z31中满足的索引,然后从该索引开始向下搜索列表。如果
Date
条件满足,则检查其余条件,并像以前一样填写
temp\u cumt[i]


这应该非常快(因为二进制排序只对48行
Z31
进行,您可以将运行时间与其他解决方案进行比较。

确保我们可以在几秒钟内完成此操作…我的第一个答案在这里,所以请轻点

## first make sure we have dates in a suitable format for comparison
## by using strptime, creating the columns estdate_tidy and date_tidy
## in Z31 and pred respectively

Z31$estdate_tidy = strptime(as.character(Z31$Est.Date), "%d/%m/%Y")
pred$date_tidy = strptime(as.character(pred$Date), "%d/%m/%Y")

## now map the estdate_tidy column over to pred using the match command -
## Z31_m and pred_m are dummy variables that hopefully make this clear

Z31_m = paste(Z31$Site, Z31$Cultivar, Z31$Planting)
pred_m = paste(pred$Site, pred$Cultivar, pred$Planting)
pred$estdate_tidy = Z31$estdate_tidy[match(pred_m, Z31_m)]

## then define a ttfilter column that copies tt, except for being 0 when
## estdate_tidy is after date_tidy (think this is what you described)

pred$ttfilter = ifelse(pred$date_tidy >= pred$estdate_tidy, pred$tt, 0)

## finally use cumsum function to sum this new column up (looks like you
## wanted the answer in cum_tt so I've put it there)

pred$cum_tt = cumsum(pred$ttfilter)
希望这有帮助:)

更新(6月7日):

解决新规范的矢量化代码-即,应针对每一组条件(场地/品种/种植)分别进行累积和-如下所示:

Z31$Lookup=with(Z31,paste(Site,Cultivar,Planting,sep="~"))
Z31$LookupNum=match(Z31$Lookup,unique(Z31$Lookup))
pred$Lookup=with(pred,paste(Site,Cultivar,Planting,sep="~"))
pred$LookupNum=match(pred$Lookup,unique(pred$Lookup))

pred$Est.Date = Z31$Est.Date[match(pred$Lookup,Z31$Lookup)]
pred$ttValid = (pred$Date>=pred$Est.Date)
pred$ttFiltered = ifelse(pred$ttValid, pred$tt, 0)

### now fill in cumsum of ttFiltered separately for each LookupNum
pred$cum_tt_Z31 = as.vector(unlist(tapply(pred$ttFiltered,
                                          pred$LookupNum,cumsum)))
在我的机器上,运行时间为0.16秒,最后的
pred$cum_tt_Z31
列与非矢量化代码的答案完全匹配:)

为完整起见,值得注意的是,上述最后一条复杂的tapply生产线可以用以下简单的方法代替,在48种可能的情况下使用一个短回路:

pred$cum_tt_Z31 = rep(NA, nrow(pred))
for (lookup in unique(pred$Lookup)) {
    subs = which(pred$Lookup==lookup)
    pred$cum_tt_Z31[subs] = cumsum(pred$ttFiltered[subs])
}
运行时间仅略微增加到0.25秒左右,因为这里的循环非常小,并且循环内完成的工作是矢量化的

我想我们已经破解了!:)

一些关于矢量化的快速观察(6月8日):

对流程步骤进行矢量化的过程将运行时间从接近一小时降低到了0.16秒。即使考虑到不同的机器速度,这也是一个至少10000倍的加速比,这使一个人可能从进行小调整但仍然保持循环结构中获得的2-5倍的加速比相形见绌

要做的第一个关键观察:在解决方案中,每一行都会创建一个完整的新向量(不循环),其长度与Z31或pred中的列相同。为了整洁起见,我经常发现将这些新向量创建为新的数据帧列很有用,但显然这并不是严格必要的

第二个观察结果:使用“粘贴n匹配”策略,将所需的Est.Date列从Z31正确地传输到pred。对于这类任务有其他方法(例如使用merge),但我采用这种方法,因为它是完全故障安全的,并保证在pred中保持顺序(这是至关重要的)。本质上,粘贴操作只允许您一次匹配多个字段,因为如果粘贴的字符串匹配,则其所有组成部分都匹配。我使用~作为分隔符(前提是我知道符号不会出现在任何字段中),以避免粘贴操作造成任何歧义。如果您使用空格分隔符,那么将类似(“a B”、“C”、“D”)的内容粘贴在一起将得到与粘贴(“a”、“B C”、“D”)相同的结果—我们希望避免任何麻烦

第三个观察:很容易对逻辑操作进行矢量化,例如查看一个向量是否超过另一个(请参见pred$ttValid),或者根据向量的值选择一个或另一个值(请参见pred$ttValid)。在目前的情况下,这些可以组合成一行,但作为一个演示,我把事情分解了一点

第四个观察:创建pred$cum_tt_Z31的最后一行实际上只是使用tapply对与pred$LookupNum的每个单独值相对应的行执行求和操作,这允许您对不同的行组应用相同的函数(这里,我们是按pred$LookupNum分组的)。pred$LookupNum的定义在这里有很大的帮助——它是一个数字索引,有一个1块,后跟一个2块,依此类推。这意味着tapply生成的累积和向量的最终列表可以简单地不列出并放入向量中,并且自动按正确的顺序排列。如果您执行tapply并按未按此顺序排序的组进行拆分,则通常需要额外的几行代码来正确地匹配备份(尽管这并不复杂)

最后观察:如果最后的TAPLY太可怕,那么值得强调的是,如果循环中的工作被很好地矢量化,那么在少数情况下(比如48个)快速循环并不总是灾难性的。更新部分末尾的“替代方法”显示
pred$cum_tt_Z31 = rep(NA, nrow(pred))
for (lookup in unique(pred$Lookup)) {
    subs = which(pred$Lookup==lookup)
    pred$cum_tt_Z31[subs] = cumsum(pred$ttFiltered[subs])
}
Z31 <- data.table(Z31,key="Site,Cultivar,Planting")
pred <- data.table(pred)

## First, let's create an extra column in `pred` to see the corresponding date from `Z31` 
## Note 1: The JT is necessary since both sets have the same column names
## Note 2: I needed to use as.integer on Planting to make it work

pred[,Z31Est.Date:={JT=J(Site,Cultivar,as.integer(Planting)); Z31[JT,Est.Date][[4]]}]

## Now we can see for each row whether the date in `pred` is higher than or equal to that from `Z31`.

pred[,DateTrue:=Date>=Z31Est.Date]

## Finally, we only have to add up `pred[i,tt]` and `pred[i-1,cum_tt]` for each row where `DateTrue` equals `TRUE`.

for (i in 1:nrow(pred)) set(pred,i,13L,if(pred[i,DateTrue]) pred[i-1,cum_tt]+pred[i,tt] else(0))