Making symmetric matrices in R

A quick short post on making symmetric matrices in R, as it could potentially be a nasty gotcha. So in R, there are two functions for accessing the lower and upper triangular part of a matrix, called lower.tri() and upper.tri() respectively. Let me illustrate:

#make a small 10 by 10 matrix
small <- matrix(rep(0,10*10), nrow=10)
small
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    0    0    0    0    0    0    0    0    0     0
 [2,]    0    0    0    0    0    0    0    0    0     0
 [3,]    0    0    0    0    0    0    0    0    0     0
 [4,]    0    0    0    0    0    0    0    0    0     0
 [5,]    0    0    0    0    0    0    0    0    0     0
 [6,]    0    0    0    0    0    0    0    0    0     0
 [7,]    0    0    0    0    0    0    0    0    0     0
 [8,]    0    0    0    0    0    0    0    0    0     0
 [9,]    0    0    0    0    0    0    0    0    0     0
[10,]    0    0    0    0    0    0    0    0    0     0

upper.tri(small)
       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9] [,10]
 [1,] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [2,] FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [3,] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [4,] FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [5,] FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
 [6,] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE
 [7,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE
 [8,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE
 [9,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
[10,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

lower.tri(small)
       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9] [,10]
 [1,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [2,]  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [3,]  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [4,]  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [5,]  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE
 [6,]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE
 [7,]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE
 [8,]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE
 [9,]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE
[10,]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE

So now if I populate my matrix with a sequence of numbers as so:

small[lower.tri(small)] <- seq(from=1,to=choose(10,2),by=1)
small[upper.tri(small)] <- seq(from=1,to=choose(10,2),by=1)
small
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    0    1    2    4    7   11   16   22   29    37
 [2,]    1    0    3    5    8   12   17   23   30    38
 [3,]    2   10    0    6    9   13   18   24   31    39
 [4,]    3   11   18    0   10   14   19   25   32    40
 [5,]    4   12   19   25    0   15   20   26   33    41
 [6,]    5   13   20   26   31    0   21   27   34    42
 [7,]    6   14   21   27   32   36    0   28   35    43
 [8,]    7   15   22   28   33   37   40    0   36    44
 [9,]    8   16   23   29   34   38   41   43    0    45
[10,]    9   17   24   30   35   39   42   44   45     0

I would result in a non-symmetrical matrix. What has happened is that the sequence of numbers has been added column by column. So what we have to do is transpose the matrix first:

#make a small 10 by 10 matrix
small <- matrix(rep(0,10*10), nrow=10)
small[lower.tri(small)] <- seq(from=1,to=choose(10,2),by=1)
small
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    0    0    0    0    0    0    0    0    0     0
 [2,]    1    0    0    0    0    0    0    0    0     0
 [3,]    2   10    0    0    0    0    0    0    0     0
 [4,]    3   11   18    0    0    0    0    0    0     0
 [5,]    4   12   19   25    0    0    0    0    0     0
 [6,]    5   13   20   26   31    0    0    0    0     0
 [7,]    6   14   21   27   32   36    0    0    0     0
 [8,]    7   15   22   28   33   37   40    0    0     0
 [9,]    8   16   23   29   34   38   41   43    0     0
[10,]    9   17   24   30   35   39   42   44   45     0

#transpose matrix
small <- t(small)
small
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    0    1    2    3    4    5    6    7    8     9
 [2,]    0    0   10   11   12   13   14   15   16    17
 [3,]    0    0    0   18   19   20   21   22   23    24
 [4,]    0    0    0    0   25   26   27   28   29    30
 [5,]    0    0    0    0    0   31   32   33   34    35
 [6,]    0    0    0    0    0    0   36   37   38    39
 [7,]    0    0    0    0    0    0    0   40   41    42
 [8,]    0    0    0    0    0    0    0    0   43    44
 [9,]    0    0    0    0    0    0    0    0    0    45
[10,]    0    0    0    0    0    0    0    0    0     0

#populate the lower triangle, which
#was formerly the upper triangle
small[lower.tri(small)] <- seq(from=1,to=choose(10,2),by=1)
small
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    0    1    2    3    4    5    6    7    8     9
 [2,]    1    0   10   11   12   13   14   15   16    17
 [3,]    2   10    0   18   19   20   21   22   23    24
 [4,]    3   11   18    0   25   26   27   28   29    30
 [5,]    4   12   19   25    0   31   32   33   34    35
 [6,]    5   13   20   26   31    0   36   37   38    39
 [7,]    6   14   21   27   32   36    0   40   41    42
 [8,]    7   15   22   28   33   37   40    0   43    44
 [9,]    8   16   23   29   34   38   41   43    0    45
[10,]    9   17   24   30   35   39   42   44   45     0

#check symmetry using the isSymmetric() function
isSymmetric(small)
[1] TRUE

#change the diagonal
diag(small) <- 1:10
small
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    1    1    2    3    4    5    6    7    8     9
 [2,]    1    2   10   11   12   13   14   15   16    17
 [3,]    2   10    3   18   19   20   21   22   23    24
 [4,]    3   11   18    4   25   26   27   28   29    30
 [5,]    4   12   19   25    5   31   32   33   34    35
 [6,]    5   13   20   26   31    6   36   37   38    39
 [7,]    6   14   21   27   32   36    7   40   41    42
 [8,]    7   15   22   28   33   37   40    8   43    44
 [9,]    8   16   23   29   34   38   41   43    9    45
[10,]    9   17   24   30   35   39   42   44   45    10

One reason for manipulating the lower and upper portion of a matrix is perhaps one would like to store the Pearson correlation coefficients on the upper triangle and the Spearman's rank correlation coefficients on the lower triangle. Just make sure you transpose the matrix before adding the correlations in.

To end, here's a quick way of visualising the matrix:

#I reversed the column order
#so that the heatmap looks
#the same as the matrix
small
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    1    1    2    3    4    5    6    7    8     9
 [2,]    1    2   10   11   12   13   14   15   16    17
 [3,]    2   10    3   18   19   20   21   22   23    24
 [4,]    3   11   18    4   25   26   27   28   29    30
 [5,]    4   12   19   25    5   31   32   33   34    35
 [6,]    5   13   20   26   31    6   36   37   38    39
 [7,]    6   14   21   27   32   36    7   40   41    42
 [8,]    7   15   22   28   33   37   40    8   43    44
 [9,]    8   16   23   29   34   38   41   43    9    45
[10,]    9   17   24   30   35   39   42   44   45    10
image(small[,10:1])

image_example

Print Friendly, PDF & Email



Creative Commons License
This work is licensed under a Creative Commons
Attribution 4.0 International License
.
Posted in RTagged
3 comments Add yours

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.