将邻接列表转换为 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】:

使用矢量化行/列索引 - replicate 'Mlist' 的序列由'Mlist' 的lengthscbindunlisted '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 中的二进制矩阵的主要内容,如果未能解决你的问题,请参考以下文章

R:将二进制文件转换为矩阵

R中igraph中转置边缘列表的邻接

计算/翻译R中二进制矩阵/向量中的数字向量

将 arma::mat 邻接矩阵转换为 C 中的 igraph 图 (Rcpp)

如何将矩阵转换为 R 中的列向量列表?

将 1.2GB 的边列表转换为稀疏矩阵