如何提高循环操作的性能

时间:2019-06-15 15:36:26

标签: r performance loops runtime

我的R代码的性能出现问题,该代码检查我的数据帧是否符合特定条件。 对于数据框中的每一行,我需要所有组合,其中该行的变量“ A”大于或等于所有其他行的变量“ B”。最后,我需要一个由3列组成的矩阵,其中包括所有组合:

  1. 列:变量A中的行号
  2. 列:变量B小于A的行数
  3. 列:-1

我需要检查每一行。当您看到我的代码时,也许我的问题变得更加清晰。

Z <-data.frame(index1=NA,index2=NA,index3=NA)

for(i in 1:nrow(my.data)){

  interim_result <- my.data[i,"A"] >= my.data$B
  if(sum(is.na(interim_result))!=length(interim_result)){

    Y <- rbind(rep(i, sum(interim_result*1)), which(interim_result == TRUE), rep(-1, sum(interim_result)))
    print(i)
    Y <- t(Y)
    colnames(Y) <- c("index1","index2","index3")
    Z <- rbind(Z,Y)
  }
}

我检查了我的代码,它可以正常运行,但是速度太慢。我的数据框大约有35万行,计算需要花费很多时间。 有谁知道我可以加快速度?

1 个答案:

答案 0 :(得分:0)

使用outer()which()

set.seed(1)
n_rows <- 10
my.data <- data.frame(A = rnorm(n_rows), B = rnorm(n_rows))

mat <- which(outer(my.data[['A']], my.data[['B']], '>='), arr.ind = T)
colnames(mat) = c('index2', 'index1')

mat[, c('index1', 'index2')]

      index1 index2
 [1,]      1      4
 [2,]      2      4
 [3,]      2      7
 [4,]      2      8
 [5,]      2      9
 [6,]      3      2
 [7,]      3      4
 [8,]      3      5
 [9,]      3      7
... a total of 39 rows

我没有包括index3,因为它是一个常数。如果始终为-1,则没有太大用处。

通过将ID添加到原始data.frame并使用lapply,我可以大大提高循环速度。这使我可以跳过which调用,而不必担心为Z

进行预分配
  my.data$ID <- seq_len(nrow(my.data))
do.call(rbind
        , lapply(seq_len(nrow(my.data))
                 , function (i) {
                   interim_result <- my.data[['ID']][my.data[i, "A"] >= my.data[['B']]]
                   if (length(interim_result) != 0) {
                     cbind(index1 = i,index2 = interim_result,index3 = -1)
                   }
                   }
                 )
)

最后,如果您使用data.table,则可以使用非等额联接。

  dt <- as.data.table(my.data)

  dt[, ID := seq_len(.N)]

  dt[dt 
     , on = .(A >= B)
     , .(index1 = i.ID, index2 = ID, index3 = -1)
     , allow.cartesian = T
     ]

性能 10行data.frame:

Unit: microseconds
            expr     min         lq       mean     median         uq       max neval
   original_loop 12607.5 12687.6510 13420.3960 12843.0520 13260.4010 17939.301    20
      optim_loop   412.5   439.4515   695.5263   451.2510   462.0020  5345.802    20
          dt_way  3053.0  3140.7510  3269.0610  3268.9010  3351.2010  3667.601    20
 outer_statement    48.5    53.9005    65.7108    70.6505    72.7515    75.701    20

100行data.frame:

Unit: microseconds
            expr       min        lq      mean     median        uq       max neval
   original_loop 42241.600 43560.001 48111.291 46051.7515 48297.301 79910.601    20
      optim_loop  3888.601  4010.551  4775.211  4107.6010  4299.400  9010.601    20
          dt_way  3356.902  3595.601  3857.906  3752.8505  3966.701  5330.101    20
 outer_statement   304.901   312.401   344.661   332.5005   348.701   473.000    20

1,000行-删除原始循环:

Unit: milliseconds
            expr     min       lq     mean   median       uq     max neval
      optim_loop 55.0290 58.18355 60.50015 60.08140 62.47300 66.6332    20
          dt_way 29.1114 29.66050 32.19182 30.00790 30.88125 45.7993    20
 outer_statement 24.2323 24.44935 26.87686 24.64055 27.48775 35.9967    20

10,000行:

Unit: seconds
            expr      min       lq     mean   median       uq      max neval
      optim_loop 2.233144 2.277568 2.401055 2.382523 2.496764 2.615275     5
          dt_way 3.622701 3.638953 3.660230 3.639226 3.649577 3.750691     5
 outer_statement 3.250272 3.353263 3.369732 3.375544 3.409773 3.459810     5

那之后我的电脑崩溃了。令我惊讶的是,优化的循环开始取得进展。