有效地将非零矩阵元素的索引保存到文件中

Efficiently save indices of nonzero matrix elements to a file

我需要将矩阵的非零元素的索引保存到文件中。这对于小型矩阵非常有效,在 a 中存储非零索引的行号,在 b:

中存储非零索引的列号
X <- matrix(c(1,0,3,4,0,5), byrow=TRUE, nrow=2);    
a <- row(X)[which(!X == 0)]
b <- col(X)[which(!X == 0)]

但是矩阵的大小很大,我需要找到一种有效的方法将索引保存到 txt 文件中,这样我就有了 a[1] b[1](新行)a[2] b[2] 等等。有什么建议吗?

Matrix 对超大矩阵有很好的解决方案。 sparseMatrix 对象可以概括为 data.frame,其中 ij 是您的索引,x 是值:

X <- matrix(c(1,0,3,4,0,5), byrow=TRUE, nrow=2);    
a <- row(X)[which(!X == 0)]
b <- col(X)[which(!X == 0)]

library(Matrix)
Y <- Matrix(X, sparse = TRUE)
(res <- summary(Y))
  2 x 3 sparse Matrix of class "dgCMatrix", with 4 entries 
    i j x
  1 1 1 1
  2 2 1 4
  3 1 3 3
  4 2 3 5
class(res)
  [1] "sparseSummary" "data.frame"   

然后您可以子集得到 ij:

res[, c("i", "j")] 
   i j
 1 1 1
 2 2 1
 3 1 3
 4 2 3

您可以使用 which 和参数 arr.ind=TRUE 获取所有非零位置的行和列,将结果写入文件 write.table:

write.table(which(X != 0, arr.ind=TRUE), "file.txt", row.names=F, col.names=F)

这会产生指定文件中元素对的 space 分隔输出:

1 1
2 1
1 3
2 3

与问题中发布的代码相比,将 whicharr.ind=TRUE 结合使用可以节省对输入矩阵的几次扫描,因此在计算要输出的数据时应该会更快一些。您可以通过更大矩阵(1000 x 1000,密度为 1%)的基准看到这一点:

set.seed(144)
bigX <- matrix(sample(c(rep(0, 99), 1), 1000000, replace=T), nrow=1000)
OP <- function(X) cbind(row(X)[which(!X == 0)], col(X)[which(!X == 0)])
josilber <- function(X) which(X != 0, arr.ind=TRUE)

library(microbenchmark)
microbenchmark(OP(bigX), josilber(bigX))
# Unit: milliseconds
#            expr       min        lq      mean    median        uq      max neval
#        OP(bigX) 20.513535 23.014517 36.463423 25.354250 59.130520 65.50304   100
#  josilber(bigX)  3.873165  4.281624  6.741824  5.250777  6.998415 45.02542   100

在这种情况下,我们看到计算非零行和列的速度提高了大约 5 倍。根据矩阵的密度和大小,输出操作 (write.table) 可能反而成为瓶颈,在这种情况下,这种方法可能没有太多好处。