Сделайте этот цикл быстрее в R - PullRequest
0 голосов
/ 05 марта 2012

Как я могу ускорить следующий (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 ]

4 голосов
/ 05 марта 2012

Согласно моему комментарию,

#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 раза быстрее или около того.

3 голосов
/ 05 марта 2012

не уверен, что такое 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...