Steve
Steve

Reputation: 5997

Create a similarity matrix of integers, using R

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

Answers (3)

James
James

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

Andrie
Andrie

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

Joris Meys
Joris Meys

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

Related Questions