This question already has answers here:
Creating a symmetric matrix in R
(7 answers)
Closed 5 years ago.
I know A'A will give a symmetric positive definite matrix. But how can I generate random matrix in R that is symmetric, but not necessary to be positive definite?
The details will of course depend on what distribution you'll want the matrix elements to have, but once you settle on that, you can adapt something like the following:
m <- matrix(sample(1:20, 36, replace=TRUE), nrow=6)
m[lower.tri(m)] <- t(m)[lower.tri(m)]
m
# [,1] [,2] [,3] [,4] [,5] [,6]
# [1,] 19 20 15 6 5 14
# [2,] 20 20 20 3 18 17
# [3,] 15 20 6 5 11 3
# [4,] 6 3 5 6 9 20
# [5,] 5 18 11 9 10 2
# [6,] 14 17 3 20 2 7
For ease of use, you can then wrap up code like that in a function, like so:
f <- function(n) {
m <- matrix(sample(1:20, n^2, replace=TRUE), nrow=n)
m[lower.tri(m)] <- t(m)[lower.tri(m)]
m
}
## Try it out
f(2)
# [,1] [,2]
# [1,] 9 13
# [2,] 13 15
f(3)
# [,1] [,2] [,3]
# [1,] 1 8 3
# [2,] 8 13 5
# [3,] 3 5 14
Related
This question already has answers here:
Most Efficient way to create a symmetric matrix
(4 answers)
How can I generate random real symmetric matrix in R [duplicate]
(1 answer)
Closed 2 years ago.
I currently have a matrix output from a program that looks like the following where the bottom left has all 1s:
B C D E
A 0 1 2 3
B 1 1 3 3
C 1 1 1 3
D 1 1 1 0
Is there a way to convert it into a symmetrical matrix instead of having all the 1s?
I do not think that the solution of #RonakShah is correct.
M = matrix(1:16, nrow=4)
M
[,1] [,2] [,3] [,4]
[1,] 1 5 9 13
[2,] 2 6 10 14
[3,] 3 7 11 15
[4,] 4 8 12 16
M[lower.tri(M)] <- M[upper.tri(M)]
M
[,1] [,2] [,3] [,4]
[1,] 1 5 9 13
[2,] 5 6 10 14
[3,] 9 13 11 15
[4,] 10 14 15 16
This is not symmetric. Instead, use
M = matrix(1:16, nrow=4)
M[lower.tri(M)] <- t(M)[lower.tri(M)]
M
[,1] [,2] [,3] [,4]
[1,] 1 5 9 13
[2,] 5 6 10 14
[3,] 9 10 11 15
[4,] 13 14 15 16
You can copy the upper triangular values to lower triangle.
mat[lower.tri(mat)] <- mat[upper.tri(mat)]
mat
# B C D E
#A 0 1 2 3
#B 1 1 3 3
#C 2 3 1 3
#D 3 3 3 0
I need to create a function, that will rearrange any square matrix based on the values in the matrix.
So if I have matrix like this:
M <- matrix(1:16, ncol = 4)
M
#> [,1] [,2] [,3] [,4]
#> [1,] 1 5 9 13
#> [2,] 2 6 10 14
#> [3,] 3 7 11 15
#> [4,] 4 8 12 16
After rearrangement it needs to look like this:
[,1] [,2] [,3] [,4]
[1,] 1 3 6 10
[2,] 2 5 9 13
[3,] 4 8 12 15
[4,] 7 11 14 16
So it is sorted from lowest (left upper corner) to highest (right lower corner), but the numbers are sorted on diagonal (is that the right word?) not in rows or columns.
I know how to to this "manually", but I can't figure out any rules that this rearrangement operates by.
1) row(m) + col(m) is constant along reverse diagonals so:
M <- replace(m, order(row(m) + col(m)), m)
gving:
> M
[,1] [,2] [,3] [,4]
[1,] 1 3 6 10
[2,] 2 5 9 13
[3,] 4 8 12 15
[4,] 7 11 14 16
It is not clear whether sorted on the diagonal means just that they are unravelled from the storage order onto the reverse diagonals or that they are actually sorted after that within each reverse diagonal. In the example in the question the two interpretations give the same answer; however, if you did wish to sort the result within reverse diagonal afterwards using different data then apply this:
ave(M, row(M) + col(M), FUN = sort)
2) A longer version:
M2 <- matrix(m[order(unlist(tapply(seq_along(m), row(m) + col(m), c)))], nrow(m))
Here's a function columns_to_diagonals in base R that ought to do what you're after. It uses split and unsplit with the appropriate factors.
columns_to_diagonals <- function(M) {
n <- ncol(M)
f <- matrix(rep(1:(2*n-1), c(1:n, (n-1):1)), ncol = n)
m <- split(M, f)
d <- row(M) + col(M)
matrix(unsplit(m, d), ncol = n)
}
First, we may test this on your original case:
M <- matrix(1:16, ncol = 4)
columns_to_diagonals(M)
#> [,1] [,2] [,3] [,4]
#> [1,] 1 3 6 10
#> [2,] 2 5 9 13
#> [3,] 4 8 12 15
#> [4,] 7 11 14 16
And then a larger, randomly permutated matrix, to check that this looks fine as well:
M <- matrix(sample(1:25), ncol = 5)
M
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 4 15 12 10 21
#> [2,] 19 7 5 23 6
#> [3,] 9 17 2 8 1
#> [4,] 3 11 16 25 14
#> [5,] 22 18 20 13 24
columns_to_diagonals(M)
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 4 9 15 18 20
#> [2,] 19 22 11 16 25
#> [3,] 3 17 2 8 6
#> [4,] 7 5 23 21 14
#> [5,] 12 10 13 1 24
Created on 2019-12-15 by the reprex package (v0.2.1)
I am trying to randomize a matrix such that each of the rows in each column are randomized individually so that in the final matrix there is no association between columns. I know that I need to use the sample() function and some sort of for(each column) loop, but I'm not exactly sure of how to go about doing it. Specifically, I am asking how to write a function that will loop through the columns of a matrix and randomize the rows of each column.
Edit: An example of what I'm trying to achieve
Original matrix:
X1 X2 X3
1 4 3 6
2 7 2 4
3 9 5 1
Sample desired output:
X1 X2 X3
1 7 3 1
2 4 5 6
3 9 2 4
As you can see, the rows in each column have been randomized separately.
If you have a matrix X, you can use apply() (ideal for matrix)
apply(X, 2, sample)
Example:
X <- matrix(1:25, 5)
# [,1] [,2] [,3] [,4] [,5]
# [1,] 1 6 11 16 21
# [2,] 2 7 12 17 22
# [3,] 3 8 13 18 23
# [4,] 4 9 14 19 24
# [5,] 5 10 15 20 25
Apply the code above gives:
# [,1] [,2] [,3] [,4] [,5]
# [1,] 3 10 11 16 21
# [2,] 5 8 12 20 22
# [3,] 4 9 14 18 24
# [4,] 2 6 15 19 25
# [5,] 1 7 13 17 23
I did not set random seed via set.seed(), so you will get different result when you run it. But all you need to know is that: the result is random.
If you have a data frame X, you'd better use sapply()
sapply(X, sample)
You could use a for loop for each column.
Or you could use:
apply(x, 2, function(col) sample(col, replace=F))
I have been thinking about a problem I have but I don't know how to express the problem to even search for it. I'd be very thankful if you could explain it to me.
So, I have a data set with the following format:
10 6 4 4
10 6 4 4
7 6 4 4
I want to conduct a pairwise calculation for which I need to sum each element to the other one by one. That is 1 with 2, 1 with 3, 1 with 4, 2 with 3, 2 with 4 and 3 with 4.
I thought to do a nested a loop in R which I read about it and I started like this:
for (i in 1:r-1) { ## r the number of columns
for (j in (i+1):r) {
....
}
I am stuck at this stage, I don't know how to express in codes what I need to do. I am sorry for posting a not progressed code, some advice would be very good that how I should go about it.
Thanks a lot in advance.
Use combn to create the "pairs":
(pairs <- combn(4,2))
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 1 2 2 3
[2,] 2 3 4 3 4 4
Then apply across the rows of your data by summing these subsets by applying across the columns of the pairs:
dat <- matrix(c(10,10,7,6,6,6,4,4,4,4,4,4),ncol=4)
t(apply(dat, 1, function(x) apply(combn(4,2),2,function(y) sum(x[y]))))
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 16 14 14 10 10 8
[2,] 16 14 14 10 10 8
[3,] 13 11 11 10 10 8
You could slightly modify your loop:
d <- read.table(text='
10 6 4 4
10 6 4 4
7 6 4 4')
nc <- ncol(d)
r <- NULL
for (i in 1:nc) {
for (j in 1:nc) {
if (i < j) { # crucial condition
r <- cbind(r, d[, i] + d[, j]) # calculate new column and bind to calculated ones
}
}
}
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 16 14 14 10 10 8
[2,] 16 14 14 10 10 8
[3,] 13 11 11 10 10 8
Another application of combn but perhaps easier to understand:
apply(combn(ncol(dat),2), 2, function(x) rowSums(dat[,x]))
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 16 14 14 10 10 8
## [2,] 16 14 14 10 10 8
## [3,] 13 11 11 10 10 8
Here, the matrix dat is indexed by each column of the result of combn giving a matrix of two columns (the two columns to be summed). rowSums then does the arithmetic.
Because I really like package functional, here is a slight variation on the above:
apply(combn(ncol(dat),2), 2, Compose(Curry(`[`, dat, i=seq(nrow(dat))), rowSums))
It should be noted that a combn approach is more flexible than using nested for loops for this sort of computation. In particular, it is easily adapted to any number of columns to sum:
f <- function(dat, num=2)
{
apply(combn(ncol(dat),num), 2, function(x) rowSums(dat[,x,drop=FALSE]))
}
This will give all combinations of num columns, and sum them:
f(dat, 1)
## [,1] [,2] [,3] [,4]
## [1,] 10 6 4 4
## [2,] 10 6 4 4
## [3,] 7 6 4 4
f(dat, 2)
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 16 14 14 10 10 8
## [2,] 16 14 14 10 10 8
## [3,] 13 11 11 10 10 8
f(dat, 3)
## [,1] [,2] [,3] [,4]
## [1,] 20 20 18 14
## [2,] 20 20 18 14
## [3,] 17 17 15 14
f(dat, 4)
## [,1]
## [1,] 24
## [2,] 24
## [3,] 21
Hi everyone who loves while hates R:
Let's say you want to turn matrix M
[,1] [,2] [,3]
[1,] 1 2 3
[2,] 4 5 6
[3,] 7 8 9
to N
[,1] [,2] [,3]
[1,] 3 2 1
[2,] 6 5 4
[3,] 9 8 7
All you need to do is
N<-M[,c(3:1)]
And N's structure is still a matrix
However, when you want to turn matrix M
[,1] [,2] [,3]
[1,] 1 2 3
to N
[,1] [,2] [,3]
[1,] 3 2 1
if you do
N<-M[,c(3:1)]
R will give you
N
[1] 3 2 1
N now is a vector! Not a matrix!
My solution is
N<-M%*%diag(3)[,c(3:1)]
which needs big space to store the identity matrix however.
Any better idea?
You're looking for this:
N<-M[,c(3:1),drop = FALSE]
Read ?Extract for more information. This is also a FAQ. This behavior is one of the most common debates folks have about the way things "should" be in R. My general impression is that many people agree that drop = FALSE might be a more sensible default, but that behavior is so old that changing it would be enormously disruptive to vast swaths of existing code.
A=t(matrix(1:25,5,5))
B=matrix(0,5,5)
for(i in 1:5){
B[i,(nrow(A)+1-i)]=1
}
A
# [,1] [,2] [,3] [,4] [,5]
# [1,] 1 2 3 4 5
# [2,] 6 7 8 9 10
# [3,] 11 12 13 14 15
# [4,] 16 17 18 19 20
# [5,] 21 22 23 24 25
A%*%B
# [,1] [,2] [,3] [,4] [,5]
# [1,] 5 4 3 2 1
# [2,] 10 9 8 7 6
# [3,] 15 14 13 12 11
# [4,] 20 19 18 17 16
# [5,] 25 24 23 22 21