【问题标题】:R: Bad loop code, improve the speed?R:糟糕的循环代码,提高速度?
【发布时间】:2015-01-19 02:59:15
【问题描述】:

我创建了一些基本代码来实现我所需要的,但它非常慢,因为它是糟糕的代码。目的是从 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          

【问题讨论】:

    标签: r performance for-loop


    【解决方案1】:

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

    SE 中的条目是唯一的吗?如果是这样,那么您可以删除匹配项 SC,因此不会再次对其进行检查。

    SE 或 SC 中的条目是否已排序?如果是这样,那么您可以使用 比较短路 SC 中的搜索(例如:如果 SC 已排序 按递增顺序,然后在检查匹配项时,如果我是 比较大于我正在寻找的东西然后我保释 出去,因为不会再有比赛了)

    【讨论】:

    • SE 按“房间”按数字/字母顺序排序,然后在每个房间内按时间升序排序。 SC 同样按 Room 排序,然后在其中按升序排序。那么有没有办法先按房间杀死它,然后再按天等其他指标杀死它?
    • 是的,这很简单。伪代码看起来像这样 `if(se_room
    【解决方案2】:

    您可以减少 for 循环中的函数调用次数(尤其是 as.POSIXlt 调用),这应该会有所帮助。此外,&amp;&amp; 运算符可能会更快,因为在第一个评估为 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 
    
        } 
    

    使用“外部”可能会更有效

    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 循环中使用 'levels(SC[,1]) 而不是 unique(SC[,1]) 的因素...]

    【讨论】:

    • 我读到了调用函数,所以这是我正在寻找的东西。鉴于我的数据集很大,我想我可能不得不使用比 R for 循环更快的东西
    【解决方案3】:

    进一步采纳@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 所指出的,这仍然是一个二次时间算法,使用您的数据属性可能还有相当大的改进空间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-04
      • 2015-03-24
      • 2021-07-31
      • 2020-11-03
      • 2020-11-19
      • 1970-01-01
      相关资源
      最近更新 更多