Эффективный способ превратить матрицу в триплет (i, j, v) - PullRequest
0 голосов
/ 28 августа 2018

Что

Учитывая некоторые 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

У меня также была другая версия, использующая apply и sapply вместо for, но они взорвались бы очень быстро. Размеры, над которыми я работаю, довольно большие, матрицы 1600х1600.

Почему

Кто-то может спросить «почему»? Причина этого в том, что мне нужны i s и j s как функции для модели, предсказывающей v. Если есть лучший способ сделать это, мне интересно услышать.

Ответы [ 4 ]

0 голосов
/ 28 августа 2018

Просто для удовольствия, вот вариант с использованием пакета 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
0 голосов
/ 28 августа 2018

Вы можете сделать это с row и col:

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
0 голосов
/ 28 августа 2018

Для тех, кто действительно любит expand.grid:

cbind(expand.grid(seq(nrow(mat)), seq(ncol(mat))), as.vector(mat))
0 голосов
/ 28 августа 2018

Самый простой способ - использовать аргумент 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, со всеми значениями как TRUE, используя relist

cbind(which(relist(TRUE, mat), arr.ind = TRUE), value = as.vector(mat))


#      row col value
# [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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...