将邻接列表转换为 R 中的二进制矩阵
Posted
技术标签:
【中文标题】将邻接列表转换为 R 中的二进制矩阵【英文标题】:Transform adjacency lists to binary matrix in R 【发布时间】:2022-01-20 15:31:28 【问题描述】:鉴于每行中 1 的位置列表,我试图找到一种有效的方法来构造二进制矩阵。这是一个小例子,虽然我正在尝试找到可以很好扩展的东西 -
给定一个二元矩阵:
> M <- matrix(rbinom(25,1,0.5),5,5)
> M
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 1 1 0
[2,] 0 1 1 1 1
[3,] 1 1 0 1 1
[4,] 1 0 0 1 0
[5,] 0 1 1 0 0
我可以使用以下方法将 M 转换为邻接表:
> Mlist <- apply(M==1, 1, which, simplify = FALSE)
> Mlist
[[1]]
[1] 2 3 4
[[2]]
[1] 2 3 4 5
[[3]]
[1] 1 2 4 5
[[4]]
[1] 1 4
[[5]]
[1] 2 3
我想将Mlist
转换回M
。一种可能性是:
M.new <- matrix(0,5,5)
for (row in 1:5)M.new[row,Mlist[[row]]] <- 1
但是,似乎应该有更有效的方法。
谢谢!
【问题讨论】:
你可以使用稀疏矩阵;例如sparseMatrix(i=rep(seq_along(Mlist), lengths(Mlist)), j=unlist(Mlist), x=1)
。对于小型示例,这可能会更慢,但对于更大、更消耗内存的示例,这可能会更快
@user20650 这是一个好主意(之前建议过)。我只是无法确定 sparseMatrix 方法更快的大小。
@Zachary ;我认为你的方法是一种明智的方法。当密集矩阵不适合 n 内存时遇到大问题时,稀疏方法很有用
@Akrun;抱歉 ;) 几乎是你写的一个字一个字的副本
@user20650 谢谢。我取消删除。如果您想出更好的方法,请作为解决方案发布。
【参考方案1】:
1) 使用最后注释中定义的 M 和 Mlist,在其组件上使用 sapply 替换所需位置处的零向量。最后转置。
M2 <- t(sapply(Mlist, replace, x = integer(length(Mlist)), 1L))
identical(M, M2) # check that M2 equals M
## [1] TRUE
2) 按键次数稍多但速度更快的变体将是
M3 <- do.call("rbind", lapply(Mlist, replace, x = integer(length(Mlist)), 1L))
identical(M, M3)
## [1] TRUE
基准测试
这里 ex1 和 ex2 是上面的 (1) 和 (2),ex0 是问题中的 for 循环,除了我们使用整数而不是双精度。请注意,(2) 比问题中的循环快约 100 倍。
library(microbenchmark)
microbenchmark(
ex0 = M.new <- matrix(0L,5,5); for (row in 1:5)M.new[row,Mlist[[row]]] <- 1L ,
ex1 = t(sapply(Mlist, replace, x = integer(length(Mlist)), 1L)),
ex2 = do.call("rbind", lapply(Mlist, replace, x = integer(length(Mlist)), 1L))
)
给予:
Unit: microseconds
expr min lq mean median uq max neval cld
ex0 4454.4 4504.15 4639.111 4564.1 4670.10 8450.2 100 b
ex1 73.1 84.75 98.220 94.3 111.75 130.8 100 a
ex2 32.0 36.20 43.866 42.7 51.85 82.5 100 a
注意
set.seed(123)
M <- matrix(rbinom(25,1,0.5),5,5)
Mlist <- apply(M==1, 1, which, simplify = FALSE)
【讨论】:
添加了基准。 这是一个聪明的方法,但似乎不能很好地适应更大的矩阵。 5x5 的速度相当快,但 1000x1000 的速度较慢:ex0 = 2.86ms,ex1 = 7.58ms,ex2 = 4.89ms。【参考方案2】:使用矢量化行/列索引 - rep
licate 'Mlist' 的序列由'Mlist' 的lengths
和cbind
和unlist
ed 'Mlist' 创建一个@987654325 @ 可用于将 'M.new' 的元素子集分配给 1
ind <- cbind(rep(seq_along(Mlist), lengths(Mlist)), unlist(Mlist))
M.new[ind] <- 1
-检查
> all.equal(M, M.new)
[1] TRUE
或者另一个选项是sparseMatrix
library(Matrix)
as.matrix(sparseMatrix(i = rep(seq_along(Mlist), lengths(Mlist)),
j = unlist(Mlist), x = 1))
[,1] [,2] [,3] [,4] [,5]
[1,] 0 0 1 1 1
[2,] 0 1 0 1 0
[3,] 1 0 0 1 0
[4,] 0 1 0 1 0
[5,] 1 0 1 1 1
【讨论】:
谢谢!这两种方法都有效,但它们的运行时间似乎比原始方法长。当M
为10000x10000矩阵时,indexed方式耗时约3倍,sparseMatrix方式耗时约6倍。以上是关于将邻接列表转换为 R 中的二进制矩阵的主要内容,如果未能解决你的问题,请参考以下文章