将矩阵转换为三元组(i,j,v)的有效方法

时间:2018-08-28 05:11:06

标签: r

什么

给出一些matrix

mat <- matrix(1:10,ncol=2)

我想将其转换为以下三元组格式:(i,j,v)其中i是行索引,j是列索引,v是值在i,j(您可以在底部看到为什么

我尝试过的:

  matrixToTriplet <- function(mat) {
    i <- 1:nrow(mat)
    nj <- ncol(mat)
    j <- 1:nj
    output <- matrix(numeric(0), ncol=3)
    for(i_ in i) {
      curr <- c(rep(i_, times=nj),j,mat[i_,])
      output <- rbind(output, matrix(curr, ncol=3))
    }
    output
  }

输出应为:

> matrixToTriplet(mat)
      [,1] [,2] [,3]
 [1,]    1    1    1
 [2,]    1    2    6
 [3,]    2    1    2
 [4,]    2    2    7
 [5,]    3    1    3
 [6,]    3    2    8
 [7,]    4    1    4
 [8,]    4    2    9
 [9,]    5    1    5
[10,]    5    2   10

我还有一个使用applysapply而不是for的版本,但是它们很快就会爆炸。我正在处理的尺寸非常大,1600x1600矩阵。

为什么

可能会问“为什么”?,原因是我需要ij作为模型的特征来预测v。如果有更好的方法可以做到这一点,我很想听听。

4 个答案:

答案 0 :(得分:2)

最简单的方法是将which参数与arr.ind= TRUE参数一起使用,它确实可以实现您想要的功能,但是问题是它期望逻辑值。因此,我们需要找到一个条件,其中所有值都变为TRUE。在这种情况下,我看到所有值都大于0。所以我们可以做到

#as.vector suggested by @snoram and verified by @mt1022 that it is faster
cbind(which(mat > 0, arr.ind = TRUE), val = as.vector(mat))


#      row col val
# [1,]   1   1   1
# [2,]   2   1   2
# [3,]   3   1   3
# [4,]   4   1   4
# [5,]   5   1   5
# [6,]   1   2   6
# [7,]   2   2   7
# [8,]   3   2   8
# [9,]   4   2   9
#[10,]   5   2  10

如果找不到任何使所有值都为TRUE的条件,我们可以创建一个尺寸与mat相同且所有值为{{1}的新矩阵}使用TRUE

relist

答案 1 :(得分:2)

您可以使用rowcol进行此操作:

x <- t(mat)
cbind(c(col(x)), c(row(x)), c(x))
#       [,1] [,2] [,3]
# [1,]    1    1    1
# [2,]    1    2    6
# [3,]    2    1    2
# [4,]    2    2    7
# [5,]    3    1    3
# [6,]    3    2    8
# [7,]    4    1    4
# [8,]    4    2    9
# [9,]    5    1    5
# [10,]    5    2   10

如果最终输出中的行顺序无关紧要,我们也可以直接使用cbind(c(row(mat)), c(col(mat)), c(mat))来完成。

基准在谈论效率时会有所帮助:

library(microbenchmark)
bmf <- function(mat, ...){
    microbenchmark(
        a = {x <- t(mat);cbind(c(col(x)), c(row(x)), c(x))},
        a2 = {cbind(c(row(mat)), c(col(mat)), c(mat))},
        b = {cbind(which(mat > 0, arr.ind = TRUE), val = c(mat))},
        c = {cbind(expand.grid(seq(nrow(mat)), seq(ncol(mat))), as.vector(mat))},
        ...)
}

mat <- matrix(seq_len(10*10), 10, 10)
bmf(mat, times = 10)
# Unit: microseconds
#  expr     min      lq     mean   median      uq     max neval
#     a   7.985   9.239  18.2556  15.0415  22.756  47.065    10
#    a2   4.310   4.681   5.5257   5.2405   5.755   9.099    10
#     b  17.032  21.672  35.8950  28.7505  59.170  68.436    10
#     c 216.101 228.736 267.7217 243.9465 288.455 380.096    10'


mat <- matrix(seq_len(1000*1000), 1000, 1000)
bmf(mat, times = 10)

# Unit: milliseconds
#  expr      min       lq     mean   median        uq      max neval
#     a 17.70805 20.51167 36.73432 21.79357  24.56775 111.6796    10
#    a2 14.61793 20.95486 37.70526 25.58968 30.91322  98.44344    10
#     b 41.74630 45.49698 76.61307 47.86678 122.90142 178.8363    10
#     c 14.40912 17.84025 25.39672 19.29968  20.12222  85.2515    10

答案 2 :(得分:2)

对于真正喜欢expand.grid的人:

cbind(expand.grid(seq(nrow(mat)), seq(ncol(mat))), as.vector(mat))

答案 3 :(得分:0)

只是为了好玩,这是使用Matrix软件包的一个选项。

mat <- matrix(1:10,ncol=2)

#create sparse matrix
library(Matrix)
M <- Matrix(mat, sparse = TRUE)

#turn into triplet representation
M <- as(M, "TsparseMatrix")

#indices are zero-based within Matrix package
m <- cbind(M@i + 1, M@j + 1, M@x) #do you really need a matrix as output?
m[order(m[,1]),] #probably you don't need this step

    #      [,1] [,2] [,3]
    # [1,]    1    1    1
    # [2,]    1    2    6
    # [3,]    2    1    2
    # [4,]    2    2    7
    # [5,]    3    1    3
    # [6,]    3    2    8
    # [7,]    4    1    4
    # [8,]    4    2    9
    # [9,]    5    1    5
    #[10,]    5    2   10