Как эффективно установить значения матрицы со списком записей в R? - PullRequest
2 голосов
/ 14 апреля 2019

Я конвертирую список чисел в матрицу. Список чисел является зашифрованным текстом. Каждое слово имеет связанный с ним номер, например «the»: 1, «it»: 2 и т. Д. Я хочу получить матрицу значений, в которой наличие закодированного слова представлено как «1». Так что если один из наших закодированных текстов будет выглядеть так:

c(1, 4, 2)

Тогда соответствующая матрица (с максимальным индексом слова 10) будет выглядеть так:

     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    1    1    0    1    0    0    0    0    0     0

Итак, вот как у меня сейчас:

encoded.text <- list(c(1, 3, 2), c(1, 7, 8))

result <- matrix(0, nrow = length(encoded.text), ncol = 10)

for (i in 1:length(encoded.text)) {
  result[i, encoded.text[[i]]] <- 1
}

Мне интересно, есть ли лучший / более эффективный способ, чем цикл for, чтобы сделать это?

Ответы [ 2 ]

1 голос
/ 14 апреля 2019

Вот один вариант с индексированием row/column.Мы unlist 'encoded.text' для индекса столбца, в то время как rep лицензирует последовательность list с lengths list как row индекса.cbind чтобы создать матрицу индекса строки / столбца, извлечь значения 'result' на основе индекса и присвоить его 1

m1 <- cbind(rep(seq_along(encoded.text), lengths(encoded.text)), 
            unlist(encoded.text))
result[m1] <- 1
result
#      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#[1,]    1    1    1    0    0    0    0    0    0     0
#[2,]    1    0    0    0    0    0    1    1    0     0

ПРИМЕЧАНИЕ. apply/Map и т. Д. Просто циклы как for петля.Это не даст никакого прироста производительности, кроме простого добавления в качестве ответа

Тесты

n <- 1e6
test <- rep(encoded.text, n)
testresult <- matrix(0, nrow = length(test), ncol = 10)
testresult2 <- copy(testresult)
testresult3 <- copy(testresult)

system.time({
m2 <- cbind(rep(seq_along(test), lengths(test)), 
            unlist(test))
testresult[m2] <- 1
})
# user  system elapsed 
#  0.290   0.098   0.388 

system.time({
testresult2[do.call(rbind, Map(cbind, seq_len(length(test)), test))] <- 1

})
#   user  system elapsed 
#  8.383   0.462   8.787 

system.time({

 for (i in 1:length(test)) {
   testresult3[i, test[[i]]] <- 1
 }
 })
#   user  system elapsed 
#  0.648   0.131   0.778 

Если мы увеличим 'n' и снова запустим (после построения данных)

n <- 1e7

system.time({
 m2 <- cbind(rep(seq_along(test), lengths(test)), 
             unlist(test))
 testresult[m2] <- 1
 })
#   user  system elapsed 
#  2.699   1.225   3.990  # almost 2 times efficient now

system.time({
 testresult2[do.call(rbind, Map(cbind, seq_len(length(test)), test))] <- 1

 })
#   user  system elapsed 
# 88.584   5.047  94.384 

 system.time({

  for (i in 1:length(test)) {
    testresult3[i, test[[i]]] <- 1
  }
  })
#   user  system elapsed 
#  5.734   0.742   6.461 

- микробенчмарк по n <- 1e7 построенным данным

ak <- function() {
    m2 <- cbind(rep(seq_along(test), lengths(test)), 
                 unlist(test))
     testresult[m2] <- 1

}

wfw <- function() {
for (i in 1:length(test)) {
    testresult3[i, test[[i]]] <- 1
  }

}
library(microbemchmark)
microbenchmark(ak(), wfw(), unit = 'relative', times = 20L)
#Unit: relative
#  expr      min       lq     mean   median       uq      max neval cld
#  ak() 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000    20  a 
# wfw() 1.946415 1.945528 1.927263 1.926645 1.910907 1.940207    20   b
0 голосов
/ 14 апреля 2019

Мы можем создать матрицу значений строк и столбцов, в которые мы хотим поместить 1, используя mapply

result[do.call(rbind, Map(cbind, seq_len(length(encoded.text)), encoded.text))] <- 1

result
#     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#[1,]    1    1    1    0    0    0    0    0    0     0
#[2,]    1    0    0    0    0    0    1    1    0     0
...