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.