从for循环申请加速R

时间:2011-11-23 18:49:25

标签: r

我正在使用两个矩阵运行for循环。一个matrix(A)有大约100个字符串(例如,name1,name2,...,name100),只有一列。另一个matrix(B)大于A,其中包含值和字符串的行和列。在B矩阵的某些位置,A矩阵的每个名称都匹配。我想在输出矩阵上使用特定的矩阵A来提取和堆叠匹配的整行。

所以,我正在运行如下,

output <- NULL
for(K in 1:nrow(A)){
  print(K)
  for(cc in 1:nrow(B)){
    for(dd in 1:ncol(B)){
      if(toupper(A[K])==toupper(B[cc,dd])){
        output <- rbind(output,B[cc,])
      }
    }
  }
}

但它太慢了。如何在运行时间方面使循环更有效?

3 个答案:

答案 0 :(得分:5)

速度问题不是因为for循环。 apply可能会更慢。您需要预先标注目标对象并使用索引分配值。

或者您需要考虑像Manuel的测试用例那样的矢量化解决方案:

 idx <- unique(which(toupper(as.matrix(B)) %in% toupper(A), arr.ind=TRUE) %% NROW(B))
 idx[idx==0] <- 4
     B[idx , ]
  z1         z2 z3
1  a  1.5623285  a
4  c -1.2196311  f
2  g  0.2551535  b

答案 1 :(得分:4)

这是一个快速的解决方案,应该提供与您相同的输出:

set.seed(13)
A <- matrix(letters[1:5])
B <- matrix(sample(letters, 12, rep(T)), 4)

x <- match(toupper(A), toupper(B), nomatch=0L)
x <- (x[x>0L]-1L) %% nrow(B) + 1L
output <- B[x, , drop=FALSE]

它的工作原理是使用match在A匹配的B中找到(向量)索引。然后它将这些索引转换为行索引,最后提取这些行。

..请注意,行B[2,]在输出中包含两次 - 这真的是你想要的吗?如果没有,请将最后一行更改为:

output <- B[unique(x), , drop=FALSE]

编辑一些时间安排。我删除了toupper次呼叫,因为它支配了时间,@ Manuel Ramon没有调用它。请注意,我们所有的输出都不同!因此可能需要进行一些调试; - )

# Create huge A and B matrices
set.seed(13)
strs <- outer(letters, LETTERS, paste)
A <- matrix(strs)
B <- matrix(sample(strs, 1e7, rep(T)), 1e4)

# My solution: 0.24 secs   
system.time({
 x <- match(A, B, nomatch=0L)
 x <- (x[x>0L]-1L) %% nrow(B) + 1L
 output1 <- B[unique(x), , drop=FALSE]
})

# @DWin's solution: 0.91 secs
system.time({
 idx <- unique(which(as.matrix(B) %in% A, arr.ind=TRUE) %% NROW(B))
 idx[idx==0] <- 4
 output2 <- B[idx, , drop=FALSE]
})

# @Manuel Ramon's solution: 0.89 secs
system.time({
  id <- apply(B, 2, function(x) A %in% x)
  output3 <- B[apply(id,1,sum)>0, ]
}) 

答案 2 :(得分:0)

这里有一些想法:

A <- matrix(c('a','b','c','d'), ncol=1)
B <- data.frame(z1=c('a','g','f','c'), z2=rnorm(4), z3=c('a','b','f','f'))

id <- apply(B, 2, function(x) A %in% x)
newB <- B[apply(id,1,sum)>0, ]