Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a similarity matrix of integers, using R

Tags:

r

matrix

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?

like image 246
Steve Avatar asked Apr 19 '11 14:04

Steve


3 Answers

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)
like image 142
Joris Meys Avatar answered Oct 27 '22 10:10

Joris Meys


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
like image 43
Andrie Avatar answered Oct 27 '22 12:10

Andrie


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)
}
like image 44
James Avatar answered Oct 27 '22 11:10

James