R:循环代码错误,提高速度?

时间:2015-01-19 02:59:15

标签: r performance for-loop

我创建了一些基本代码来实现我需要的东西,但由于它的代码很糟糕,因此速度非常慢。目的是从SE列中取一行,如果它与SC列匹配,则为它所属的每5分钟括号向计数器添加1。

我写的代码是

for (i in 1:(nrow(SC)))
  for(j in 1:(nrow(SE)))
    for (k in 0:5)
      if ( (SE[j,3]==SC[i,1]) & 
        (as.POSIXlt(SE[j,1])>as.POSIXlt(SC[i,4]) +k*5*60)&
        (as.POSIXlt(SE[j,1])<=as.POSIXlt(SC[i,4])+ (k+1)*5*60 ) &
        (SE[j,2]==1) )
      { SC[i,6+k]=SC[i,6+k]+1 } 

也就是说检查SC的每个单元格以查看条件(房间号是否相同,时间括号之间的时间等)。

这是非常低效的,因为三重循环在R中永远需要。寻找替换循环的方法,可能是向量还是应用?

> SE
            UTC         pin  Room
 1  2014-12-22 10:14:34   1 Alpha
 4  2014-12-22 10:15:27   1 Alpha
 5  2014-12-22 10:16:00   1 Alpha
 8  2014-12-22 10:18:10   1 Alpha
 12 2014-12-22 10:19:06   1 Alpha
 13 2014-12-22 10:20:00   1 Alpha
 14 2014-12-22 10:08:34   1  Beta
 17 2014-12-22 10:15:29   1  Beta
 18 2014-12-22 10:16:00   1  Beta
 19 2014-12-22 10:17:00   1  Beta
 22 2014-12-22 10:18:10   1  Beta
 24 2014-12-22 10:19:00   1  Beta
 26 2014-12-22 10:19:11   1  Beta
 28 2014-12-22 10:09:34   1 Gamma
 29 2014-12-22 10:39:11   1 Gamma


 > SC
    Room Capacity Video.Conference                  ST                  ET 
 1 Alpha       16                1 2014-12-22 10:00:00 2014-12-22 10:30:00
 2 Alpha       16                1 2014-12-22 10:30:00 2014-12-22 11:00:00
 3  Beta       16                1 2014-12-22 10:00:00 2014-12-22 10:30:00
 4  Beta       16                1 2014-12-22 10:30:00 2014-12-22 11:00:00
 5 Gamma       10                0 2014-12-22 10:00:00 2014-12-22 10:30:00
 6 Gamma       10                0 2014-12-22 10:30:00 2014-12-22 11:00:00


>Desired #This is the intended output
 X  Room Capacity Vid                  ST              ET        X0.to.5.min  X5.to.10.min  X10.to.15.min  X15.to.20.min  X20.to.25.min  X25.to.30.min 
 1  Alpha       16   1 2014-12-22 10:00:00 2014-12-22 10:30:00           0            0             1             5             0            0           
 2  Alpha       16   1 2014-12-22 10:30:00 2014-12-22 11:00:00           0            0             0             0             0            0           
 3   Beta       16   1 2014-12-22 10:00:00 2014-12-22 10:30:00           0            1             0             6             0            0           
 4   Beta       16   1 2014-12-22 10:30:00 2014-12-22 11:00:00           0            0             0             0             0            0           
 5  Gamma       10   0 2014-12-22 10:00:00 2014-12-22 10:30:00           0            1             0             0             0            0          
 6  Gamma       10   0 2014-12-22 10:30:00 2014-12-22 11:00:00           0            1             0             0             0            0          

3 个答案:

答案 0 :(得分:2)

此刻您的算法运行在O(n ^ 2),如果您对SE和SC列的属性一无所知,这是您可以做的最好的算法。 如果任一列中的数据具有某些特定属性,那么您可以进行一些优化。例如

SE中的条目是唯一的吗?如果是,那么你可以删除匹配     SC因此不会再次检查。

SE或SC中的条目是否排序?如果是,那么你可以使用     将SC中的搜索短路的比较(例如:如果SC被排序     按顺序增加,然后在检查匹配时,如果行I&#m; m     比较大于我搜索的时间然后我保释     因为不再有比赛了)

答案 1 :(得分:0)

你可以减少for循环中的函数调用次数(特别是as.POSIXlt调用),这应该有所帮助。此外,&&运算符可能会更快,因为在第一个计算结果为false的条件之后,将不会评估后续比较。

posix.SE  <- as.POSIXlt(SE[,1])
posix.SC  <- as.POSIXlt(SC[,4])

for (i in 1:(nrow(SC)))
for(j in 1:(nrow(SE)))
for (k in (0:5))
if ( (SE[j,3]==SC[i,1]) &&
    (posix.SE[j]>posix.SC[i] + k*300)&&
    (posix.SE[j]<=posix.SC[i]+ (k+1)*300 ) &&
    (SE[j,2]==1) ) {

    SC[i,6+k]=SC[i,6+k]+1 

} 

您还可以通过以下方式减少评估if子句中第一个条件的次数:

for(val in unique(SE[,3]))
    for(i in which(SC[,1] == val))
    for(j in which(SE[,3] == val))
    for (k in (0:5))
    if ((posix.SE[j]>posix.SC[i] + k*300)&&
        (posix.SE[j]<=posix.SC[i]+ (k+1)*300 ) &&
        (SE[j,2]==1) ) {

        SC[i,6+k]=SC[i,6+k]+1 

    } 

使用&#39;外部&#39;

可能更有效率
for(val in unique(SC[,1])){
    # index the relevent rows for each value in SC[,1]
    index.SC  <-  which(SC[,1] == val)
    index.SE  <-  which(SE[,3] == val & SE[,2]==1)
    MX <- outer(posix.SE[index.SE], posix.SC[index.SC],`-`)
    for (k in (0:5))
        SC[indxe.SC,6+k]  <-  apply((MX > k*300)& (MX <= (k+1)*300 ),2,sum)
}

[如果SC [,1]是您想要在for循环中使用&#39;级别(SC [,1])而不是唯一(SC [,1])的因素... ]

答案 2 :(得分:0)

进一步考虑@ jthorpe的建议,在可能的情况下进行矢量化并进行常规计算

step <- 5 * 60
se <- as.POSIXlt(SE[,1]) / step
sc <- as.POSIXlt(SC[,4]) / step
k <- 0:5

更新data.frame非常昂贵,因此请创建一个包含答案的矩阵

ans <- as.matrix(SC[, 6 + 0:5])

重新排列循环,以便可以对第一个和最后一个测试标准的计算进行矢量化

for (j in seq_along(se)[SE[,2] == 1])
    for (i in seq_along(sc)[SE[j, 3] == SC[,1]])

并矢量化最里面的循环

    {
        d <- se[j] - sc[i]
        idx <- k[(d > k) & (d <= (k + 1))] + 1
        ans[i, idx] <- ans[i, idx] + 1
    }

这仍然是二次时间算法,正如@hhafez所指出的那样,使用数据属性可能还有相当大的改进空间。