获取匹配条件的第一个值(循环太慢)

时间:2019-03-21 06:11:44

标签: r

我有很多与此相似的矩阵,但是有成千上万的行:

r <- 10
c <- 2
set.seed(333)

m1 <- matrix(runif(r*c)+1, r, c)

> m1
          [,1]     [,2]
 [1,] 1.467001 1.393902
 [2,] 1.084598 1.474218
 [3,] 1.973485 1.891222
 [4,] 1.571306 1.665011
 [5,] 1.020119 1.736832
 [6,] 1.723557 1.911469
 [7,] 1.609394 1.637850
 [8,] 1.306719 1.864651
 [9,] 1.063510 1.287575
[10,] 1.305353 1.129959

我有一个循环告诉我,对于第一列的每个值,第二列的第一个值的索引是10%,例如:

result <- 1:nrow(m1)

for (i in 1:nrow(m1)){
    result[i] <- which(m1[,2]>(1.1*m1[,1][i]))[1]
}
> result
 [1]  3  1 NA  3  1  6  3  2  1  2

我有太多的矩阵,要花费几个小时,在对我的代码进行性能分析之后,迄今为止最大的耗时任务是此循环。根据您的说法,最快的方法是什么?

例如,r = 30000时:

start_time <- Sys.time()

for (i in 1:nrow(m1)){
    result[i] <- which(m1[,2]>(1.1*m1[,1][i]))[1]
}

end_time <- Sys.time()
a <- end_time - start_time

> a
Time difference of 11.25815 secs

感谢您的帮助!

5 个答案:

答案 0 :(得分:10)

这里有一些快捷方式。您正在寻找第2列中的第一个值大于其他值的第一个值。这意味着,永远不要去看比我们先前在第2栏中看到的值低的值。

在您的10行示例中,如下所示:

> cummax(m1[, 2])
 [1] 1.393902 1.474218 1.891222 1.891222 1.891222 1.911469 1.911469 1.911469 1.911469 1.911469
> which(cummax(m1[, 2]) == m1[, 2])
[1] 1 2 3 6

如您所见,这些是结果向量中的唯一值。

可以进行的第二个优化是对第一列进行排序。如果您首先开始寻找最低值并逐步提高,则不必每次都浏览第二列。如果与左行不再匹配,则只需要转到下一行。

这确实承担了对矩阵进行排序的费用,但之后可以通过一次遍历两列来找到结果。

dostuff <- function(m1){
  orderColumn1 <- order(m1[, 1])

  plus.10 <- m1[, 1] * 1.1

  results <- rep(NA, length(plus.10))

  IndexColumn1 <- 1
  IndexColumn2 <- 1
  row2CurrentMax <- 0
  while(IndexColumn2 <= nrow(m1)){
    row2Current <- m1[IndexColumn2, 2]
    if(row2Current > row2CurrentMax){
      row2CurrentMax <- row2Current
      while(TRUE){
        row1Current <- plus.10[orderColumn1[IndexColumn1]]
        if(row1Current <= row2CurrentMax){
          results[orderColumn1[IndexColumn1]] <- IndexColumn2
          IndexColumn1 <- IndexColumn1 + 1
        } else {
          break
        }
      }
    }
    IndexColumn2 <- IndexColumn2 + 1
  }
  results
}

具有30000行:

> result <- dostuff(m1)
> end_time <- Sys.time()
> a <- end_time - start_time
> a
Time difference of 0.0600059 secs

答案 1 :(得分:9)

我不认为这是最快的方法,但是它会比使用当前的for循环方法更快。

// select row with index that you want (I used 0 for example)
var row = dataGridView1.Rows[0];
List<DateTime> datesInRow = new List<DateTime>();
foreach (DataGridViewCell cell in row.Cells)
  datesInRow.Add((DateTime)cell.Value);
var maxDate = datesInRow.Max();

编辑:根据Ronak的要求,plus.10 <- m1[, 1] * 1.1 m2 <- m1[,2] result <- sapply( plus.10, function(x) which.min(m2 < x)) result[plus.10 > max(m2) ] <- NA result [1] 3 1 NA 3 1 6 3 2 1 2 迄今为止针对10000行提出的解决方案的结果:

microbenchmark

答案 2 :(得分:3)

这是使用match()的尝试,与原始帖子中的r = 30000示例相比,该方法减少了大约25%的时间。

sapply(m1[, 1] * 1.1, function(x) match(TRUE, m1[, 2] > x))

[1]  3  1 NA  3  1  6  3  2  1  2

答案 3 :(得分:2)

优化代码的最佳方法是使用data.table软件包

此代码使您的速度提高了2倍以上。

library(data.table);

setDTthreads(0);

r <- 30000;
c <- 2;
set.seed(333);

m1 <- matrix(runif(r*c)+1, r, c);
result1 <- rep(NA, nrow(m1));

start_time <- Sys.time();

for (i in 1:nrow(m1))
{
    result1[i] <- which(m1[,2]>(1.1*m1[,1][i]))[1];
}

#result1

end_time <- Sys.time()
a <- end_time - start_time
a


start_time <- Sys.time()

tstDT <- data.table(m1);
#result2 <- tstDT[, sapply(V1, function(elem) { which(V2 > 1.1*elem)[1] })]
result2 <- tstDT[, sapply(V1, function(x) match(TRUE, V2 > 1.1*x) )]

#result2

end_time <- Sys.time()
a <- end_time - start_time
a

一点评论-我使用由gcc编译的data.table,其中march = native和O3。可能的O2和march = core(如通过安装的标准包装中)的速度会降低,但是...

结果:

> library(data.table);
> 
> setDTthreads(0);
> 
> r <- 30000;
> c <- 2;
> set.seed(333);
> 
> m1 <- matrix(runif(r*c)+1, r, c);
> result1 <- rep(NA, nrow(m1));
> 
> start_time <- Sys.time();
> 
> for (i in 1:nrow(m1))
+ {
+     result1[i] <- which(m1[,2]>(1.1*m1[,1][i]))[1];
+ }
> 
> #result1
> 
> end_time <- Sys.time()
> a <- end_time - start_time
> a
Time difference of 8.738938 secs
> 
> 
> start_time <- Sys.time()
> 
> tstDT <- data.table(m1);
> #result2 <- tstDT[, sapply(V1, function(elem) { which(V2 > 1.1*elem)[1] })]
> result2 <- tstDT[, sapply(V1, function(x) match(TRUE, V2 > 1.1*x) )]
> 
> #result2
> 
> end_time <- Sys.time()
> a <- end_time - start_time
> a
Time difference of 3.582921 secs
> 
> 
> 
> 

答案 4 :(得分:1)

我建议这些:

r <-30000
c <- 2
set.seed(333)

m1 <- matrix(runif(r*c)+1, r, c)
x2 <-m1[, 2]



start_time <- Sys.time()

result <- lapply(m1[, 1], function(x) {
  min(which(m1[,2]>(1.1*x)))
})
end_time <- Sys.time()
a <- end_time - start_time
a


start_time <- Sys.time()

result <- lapply(m1[, 1], function(x) {
            min(which(x2>(1.1*x)))
})
end_time <- Sys.time()
a <- end_time - start_time
a

第一个:8.6 s 第二个:6.4 s