如何使用 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' 作为列索引,而 rep
将 list
的序列与 list
的 lengths
联系为 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
我正在将数字列表转换为矩阵。数字列表是编码文本。每个单词都有一个与之关联的数字,例如 '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' 作为列索引,而 rep
将 list
的序列与 list
的 lengths
联系为 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
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