Efficient creation of tridiagonal matrices - r

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.

Related

R: how to calculate element-wise arg-min from a list of matrices?

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

Converting a vector in R into a lower triangular matrix in specific order

I have a vector where the order of the elements are important, say
x <- c(1,2,3,4)
I would like to arrange my vector into a lower triangular matrix with a specific order where each row contains the preceding element of the vector. My goal is to obtain the following matrix
lower_diag_matrix
[,1] [,2] [,3] [,4]
[1,] 4 0 0 0
[2,] 3 4 0 0
[3,] 2 3 4 0
[4,] 1 2 3 4
I know I can fill the lower triangular area using lower_diag_matrix[lower.tri(lower_diag_matrix,diag = T)]<-some_vector but I can't seem to figure out the arrangement of the vector used to fill the lower triangular area. In practice the numbers will be random, so I would need a generic way to fill the area.
Here's one way:
x <- c(2, 4, 7)
M <- matrix(0, length(x), length(x))
M[lower.tri(M, diag = TRUE)] <- rev(x)[sequence(length(x):1)]
M
# [,1] [,2] [,3]
# [1,] 7 0 0
# [2,] 4 7 0
# [3,] 2 4 7

Get elements over opposite diagonal in a matrix in R

I am trying to solve a little problem with a matrix in R. I have the next matrix in R (alfa):
alfa <- matrix(1:9,nrow=3)
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
The opposite diagonal of alfa is filled of zeros. I would like to get in a new matrix all elements over this opposite diagonal (maybe the upper triangle over this diagonal). I wish to get a new matrix like this:
[,1] [,2] [,3]
[1,] 1 4 0
[2,] 2 0 0
[3,] 0 0 0
Or like this matrix with NA:
[,1] [,2] [,3]
[1,] 1 4 0
[2,] 2 0 NA
[3,] 0 NA NA
Where the elements located down the opposite diagonal of alfa are zero or NA, as you can see. I have tried with code using row(alfa) and col(alfa) but I can't get the expected matrix, for example:
(row(alfa)+col(alfa)-1)%%ncol(alfa)!=0
And I got this result where both upper and down elements over opposite diagonal are TRUE:
[,1] [,2] [,3]
[1,] TRUE TRUE FALSE
[2,] TRUE FALSE TRUE
[3,] FALSE TRUE TRUE
But I only want the upper elements, and the rest elements should be filled with zero or NA.
Many thanks for your help.
lower.tri almost does what you want, but you need to reverse the rows.
alfa[apply(lower.tri(alfa), 1, rev)] <- NA
Here, the matrix of the lower anti-diagonal is built, and used to select into alfa (vector indexing) for replacement.
lower.tri has a diag argument, which will also select the diagonal if set to TRUE.
f <- function(mat, diag = 0, offdiag = NA){
rev_vec <- seq(ncol(mat), 1)
j <- mat[,rev_vec]
j[lower.tri(j)] <- offdiag
diag(j) <- diag
j[,rev_vec]
}
You can specify if you want the off-diagonals to be NA or 0 by changing the offdiag parameter.

How to replace non-diagonal elements in a matrix?

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.

Replace the first zero element of a row of a matrix,

As fast as possible, I would like to replace the first zeros in some rows of a matrix with values stored in another vector.
There is a numeric matrix where each row is a vector with some zeros.
I also have two vectors, one containing the rows, in what to be replaced, and another the new values: replace.in.these.rows and new.values. Also, I can generate the vector of first zeroes with sapply
mat <- matrix(1,5,5)
mat[c(1,8,10,14,16,22,14)] <- 0
replace.in.these.rows <- c(1,2,3)
new.values <- c(91,92,93)
corresponding.poz.of.1st.zero <- sapply(replace.in.these.rows,
function(x) which(mat [x,] == 0)[1] )
Now I would like something that iterates over the index vectors, but without a for loop possibly:
matrix[replace.in.these.rows, corresponding.poz.of.the.1st.zero ] <- new.values
Is there a trick with indexing more than simple vectors? It could not use list or array(e.g.-by-column) as index.
By default R matrices are a set of column vectors. Do I gain anything if I store the data in a transposed form? It would mean to work on columns instead of rows.
Context:
This matrix stores contact ID-s of a network. This is not an adjacency matrix n x n, rather n x max.number.of.partners (or n*=30) matrix.
The network uses edgelist by default, but I wanted to store the "all links from X" together.
I assumed, but not sure if this is more efficient than always extract the information from the edgelist (multiple times each round in a simulation)
I also assumed that this linearly growing matrix form is faster than storing the same information in a same formatted list.
Some comments on these contextual assumptions are also welcome.
Edit: If only the first zeros are to be replace then this approach works:
first0s <-apply(mat[replace.in.these.rows, ] , 1, function(x) which(x==0)[1])
mat[cbind(replace.in.these.rows, first0s)] <- new.values
> mat
[,1] [,2] [,3] [,4] [,5]
[1,] 91 1 1 0 1
[2,] 1 1 1 1 92
[3,] 1 93 1 1 1
[4,] 1 1 0 1 1
[5,] 1 0 1 1 1
Edit: I thought that the goal was to replace all zeros in the chosen rows and this was the approach. A completely vectorized approach:
idxs <- which(mat==0, arr.ind=TRUE)
# This returns that rows and columns that identify the zero elements
# idxs[,"row"] %in% replace.in.these.rows
# [1] TRUE TRUE FALSE FALSE TRUE TRUE
# That isolates the ones you want.
# idxs[ idxs[,"row"] %in% replace.in.these.rows , ]
# that shows what you will supply as the two column argument to "["
# row col
#[1,] 1 1
#[2,] 3 2
#[3,] 1 4
#[4,] 2 5
chosen.ones <- idxs[ idxs[,"row"] %in% replace.in.these.rows , ]
mat[chosen.ones] <- new.values[chosen.ones[,"row"]]
# Replace the zeros with the values chosen (and duplicated if necessary) by "row".
mat
#---------
[,1] [,2] [,3] [,4] [,5]
[1,] 91 1 1 91 1
[2,] 1 1 1 1 92
[3,] 1 93 1 1 1
[4,] 1 1 0 1 1
[5,] 1 0 1 1 1

Resources