在R中使这个循环更快

时间:2012-03-05 18:35:02

标签: performance r loops text-mining

如何加速以下(noob)代码:

#"mymatrix" is the matrix of word counts (docs X terms) 
#"tfidfmatrix" is the transformed matrix
tfidfmatrix = Matrix(mymatrix, nrow=num_of_docs, ncol=num_of_words, sparse=T)

#Apply a transformation on each row of the matrix
for(i in 1:dim(mymatrix)[[1]]){
  r = mymatrix[i,]
  s = sapply(r, function(x) ifelse(x==0, 0, (1+log(x))*log((1+ndocs)/(1+x)) ) )
  tfmat[i,] = s/sqrt(sum(s^2))
}
return (tfidfmatrix)

问题是我正在处理的矩阵相当大(~40kX100k),而且这段代码非常慢。

我没有使用“apply”(而不是使用for循环和sapply)的原因是apply会给我我想要的矩阵的转置 - 我想要num_of_docs X num_of_words,但是apply会给我转。然后,我将花费更多的时间来计算转置并重新分配它。

有关加快速度的想法吗?

非常感谢。

编辑:我发现下面的建议大大加快了我的代码(除了让我觉得愚蠢)。有关我可以从哪里学习编写“优化”R代码的建议吗?

编辑2:好的,所以事情不对。一旦我做s.vec[!is.finite(s.vec)] <- 0 s.vec的每个元素被设置为0.只是为了重新迭代我的原始矩阵是一个包含整数的稀疏矩阵。这是由于我正在使用的Matrix包的一些怪癖。当我s.vec[which(s.vec==-Inf)] <- 0事情按预期工作时。想法?

2 个答案:

答案 0 :(得分:4)

根据我的评论,

#Slightly larger example data
mymatrix <- matrix(runif(10000),nrow=10)
mymatrix[sample(10000,100)] <- 0
tfmat <- matrix(nrow=10, ncol=1000)
ndocs <- 1

justin <- function(){
    s.vec <- ifelse(mymatrix==0, 0, (1 + log(mymatrix)) * log((1 + ndocs)/(1 + mymatrix)))
    tfmat.vec <- s.vec/sqrt(rowSums(s.vec^2))
}

joran <- function(){
    s.vec <- (1 + log(mymatrix)) * log((1 + ndocs)/(1 + mymatrix))
    s.vec[!is.finite(s.vec)] <- 0
    tfmat.vec <- s.vec/sqrt(rowSums(s.vec^2))
}

require(rbenchmark)    
benchmark(justin(),joran(),replications = 1000)

  test replications elapsed relative user.self sys.self user.child sys.child
2  joran()         1000   0.940  1.00000     0.842    0.105          0         0
1 justin()         1000   2.786  2.96383     2.617    0.187          0         0

所以它大约快3倍左右。

答案 1 :(得分:3)

不确定ndocs是什么,但ifelse已经过矢量化,因此您应该能够使用ifelse语句,而无需逐行遍历矩阵并sapply沿着这条路。对于最终的计算也可以这样说。

但是,您还没有给出完整的复制示例...

mymatrix <- matrix(runif(100),nrow=10)
tfmat <- matrix(nrow=10, ncol=10)
ndocs <- 1

s.vec <- ifelse(mymatrix==0, 0, 1 + log(mymatrix)) * log((1 + ndocs)/(1 + mymatrix))

for(i in 1:dim(mymatrix)[[1]]){
  r = mymatrix[i,]
  s = sapply(r, function(x) ifelse(x==0, 0, (1+log(x))*log((1+ndocs)/(1+x)) ) )
  tfmat[i,] <- s
}

all.equal(s.vec, tfmat)

所以唯一丢失的部分是最终计算中的rowSums

tfmat.vec <- s.vec/sqrt(rowSums(s.vec^2))

for(i in 1:dim(mymatrix)[[1]]){
  r = mymatrix[i,]
  s = sapply(r, function(x) ifelse(x==0, 0, (1+log(x))*log((1+ndocs)/(1+x)) ) )
  tfmat[i,] = s/sqrt(sum(s^2))
}

all.equal(tfmat, tfmat.vec)