如何有效地在(稀疏)矩阵中插入行?
How to insert row in a (sparse) matrix efficiently?
我有一个巨大的稀疏矩阵,需要在特定的行索引处插入一行。我基本上应用了建议的技术 here,但效率非常低,因为它复制了原始矩阵。
下面简单介绍一下代码,范围是高效生成M2
(理想情况下,直接替换M
,这样就连M2
都不需要了):
library(Matrix)
M = as(cbind(1:4,1:4),"sparseMatrix") # original sparse matrix
M # view
v = c(2,4) # row indices where new rows should be inserted
M2 = M # copy matrix to keep sparse class
for(i in 1:length(v)) M2 = rbind(M2,rep(0, ncol(M2))) # insert new rows at end
M2[-v,] = M # get each row in v 2 times ## <- THIS takes very long
M2[v,] = 0 # populate the newly inserted rows (e.g. here: 0)
M2 # view
理想情况下,我正在寻找一个函数,例如 append
用于向量或 add_row
用于小标题。
编辑:我注意到上面的过程不仅效率低下,而且也不是我想要的,因为它在 new 的索引 2 和 4 处添加了行(结果)矩阵 M2,而我正在寻找在 old(初始)矩阵的索引 2 和 4 处添加行,即现在的最终结果是:
[1,] 1 1
[2,] . .
[3,] 2 2
[4,] . .
[5,] 3 3
[6,] 4 4
即在新矩阵的索引 2 和 4 处添加了行。我要找的是:
[1,] 1 1
[2,] . .
[3,] 2 2
[4,] 3 3
[5,] . .
[6,] 4 4
即在旧矩阵的索引 2 和 4 之前添加了新行。在他的 +1 回答中,@Ben Bolker 正确地解释了这一点。 :)
我认为这应该相当有效地工作:它以稀疏(dgCMatrix
或 dgTMatrix
)格式处理矩阵的基础行索引和维度。
add_rows <- function(M,v) {
oldind <- seq(dim(M)[1])-1L ## all rows (0-indexed)
## new row indices: count how many rows are inserted before a given row
## (maybe a clearer/better/more efficient way to do this?)
newind <- oldind + as.integer(rowSums(outer(oldind,v,">=")))
## modify dimensions
M@Dim <- M@Dim + c(length(v),0L)
## modify row indices (1L accounts for 0 vs 1-indexing)
M@i <- newind[M@i+1L]
M
}
最有可能成为 (memory/computation) 瓶颈的部分是新行索引的计算。我通过构建 all 行号的索引来做到这一点。下面的版本只对占用的行进行 newind
计算,代价是额外调用 unique()
和 match()
.
add_rows_2 <- function(M,v) {
oldind <- unique(M@i)
## new row indices
newind <- oldind + as.integer(rowSums(outer(oldind,v,">=")))
## modify dimensions
M@Dim <- M@Dim + c(length(v),0L)
M@i <- newind[match(M@i,oldind)]
M
}
我有一个巨大的稀疏矩阵,需要在特定的行索引处插入一行。我基本上应用了建议的技术 here,但效率非常低,因为它复制了原始矩阵。
下面简单介绍一下代码,范围是高效生成M2
(理想情况下,直接替换M
,这样就连M2
都不需要了):
library(Matrix)
M = as(cbind(1:4,1:4),"sparseMatrix") # original sparse matrix
M # view
v = c(2,4) # row indices where new rows should be inserted
M2 = M # copy matrix to keep sparse class
for(i in 1:length(v)) M2 = rbind(M2,rep(0, ncol(M2))) # insert new rows at end
M2[-v,] = M # get each row in v 2 times ## <- THIS takes very long
M2[v,] = 0 # populate the newly inserted rows (e.g. here: 0)
M2 # view
理想情况下,我正在寻找一个函数,例如 append
用于向量或 add_row
用于小标题。
编辑:我注意到上面的过程不仅效率低下,而且也不是我想要的,因为它在 new 的索引 2 和 4 处添加了行(结果)矩阵 M2,而我正在寻找在 old(初始)矩阵的索引 2 和 4 处添加行,即现在的最终结果是:
[1,] 1 1
[2,] . .
[3,] 2 2
[4,] . .
[5,] 3 3
[6,] 4 4
即在新矩阵的索引 2 和 4 处添加了行。我要找的是:
[1,] 1 1
[2,] . .
[3,] 2 2
[4,] 3 3
[5,] . .
[6,] 4 4
即在旧矩阵的索引 2 和 4 之前添加了新行。在他的 +1 回答中,@Ben Bolker 正确地解释了这一点。 :)
我认为这应该相当有效地工作:它以稀疏(dgCMatrix
或 dgTMatrix
)格式处理矩阵的基础行索引和维度。
add_rows <- function(M,v) {
oldind <- seq(dim(M)[1])-1L ## all rows (0-indexed)
## new row indices: count how many rows are inserted before a given row
## (maybe a clearer/better/more efficient way to do this?)
newind <- oldind + as.integer(rowSums(outer(oldind,v,">=")))
## modify dimensions
M@Dim <- M@Dim + c(length(v),0L)
## modify row indices (1L accounts for 0 vs 1-indexing)
M@i <- newind[M@i+1L]
M
}
最有可能成为 (memory/computation) 瓶颈的部分是新行索引的计算。我通过构建 all 行号的索引来做到这一点。下面的版本只对占用的行进行 newind
计算,代价是额外调用 unique()
和 match()
.
add_rows_2 <- function(M,v) {
oldind <- unique(M@i)
## new row indices
newind <- oldind + as.integer(rowSums(outer(oldind,v,">=")))
## modify dimensions
M@Dim <- M@Dim + c(length(v),0L)
M@i <- newind[match(M@i,oldind)]
M
}