I have a matrix that should be symmetric according to theory, but might not be observed as symmetric in my data. I would like to force this to be symmetric by using the maximum of the two compared cells.
test_matrix <- matrix(c(0,1,2,1,0,1,1.5,1,0), nrow = 3)
test_matrix
#> [,1] [,2] [,3]
#>[1,] 0 1 1.5
#>[2,] 1 0 1.0
#>[3,] 2 1 0.0
It's easy enough to do this with a double loop.
for(i in 1:3){
for(j in 1:3){
test_matrix[i, j] <- max(test_matrix[i, j], test_matrix[j, i])
}
}
test_matrix
#> [,1] [,2] [,3]
#> [1,] 0 1 2
#> [2,] 1 0 1
#> [3,] 2 1 0
But my matrix is larger than $3x3$, and R's problems with loops are well-documented. I'm also interested in making my code as clean as possible. In fact, I considered putting this on code golf, but this is a real problem that I think others might be interested in.
I've seen this one as well as this one, but mine is different in that those op's seemed to actually have a symmetric matrix that just needed reordering, and I have a matrix that I need to change to be symmetric.
You could use pmax(), which returns the element-wise maxima of a pair of vectors.
pmax(test_matrix, t(test_matrix))
# [,1] [,2] [,3]
# [1,] 0 1 2
# [2,] 1 0 1
# [3,] 2 1 0
It'll work with a pair of matrices, as here, because: (1) in R, matrices are 'just' vectors with attached (dimension) attributes; and (2) the code used to implement pmax() is nice enough to reattach the attributes of it's first argument to the value that it returns.
Related
Suppose I have a list of matrices. Suppose further I have found the smallest values by the column.
Here is my last question
I really need to know from which matrix each smallest value is selected. My original function is very complicated. Therefore, I provided a simple example. I have one idea and really do not know to implement it correctly in R.
My idea is:
Suppose that [i,j] is the elements of the matrix. Then,
if(d[[1]][i,j] < d[[2]][i,j]){
d[[1]][i,j] <– "x"
}else { d[[2]][i,j] <– "z"}
So, I would like to sign the name of the matrix that corresponds to each smallest value. Then, store the names in a separate matrix. So, then I can see the values in one matrix and their corresponding names (from where they come from) in another matrix
For example,
y <- c(3,2,4,5,6, 4,5,5,6,7)
x[lower.tri(x,diag=F)] <- y
> x
[,1] [,2] [,3] [,4] [,5]
[1,] 0 0 0 0 0
[2,] 3 0 0 0 0
[3,] 2 6 0 0 0
[4,] 4 4 5 0 0
[5,] 5 5 6 7 0
k <- c(1,4,5,2,5,-4,4,4,4,5)
z[lower.tri(z,diag=F)] <- k
> z
[,1] [,2] [,3] [,4] [,5]
[1,] 0 0 0 0 0
[2,] 1 0 0 0 0
[3,] 4 5 0 0 0
[4,] 5 -4 4 0 0
[5,] 2 4 4 5 0
d <- list(z, x)
Then:
do.call(pmin, d) (answered by #akrun)
Then, I will only get the matrix with smallest values. I would like to know where each value is come from?
Any idea or help, please?
You can use Map and do.call to create your own functions that will be applied element-wise to a list of inputs,
in your case a list of matrices.
pwhich.min <- function(...) {
which.min(c(...)) # which.min takes a single vector as input
}
di <- unlist(do.call(Map, c(list(f = pwhich.min), d)))
dim(di) <- dim(x) # take dimension from one of the inputs
di
[,1] [,2] [,3] [,4] [,5]
[1,] 1 1 1 1 1
[2,] 2 1 1 1 1
[3,] 1 2 1 1 1
[4,] 1 2 2 1 1
[5,] 2 2 2 2 1
EDIT:
To elaborate,
you could do something like Map(f = min, z, x) to apply min to each pair of values in z and x,
although in that case min already supports arbitrary amount of inputs through an ellipsis (...).
By contrast,
which.min only takes a single vector as input,
so you need a wrapper with an ellipsis that combines all values into a vector
(pwhich.min above).
Since you may want to have more than two matrices,
you can put them all in a list,
and use do.call to put each element in the list as a parameter to the function you specify in f.
Or another option would be to convert it to a 3D array and use apply with which.min
apply(array(unlist(d), c(5, 5, 2)), c(1, 2), which.min)
Or with pmap from purrr
library(purrr)
pmap_int(d, ~ which.min(c(...))) %>%
array(., dim(x))
I have a square matrix which I know can become a triangular one by permutation, what would be the Matrix::function call to get the triangular one ?
I have thus B as
B <- matrix(c(0,5,6,1,2,3,0,0,9),3,3)
and want to get a function that renders as result
B[,c(2,1,3)]
You could try with
B[, order(sapply(1:ncol(B), function(x) rle(B[,x]==0)$lengths[1]*rle(B[,x]==0)$values[1]))]
# [,1] [,2] [,3]
#[1,] 1 0 0
#[2,] 2 5 0
#[3,] 3 6 9
This orders the columns of the matrix according to the number of consecutive zeros (counted from the top row downwards), with the shortest sequence of zeros first.
How can I create a quadratic band matrix, where I give the diagonal and the first diagonal below and above the diagonal? I am looking for a function like
tridiag(upper, lower, main)
where length(upper)==length(lower)==length(main)-1 and returns, for example,
tridiag(1:3, 2:4, 3:6)
[,1] [,2] [,3] [,4]
[1,] 3 1 0 0
[2,] 2 4 2 0
[3,] 0 3 5 3
[4,] 0 0 4 6
Is there an efficient way to do it?
This function will do what you want:
tridiag <- function(upper, lower, main){
out <- matrix(0,length(main),length(main))
diag(out) <- main
indx <- seq.int(length(upper))
out[cbind(indx+1,indx)] <- lower
out[cbind(indx,indx+1)] <- upper
return(out)
}
Note that when the index to a matrix is a 2 column matrix, each row in that index is interpreted as the row and column index for a single value in the vector being assigned.
More specifically, I want all elements other than the diagonal ones (X_11, X_22, X_33,...,X_jj) to be zero.
E.g. I want:
[1 4 5
2 3 5
3 9 8]
to be:
[1 0 0
0 3 0
0 0 8]
Is this possible? Sorry I'm a complete noob at this..
It's a simple one liner. First, get the data in:
> (a <- matrix(scan(),nr=3,byrow=TRUE))
1: 1 4 5 2 3 5 3 9 8
10:
Read 9 items
[,1] [,2] [,3]
[1,] 1 4 5
[2,] 2 3 5
[3,] 3 9 8
Method 1:
> diag(diag(a))
[,1] [,2] [,3]
[1,] 1 0 0
[2,] 0 3 0
[3,] 0 0 8
The thing is, if its argument is a matrix, diag extracts the diagonal... but if the argument is a vector, it's a function that creates a diagonal matrix. So just use it twice. (In fact diag has four different uses, depending on what you give it, though two of the cases are quite similar.) See ?diag
If your matrices are huge this isn't likely to be the most efficient way, but for moderate size cases that's the way I do it.
---
Method 2:
A completely different one-liner that also works -
ifelse(row(a)==col(a),a,0)
The two work the same on square matrices. But they have a different result on non-square matrices - the first one returns a square matrix (of dimension the smaller of the two original dimensions), while the second one returns an object of the same shape as its argument; this can be useful depending on the situation.
The simplest way to do this, is to create a new matrix filled with 0s, then replace its diagonal with the diagonal of the old matrix.
So if you have:
m <- cbind(c(1,2,3), c(4,3,9), c(5, 5, 8)) # The original matrix
diagonal <- diag(m)
m <- matrix(0, nrow(m), ncol(m)) # Overwrite the old matrix
diag(m) <- diagonal
For a matrix of n x m
for i to n
for j to m
if i != j
matrix[i][j] = 0;
If m is your matrix try:
m = matrix(c(1,4,5,2,3,5,3,9,8),3,3)
m[upper.tri(m) | lower.tri(m)] = 0
m
## [,1] [,2] [,3]
## [1,] 1 0 0
## [2,] 0 3 0
## [3,] 0 0 8
It simply dependes on the size of the matrix you're dealing with, let's say you have a nxn matrix then the diagonals are gonna be at these places 0, n+1 , 2(n+1), 3(n+1),... if your matrix as you mentioned is not multi dimensional and is linear! so by simply writing a for loop it is possible.
I'm transferring my answer from your second post on this topic.
You can use the following to compute a logical matrix which describes the non-diagonal entries of a n×n matrix:
outer(1:n, 1:n, function(i,j) i!=j)
Applied to your example:
> m <- matrix(c(1,2,3,4,3,9,5,5,8),ncol=3)
> m
[,1] [,2] [,3]
[1,] 1 4 5
[2,] 2 3 5
[3,] 3 9 8
> m[outer(1:3, 1:3, function(i,j) i!=j)] <- 0
> m
[,1] [,2] [,3]
[1,] 1 0 0
[2,] 0 3 0
[3,] 0 0 8
I also like the triangle approach by #e4e5f4. That might be a bit faster than this code here, but this code here might be easier to adapt to different situations. So it's good to know this one, even if that one might be preferrable for your current application.
I have a matrix of indices I where some of the indices are repeated. I put an example below.
I have another matrix A with dimensions compatible with the indices and initiated to 0 everywhere. I would like to do something like
A[I] += 1
I face two issues:
A[I] = A[I] + 1 is too inefficient
matrix I has redundant indices. For example rows 2 & 6 are identical and I would like to obtain A[1,2] = 2
A partial answer would be to create a 3 columns matrix with the two first columns being the product of unique(I) and the third column with the counts, but I don't see any solution for that either. Any pointer or help would be greatly appreciated!
> I is:
[,1] [,2]
[1,] 1 1
[2,] 1 2
[3,] 1 3
[4,] 1 4
[5,] 1 1
[6,] 1 2
[7,] 1 3
This may be quickest using sparse matrix methods (see the Matrix package and others).
Using standard matricies you could collapse the identical rows using the xtabs function then matrix assignment (edited based on comment):
I <- cbind(1, c(1:4,1:3))
tmp <- as.data.frame(xtabs( ~I[,1]+I[,2] ))
A <- matrix(0, nrow=5, ncol=5)
tmp2 <- as.matrix(tmp[,1:2])
tmp3 <- as.numeric(tmp2)
dim(tmp3) <- dim(tmp2)
A[ tmp3 ] <- tmp[,3]
A
You could probably make it a little quicker by pulling the core functionality out of as.data.frame.table rather than converting to data frame and back again.
Here is another version that may be more efficient. It will overwrite some 0's with other 0's computed by xtabs:
I <- cbind(1:5,1:5)
A <- matrix(0, 5, 5)
tmp <- xtabs( ~I[,2]+I[,1] )
A[ as.numeric(rownames(tmp)), as.numeric(colnames(tmp)) ] <- c(tmp)
A
If the A matrix has dimnames and the I matrix has the names instead of the indexes, then this later one will also work (just remove the as.numerics.
Here you go:
## Reproducible versions of your A and I objects
A <- matrix(0, nrow=2, ncol=5)
## For computations that follow, you'll be better off having this as a data.frame
## (Just use `I <- as.data.frame(I)` to convert a matrix object I).
I <- read.table(text=" 1 1
1 2
1 3
1 4
1 1
1 2
1 3", header=FALSE)
## Create data.frame with number of times each matrix element should
## be incremented
I$count <- ave(I[,1], I[,1], I[,2], FUN=length)
I <- unique(I)
## Replace desired elements, using a two column matrix (the "third form of
## indexing" mentioned in "Matrices and arrays" section" of ?"[").
A[as.matrix(I[1:2])] <- I[[3]]
A
# [,1] [,2] [,3] [,4] [,5]
# [1,] 2 2 2 1 0
# [2,] 0 0 0 0 0