Reputation: 5997
I have a matrix with diagonals equal to zero and off-diagonals all equal to one (the inverse of an identity matrix):
mat1 <- matrix(c(0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0), 5, 5)
I also have a vector that is always the same length as the dims of the matrix and always starts at zero:
vec1 <- c(0,1,2,3,4)
using these two objects I want to create a matrix that looks like this:
mat2 <- matrix(c(0,1,2,3,4,1,0,1,2,3,2,1,0,1,2,3,2,1,0,1,4,3,2,1,0), 5, 5)
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 2 3 4
[2,] 1 0 1 2 3
[3,] 2 1 0 1 2
[4,] 3 2 1 0 1
[5,] 4 3 2 1 0
I want an operation that will generalize so that if I have a matrix of dims 9 by 9, for example, and a vector of 0:8 I can achieve the equivalent result. Any ideas for how to approach this?
Upvotes: 5
Views: 1304
Reputation: 66874
How about:
genMat <- function(n){
mat <- outer(1:n,1:n,"-")%%n
tmp <- mat[lower.tri(mat)]
mat <- t(mat)
mat[lower.tri(mat)] <- tmp
mat
}
> genMat(5)
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 2 3 4
[2,] 1 0 1 2 3
[3,] 2 1 0 1 2
[4,] 3 2 1 0 1
[5,] 4 3 2 1 0
Edit
For arbitrary vec1
:
genMat2 <- function(vec){
n <- length(vec)
mat <- outer(1:n,1:n,"-")%%n
tmp <- mat[lower.tri(mat)]
mat <- t(mat)
mat[lower.tri(mat)] <- tmp
matrix(vec[mat+1],n,n)
}
> genMat2(c(0,2,4,3,9))
[,1] [,2] [,3] [,4] [,5]
[1,] 0 2 4 3 9
[2,] 2 0 2 4 3
[3,] 4 2 0 2 4
[4,] 3 4 2 0 2
[5,] 9 3 4 2 0
Edit 2
In fact, there's no need to use the modulus and then play with the matrix, abs
will work fine to make the original matrix definition a 1-liner:
abs(outer(1:n,1:n,"-"))
So,
genMat <- function(n){
abs(outer(1:n,1:n,"-"))
}
and
genMat2 <- function(vec){
n <- length(vec)
matrix(vec[abs(outer(1:n,1:n,"-"))+1],n,n)
}
Upvotes: 1
Reputation: 179588
The following solution makes use of upper.tri
and lower.tri
to isolate the upper and lower triangular matrix. In addition, it makes use of sequence
to create the desired vector sequence.
n <- 9
vec <- (1:n)-1
m <- matrix(0, n, n)
m[lower.tri(m, diag=TRUE)] <- vec[sequence(n:1)] #### Edit
m <- t(m)
m[lower.tri(m, diag=TRUE)] <- vec[sequence(n:1)] #### Edit
m
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] 0 1 2 3 4 5 6 7 8
[2,] 1 0 1 2 3 4 5 6 7
[3,] 2 1 0 1 2 3 4 5 6
[4,] 3 2 1 0 1 2 3 4 5
[5,] 4 3 2 1 0 1 2 3 4
[6,] 5 4 3 2 1 0 1 2 3
[7,] 6 5 4 3 2 1 0 1 2
[8,] 7 6 5 4 3 2 1 0 1
[9,] 8 7 6 5 4 3 2 1 0
Upvotes: 6
Reputation: 108623
As vec1 starts with a zero, then you can do :
MakeMatrix <- function(x){
n <- length(x)
id <- abs(rep(1:n,n)-rep(1:n,each=n)) + 1
matrix(x[id],ncol=n)
}
MakeMatrix(vec1)
So there's no need to take the mat1 in the input, as that one is actually redundant. You can just construct the matrix within the function.
The trick is in providing a sequence of id values to select from the vector, and then transform everything to a matrix.
Edit : If you're only going to use sequences, you could as well do :
MakeMatrix <- function(n){
id <- abs(rep(1:n,n)-rep(1:n,each=n))
matrix(id,ncol=n)
}
MakeMatrix(7)
Upvotes: 6