给出一些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
的版本,但是它们很快就会爆炸。我正在处理的尺寸非常大,1600x1600矩阵。
可能会问“为什么”?,原因是我需要i
和j
作为模型的特征来预测v
。如果有更好的方法可以做到这一点,我很想听听。
答案 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)
您可以使用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
答案 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