2012-03-05 77 views
0

我如何可以加快以下(小白)代碼:讓這個循環更快R中

#"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)的原因是應用將給我矩陣的轉置我想 - 我想num_of_docs X num_of_words,但應用會給我轉置。那麼我將不得不花更多的時間來計算轉置並重新分配它。

任何想法讓這個更快?

非常感謝。

編輯:我發現下面的建議大大加快了我的代碼(除了讓我感到愚蠢)。我可以從哪裏學習編寫「優化」R代碼的任何建議?

編輯2:好的,所以有些事情是不對的。一旦我做了s.vec[!is.finite(s.vec)] <- 0 s.vec的每個元素都被設置爲0.只是重新迭代我的原始矩陣是一個包含整數的稀疏矩陣。這是由於我正在使用的Matrix軟件包的一些怪癖。當我做s.vec[which(s.vec==-Inf)] <- 0事情按預期工作。思考?

+0

我不知道r,但你有沒有試過在循環之外移動dim(mymatrix)? (你能嗎?) – 2012-03-05 18:37:47

+0

他們可能可以但它不會有很大的區別。 – Dason 2012-03-05 19:29:11

+0

我相信我前段時間在R FAQ中找到了這個。 http://www.burns-stat.com/pages/Tutor/R_inferno.pdf。這是一個向量化的精彩而易讀的指南。 – digitalmaps 2012-03-06 03:16:37

回答

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) 
+0

我敢打賭(少量)完全拋棄'ifelse',並且用'is.finite'替代'-Inf'值會更快。 – joran 2012-03-05 19:03:57

+0

@joran我一直聽到,但沒有測試它自己。雖然好點。讓日誌返回-Inf並在之後改變它們可能是要走的路。 – Justin 2012-03-05 19:07:22

+0

@justin:很好的建議。謝謝。 – user721975 2012-03-05 19:55:38

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倍左右。

+0

(+1)哈!做得好!! – Justin 2012-03-05 19:17:32

+0

@joran:我永遠不會驚訝於我在這個網站上找到的東西。非常感謝。 – user721975 2012-03-05 19:56:09