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])

This work is licensed under a Creative Commons

Attribution 4.0 International License.

If x is an upper triangle matrix, then x <- x + t(x) should do the trick.

Thanks for the tip, which is more concise.