如何用优化函数替换R中的for循环(lapply?)

时间:2010-09-10 11:54:49

标签: r loops vectorization

我的每一行都有一个包含时间事件的数据框。在一行中,我有发送者的事件类型(typeid = 1),另一行是接收者的事件(typeid = 2)。我想计算发送方和接收方之间的延迟(时差)。

我的数据以data.frame的形式组织,如下面的快照所示:

dd[1:10,]
     timeid   valid typeid
1  18,00035 1,00000      1
2  18,00528 0,00493      2
3  18,02035 2,00000      1
4  18,02116 0,00081      2
5  18,04035 3,00000      1
6  18,04116 0,00081      2
7  18,06035 4,00000      1
8  18,06116 0,00081      2
9  18,08035 5,00000      1
10 18,08116 0,00081      2

calc_DelayVIDEO <- function (dDelay ){

        pktProcess <- TRUE
        nLost <- 0
        myDelay <- data.frame(time=-1, delay=-1, jitter=-1, nLost=-1)
        myDelay <- myDelay[-1, ]
        tini <- 0
        tend <- 0
        for (itr in c(1:length(dDelay$timeid))) {
           aRec <- dDelay[itr,]
           if (aRec$typeid == 1){
                tini <- as.numeric(aRec$timeid)
                if (!pktProcess ) {
                   nLost <- (nLost + 1)
                   myprt(paste("Packet Lost at time ", aRec$timeid, " lost= ", nLost, sep=""))
                }

                pktProcess <- FALSE 
           }else if (aRec$typeid == 2){

                tend <- as.numeric(aRec$timeid)
                dd <- tend - tini
                jit <- calc_Jitter(dant=myDelay[length(myDelay), 2], dcur=dd)
                myDelay <- rbind(myDelay, c(aRec$timeid, dd, jit, nLost))
                pktProcess <- TRUE
                #myprt(paste("time=", aRec$timeev, " delay=", dd, " Delay Var=", jit, " nLost=", nLost ))
           }
        }
        colnames(myDelay) <- c("time", "delay", "jitter", "nLost")
        return (myDelay)
}

要执行延迟计算,我使用calc_DelayVideo函数,对于具有大量记录(~60000)的数据帧,它需要花费大量时间。

如何用更优化的R函数替换for循环? 我可以用lapply来做这样的计算吗?如果是这样,你能举个例子吗?

提前致谢,

4 个答案:

答案 0 :(得分:4)

通常的解决方案是仔细思考问题以找到矢量化的东西。

如果失败了,我有时会尝试用C ++重写循环; Rcpp包可以帮助界面。

答案 1 :(得分:2)

*apply函数套件未针对循环进行优化。此外,我已经解决了for循环速度超过apply的问题,因为apply使用了更多内存并导致我的机器交换。

我建议完全初始化myDelay对象并避免使用rbind(必须重新分配内存):

init <- rep(NA, length(dDelay$timeid))
myDelay <- data.frame(time=init, delay=init, jitter=init, nLost=init)

然后替换:

myDelay <- rbind(myDelay, c(aRec$timeid, dd, jit, nLost))

myDelay[i,] <- c(aRec$timeid, dd, jit, nLost)

答案 2 :(得分:2)

正如Dirk所说:矢量化将有所帮助。一个例子是将调用移到as.numeric循环(因为这个函数适用于向量)。

dDelay$timeid <- as.numeric(dDelay$timeid)

其他可能有用的事情

没有打扰aRec <- dDelay[itr,]行,因为您只需访问dDelay行,而无需创建新变量。

预分配myDelay,因为它在循环中增长可能是一个瓶颈。有关此问题的更多信息,请参阅约书亚的答案。

答案 3 :(得分:0)

另一个优化:如果我正确读取你的代码,你可以使用:

轻松计算向量nLost
nLost <-cumsum(dDelay$typeid==1)

在循环之外。那个你最后可以添加到数据帧中。为您节省了大量时间。如果我使用您的数据框,那么:

> nLost <-cumsum(dd$typeid==1)
> nLost
 [1] 1 1 2 2 3 3 4 4 5 5

同样,丢失包裹的时间可以计算为:

> dd$timeid[which(dd$typeid==1)]
[1] 18,00035 18,02035 18,04035 18,06035 18,08035

如果您想在某处报告它们。

为了测试,我使用了:

dd <- structure(list(timeid = structure(1:10, .Label = c("18,00035", 
"18,00528", "18,02035", "18,02116", "18,04035", "18,04116", "18,06035", 
"18,06116", "18,08035", "18,08116"), class = "factor"), valid = structure(c(3L, 
2L, 4L, 1L, 5L, 1L, 6L, 1L, 7L, 1L), .Label = c("0,00081", "0,00493", 
"1,00000", "2,00000", "3,00000", "4,00000", "5,00000"), class = "factor"), 
    typeid = c(1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L)), .Names = c("timeid", 
"valid", "typeid"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5", "6", "7", "8", "9", "10"))