0

Apologies in advance, I had a hard time titling this question. I have a species interaction matrix where the columns and rows represent species, and the matrix fill is the frequency of their interactions:

matrix<-   2A   2B   2C   
         1A 1   2    4      
         1B 0   1    1    
         1C 5   4    1    

I want to make a matrix with the following form:

 mat<-        
            comm
     1A_2A   1
     1A_2B   2
     1A_2C   4
     1B_2A   0
     1B_2B   1
     1B_2C   1
     1C_2A   5
     1C_2B   4
     1C_2C   1

where the species interaction becomes the rows and the column can be any variable, but the matrix fill remains the frequency of interactions and the structure remains a matrix. This way the matrix takes the form of a community x species matrix. The reason I want to collapse the matrix in this way and retain matrix form is to enable use of functions in library(vegetarian) such as d() which requires a matrix form.

Danielle
  • 785
  • 7
  • 15
  • Possible duplicate of [Reshaping data.frame from wide to long format](https://stackoverflow.com/questions/2185252/reshaping-data-frame-from-wide-to-long-format) – Ronak Shah Jun 30 '17 at 01:05
  • I am under the impression If I use the reshape package the structure would become a dataframe. Will the structure remain a matrix? – Danielle Jun 30 '17 at 02:00
  • you absorb one column into new row labels, drop that column and are left with a 9x1 numerical thingie – Dinesh Jun 30 '17 at 02:17
  • There's no reason to maintain a matrix, when a data.frame would be more appropriate. – thc Jun 30 '17 at 03:13
  • @thc that's your opinion, but OP has explicitly explained why (s)he wants to have a matrix as a result – Jaap Jul 01 '17 at 09:40
  • @Jaap it's not "just my opinion". It is much less effort to first convert to data.frame and gather with dplyr, since that is what dplyr is for. Also, if they need to put it into a matrix form, they can do a simple conversion before the call which requires matrix form. – thc Jul 02 '17 at 06:48
  • @thc Although they often are, dataframes aren't always the best format of storing values. Neither is `dplyr` the answer to everything. OP has explicitly explained why a matrix is needed. Why would you then convert a matrix to a dataframe and later convert back to a matrix when that isn't needed? – Jaap Jul 02 '17 at 17:01
  • @Jaap Because it takes less effort to think about, and R isn't about extracting every ounce of efficiency. For example, take a look at the first line of your answer: `m2 <- matrix(t(m1))`. This is esoteric and requires understanding the order in how `matrix` transforms a wide matrix into a skinny matrix. Even if you do know the answer to that, you still have to "work it out" both after transpose and in effort to ensure your labels are in the correct order. On the other hand, `gather` always does the same thing as you'd expect and doesn't require you to think much of anything. – thc Jul 03 '17 at 03:41

2 Answers2

1

Using:

m2 <- matrix(t(m1))
dimnames(m2) <- list(paste(rep(rownames(m1), each = nrow(m1)),
                           colnames(m1), sep = '_'),
                     'comm')

gives:

> m2
      comm
1A_2A    1
1A_2B    2
1A_2C    4
1B_2A    0
1B_2B    1
1B_2C    1
1C_2A    5
1C_2B    4
1C_2C    1

What this does:

  • You can see a matrix as a two-dimensional vector. To get a vector you can simply use c(m1). This will give a vector with the values of the first column first, then the values of the second column and so on. To get the values in rowwise order, you can transpose the matrix with t(m1).
  • By wrapping that in matrix you'll get a one-column matrix, which is the default behavior when neither the number of columns or rows is specified. Consequently, matrix(t(m1)) is the same as matrix(t(m1), ncol = 1).
  • Finally, you create a list of length 2 with rownames (paste(rep(rownames(m1), each = nrow(m1)), colnames(m1), sep = '_') and columnnames ('comm) and assign that to dimnames. The new rownames is created by repeating each rowname of m1 as many times as the number of columns in m1 and pasting those with a vector of the column names (which will be recycled).

If you want the values in column order, you can adapt the above code to:

m2 <- matrix(m1)
dimnames(m2) <- list(paste(rownames(m1),
                           rep(colnames(m1), each = ncol(m1)), sep = '_'),
                     'comm')

which gives the same result but in a different order:

> m2
      comm
1A_2A    1
1B_2A    0
1C_2A    5
1A_2B    2
1B_2B    1
1C_2B    4
1A_2C    4
1B_2C    1
1C_2C    1

Alternatively you could also use the reshape2-package:

library(reshape2)
d1 <- melt(m1)
rownames(d1) <- paste(d1$Var1, d1$Var2, sep = '_')
d1 <- d1[, 3, drop = FALSE]

which returns a dataframe:

> d1
      value
1A_2A     1
1B_2A     0
1C_2A     5
1A_2B     2
1B_2B     1
1C_2B     4
1A_2C     4
1B_2C     1
1C_2C     1

To get a matrix, you can just wrap d1in as.matrix: as.matrix(d1).


Used data:

m1 <- matrix(c(1,2,4,0,1,1,5,4,1), ncol = 3, byrow = TRUE,
             dimnames = list(c('1A','1B','1C'),c('2A','2B','2C')))
Jaap
  • 81,064
  • 34
  • 182
  • 193
  • 1
    Thank you for your helpful solution. I suspected this question would provoke some people, but for a novice R user like myself, solutions like yours help me understand the language better, than it may for someone these things are more evident to. I especially appreciate your annotations which explain your methods clearly. So thank you for investing your time in a solution that helped me rather than unhelpful criticism. – Danielle Jul 03 '17 at 18:25
  • Thank you for your helpful solution. I suspected this question would provoke some people, but for a novice R user like myself, solutions like yours help me understand the language better, than it may for someone these things are more evident to. I especially appreciate your annotations which explain your methods clearly. So thank you for investing your time in a solution that helped me rather than unhelpful criticism. – Danielle Jul 03 '17 at 18:25
  • @Danielle Glad I could help. I agree that its good to try to understand the language better. I'm also still learning new things about R regularly :-) Among other things by answering questions like yours'. – Jaap Jul 03 '17 at 19:44
-2

Use dplyr/tidyverse:

library(tidyverse)

df <-   read.table(textConnection("2A   2B   2C   
         1A 1   2    4      
         1B 0   1    1    
         1C 5   4    1"), check.names=F)

new_df <- df %>% rownames_to_column("Species_1") %>% gather(key="Species_2", value="Interactions", -Species_1)


Species_1 Species_2 Interactions
1        1A        2A            1
2        1B        2A            0
3        1C        2A            5
4        1A        2B            2
5        1B        2B            1
6        1C        2B            4
7        1A        2C            4
8        1B        2C            1
9        1C        2C            1
thc
  • 9,527
  • 1
  • 24
  • 39