1

The names of columns and rows in my matrix relate to groups, which have different relationships to other groups. I want to create a matrix where the values are based on the row and column names, and the corresponding relationship.

I have created an inefficient way to do this, which is okay for a small 3*3 matrix, but is not practical for large matrices.

My example data is as follows:

tom <- data.frame("w"=c(7,1,2),"x"=c(2,4,4),"y"=c(12,4,8))
row.names(tom) <- colnames(tom)

same <- data.frame("trait"=c("w","x","y"),
                   "group"=c(1,2,2),
                   "own_group_relationship"=c(0.86,0.55,0.55))

diff <- data.frame("trait"=c("w","x","y"),
                    "diff_group_relationship"=c(0.23,0.23,0.23))

My statement is as such:

a1 <- if ( row.names(tom[c(1),]) == colnames(tom[c(1)]) ) {
  merge(data.frame("trait" = row.names(tom[c(1),])),same)[1,3]
} else {
  merge(data.frame("trait" = row.names(tom[c(1),])),diff)[1,2]
}

This currently applies to one element at a time. I have had to repeat this code 8 more times, changing the corresponding row names to get 9 values (a1 to a9).

a2 <- if ( row.names(tom[c(1),]) == colnames(tom[c(2)]) ) {merge(data.frame("trait" = row.names(tom[c(1),])),same)[1,3]} else {merge(data.frame("trait" = row.names(tom[c(1),])),diff)[1,2]}   
a3 <- if ( row.names(tom[c(1),]) == colnames(tom[c(3)]) ) {merge(data.frame("trait" = row.names(tom[c(1),])),same)[1,3]} else {merge(data.frame("trait" = row.names(tom[c(1),])),diff)[1,2]}   
a4 <- if ( row.names(tom[c(2),]) == colnames(tom[c(1)]) ) {merge(data.frame("trait" = row.names(tom[c(1),])),same)[1,3]} else {merge(data.frame("trait" = row.names(tom[c(1),])),diff)[1,2]}   
a5 <- if ( row.names(tom[c(2),]) == colnames(tom[c(2)]) ) {merge(data.frame("trait" = row.names(tom[c(1),])),same)[1,3]} else {merge(data.frame("trait" = row.names(tom[c(1),])),diff)[1,2]}   
a6 <- if ( row.names(tom[c(2),]) == colnames(tom[c(3)]) ) {merge(data.frame("trait" = row.names(tom[c(1),])),same)[1,3]} else {merge(data.frame("trait" = row.names(tom[c(1),])),diff)[1,2]}   
a7 <- if ( row.names(tom[c(3),]) == colnames(tom[c(1)]) ) {merge(data.frame("trait" = row.names(tom[c(1),])),same)[1,3]} else {merge(data.frame("trait" = row.names(tom[c(1),])),diff)[1,2]}   
a8 <- if ( row.names(tom[c(3),]) == colnames(tom[c(2)]) ) {merge(data.frame("trait" = row.names(tom[c(1),])),same)[1,3]} else {merge(data.frame("trait" = row.names(tom[c(1),])),diff)[1,2]}   
a9 <- if ( row.names(tom[c(3),]) == colnames(tom[c(3)]) ) {merge(data.frame("trait" = row.names(tom[c(1),])),same)[1,3]} else {merge(data.frame("trait" = row.names(tom[c(1),])),diff)[1,2]}   

These 9 values are easily converted to a 3*3 matrix, however there has to be a more elegant solution.

vec <- c(a1,a2,a3,a4,a5,a6,a7,a8,a9)
mtrx <- matrix(vec, nrow=3, ncol=3)
mtrx # the resulting matrix of group inter group relationships
     [,1] [,2] [,3]
[1,] 0.86 0.23 0.23
[2,] 0.23 0.86 0.23
[3,] 0.23 0.23 0.86
lmo
  • 37,904
  • 9
  • 56
  • 69
Tom Kirk
  • 13
  • 2
  • Can't you just use something like `vec <- matrix(NA, 3, 3); diag(vec) <- "condition A"; vec[row(vec) != col(vec)] <- "condition B"`? Similar to [this question](http://stackoverflow.com/questions/13049575/r-min-max-and-mean-of-off-diagonal-elements-in-a-matrix) – slamballais Jul 05 '16 at 12:09

1 Answers1

0

Here is a solution using outer and match:

outer(rownames(tom), colnames(tom),
      FUN=function(x, y) {
                          (x==y) * same$own_group_relationship[match(x, same$trait)] +
                          (x!=y) * diff$diff_group_relationship[match(x, diff$trait)]
      })

Which returns

     [,1] [,2] [,3]
[1,] 0.86 0.23 0.23
[2,] 0.23 0.55 0.23
[3,] 0.23 0.23 0.55

The inner function produces a vector that will pull out the correct values depending on whether or not the row and column names match. The outer function returns the correct positions and dimensions.

lmo
  • 37,904
  • 9
  • 56
  • 69