如何使用 R 中的列表条目有效地设置矩阵的值?

How to efficiently set values of matrix with list entries in R?

我正在将数字列表转换为矩阵。数字列表是编码文本。每个单词都有一个与之关联的数字,例如 '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
}

我想知道,有没有 better/more 比 for 循环更有效的方法来做到这一点?

这是一个带有 row/column 索引的选项。我们 unlist 的 'encoded.text' 作为列索引,而 replist 的序列与 listlengths 联系为 row 索引。 cbind做一个row/column索引矩阵,根据索引提取'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

我们可以使用 mapply

创建一个行值和列值矩阵,我们想在其中放置 1
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