16

How can I melt a lower half triangle plus diagonal matrix ?

11 NA NA  NA  NA
12 22 NA  NA  NA
13 23 33  NA  NA
14 24 34  44  NA
15 25 35  45  55
    A <- t(matrix (c(11,  NA, NA,  NA,  NA, 12, 22, NA,  NA,  NA,
   13, 23, 33,  NA,  NA, 14, 24, 34,  44,  NA,15, 25, 
   35,  45,  55), ncol = 5)) 

 > A
     [,1] [,2] [,3] [,4] [,5]
[1,]   11   NA   NA   NA   NA
[2,]   12   22   NA   NA   NA
[3,]   13   23   33   NA   NA
[4,]   14   24   34   44   NA
[5,]   15   25   35   45   55 

To data.frame in row and col (preserving the following order)

col  row   value 
1     1      11
1     2      12
1     3      13
1     4      14
1     5      15
2     2      22
2     3      23
2     4      24
2     5      25
3     3      33
3     4      34
3     5      35
4     4      44
4     5      45
5     5      55
Joshua Ulrich
  • 173,410
  • 32
  • 338
  • 418
jon
  • 11,186
  • 19
  • 80
  • 132

5 Answers5

19

If you want the indices as columns as well, this should work:

m <- matrix(1:25,5,5)
m[upper.tri(m)] <- NA
m

     [,1] [,2] [,3] [,4] [,5]
[1,]    1   NA   NA   NA   NA
[2,]    2    7   NA   NA   NA
[3,]    3    8   13   NA   NA
[4,]    4    9   14   19   NA
[5,]    5   10   15   20   25

cbind(which(!is.na(m),arr.ind = TRUE),na.omit(as.vector(m)))
      row col   
 [1,]   1   1  1
 [2,]   2   1  2
 [3,]   3   1  3
 [4,]   4   1  4
 [5,]   5   1  5
 [6,]   2   2  7
 [7,]   3   2  8
 [8,]   4   2  9
 [9,]   5   2 10
[10,]   3   3 13
[11,]   4   3 14
[12,]   5   3 15
[13,]   4   4 19
[14,]   5   4 20
[15,]   5   5 25

I guess I'll explain this a bit. I'm using three "tricks":

  1. The arr.ind argument to which to get the indices
  2. The very useful na.omit function to avoid some extra typing
  3. The fact that R stores matrices in column major form, hence as.vector returns the values in the right order.
joran
  • 169,992
  • 32
  • 429
  • 468
  • thanks this is great solution - how can we do column within rows (i.e. when level of rows 1 1 1 1...., the column will be 1 2 3 4 ...) – jon Nov 22 '11 at 11:38
  • If I understand what you mean, I think the same solution works, but you'll have to sort the result to have them listed in your desired order. – joran Nov 22 '11 at 15:22
12

My one liner.

reshape2::melt(A, varnames = c('row', 'col'), na.rm = TRUE)
Ramnath
  • 54,439
  • 16
  • 125
  • 152
1

Here's my first solution:

test <- rbind(c(11,NA,NA,NA,NA),
  c(12,22,NA,NA,NA),
  c(13,23,33,NA,NA),
  c(14,24,34,44,NA),
  c(15,25,35,45,55))  ## Load the matrix

test2 <- as.vector(test)  ## "melt" it into a vector

test <- cbind( test2[!is.na(test2)] )  ## get rid of NAs, cbind it into a column 

Results are:

> test
      [,1]
 [1,]   11
 [2,]   12
 [3,]   13
 [4,]   14
 [5,]   15
 [6,]   22
 [7,]   23
 [8,]   24
 [9,]   25
[10,]   33
[11,]   34
[12,]   35
[13,]   44
[14,]   45
[15,]   55

Alternatively, you can use the matrix command:

test <- rbind(c(11,NA,NA,NA,NA),
  c(12,22,NA,NA,NA),
  c(13,23,33,NA,NA),
  c(14,24,34,44,NA),
  c(15,25,35,45,55))  ## Load the matrix

test2 <- matrix(test, ncol=1)  
test <- cbind( test2[!is.na(test2), ] )   
  ## same as above, except now explicitly noting rows to replace.
CompEcon
  • 1,994
  • 1
  • 14
  • 12
  • This doesn't return the indices of the columns and rows. – John Nov 22 '11 at 04:04
  • Ah, apologies; I didn't realize you wanted all those as the result. I'd assumed that those were just to help indicate order of the components. One moment... – CompEcon Nov 22 '11 at 04:16
  • 1
    Actually the best way to do that is definitely the "which" command a la @joran's. – CompEcon Nov 22 '11 at 04:31
1

Here is my attempt:

# enter the data
df <- c(11,12,13,14,15,NA,22,23,24,25,NA,NA,33,34,35,NA,NA,NA,44,45,NA,NA,NA,NA,55)
dim(df) <- c(5,5)
df

# make new data frame with rows and column indicators
melteddf <- data.frame(
value=df[lower.tri(df,diag=T)],
col=rep(1:ncol(df),ncol(df):1),
row=unlist(sapply(1:nrow(df),function(x) x:nrow(df)))
)

I wish I knew about the arr.ind part of cbind which before now though.

thelatemail
  • 91,185
  • 12
  • 128
  • 188
1

Here is a method using arrayInd which is basically the same as @joran's but might be useful in other settings:

na.omit( data.frame(arrayInd(1:prod(dim(A)), dim(A)), value=c(A)) )
   X1 X2 value
1   1  1    11
2   2  1    12
3   3  1    13
4   4  1    14
5   5  1    15
7   2  2    22
8   3  2    23
9   4  2    24
10  5  2    25
13  3  3    33
14  4  3    34
15  5  3    35
19  4  4    44
20  5  4    45
25  5  5    55
IRTFM
  • 258,963
  • 21
  • 364
  • 487