Simplest way to repeat matrix along 3rd dimension - r

How to repeat a matrix A along 3rd dimension n times? array(rep(A,n),c(dim(A),n)) should work but is there a simpler way? I was hoping A[,,rep(1,n)] would do the trick but apparently not.

You forgot to change the dimensions first:
A = matrix(1:4, nrow = 2)
dim(A) = c(dim(A), 1)
A[, , rep(1, 3)]
#, , 1
#
# [,1] [,2]
#[1,] 1 3
#[2,] 2 4
#
#, , 2
#
# [,1] [,2]
#[1,] 1 3
#[2,] 2 4
#
#, , 3
#
# [,1] [,2]
#[1,] 1 3
#[2,] 2 4

I guess the following is a little shorter:
myArray <- replicate(n, A)
Here is an example:
temp <- matrix(1:10, 5)
replicate(3, temp)

Related

trouble rearranging my 2x2 matrix in a simple way in R

I'm trying to turn
df<-matrix(1:4,nrow = 2,ncol = 2)
df
[,1] [,2]
[1,] 1 3
[2,] 2 4
into
matrix(c(2,4,1,3),nrow = 1,ncol = 4)
2 4 1 3
so that i can run it through a for loop to rbind many entries.
I've been trying
cbind(df[row 2,],df[row 1,])
but it's not working. Is there a simple way to do this that won't require me to separate the matrix and then bring it back together?
Here is another way. Without the call to matrix it returns a vector, not a matrix.
df <- matrix(1:4, 2)
matrix(c(t(df[nrow(df):1,])), 1)
# [,1] [,2] [,3] [,4]
#[1,] 2 4 1 3
We can use
t(c(t(df[nrow(df):1, ])))
# [,1] [,2] [,3] [,4]
#[1,] 2 4 1 3
Turning a comment into an answer, a fourth option is
rev(t(m[, ncol(m):1]))
# [1] 2 4 1 3
with
m <- matrix(1:4, 2)
Maybe you can try the code below
r <- unlist(rev(data.frame(t(df))))
or
r <- do.call(c,rev(split(df,1:nrow(df))))
or
r <- unlist(rev(split(df,1:nrow(df))))

Apply function on each element of a list of matrices

I have a list of matrices.
(below is a simplified example, I actually have a list of 3 matrices, the first one being in 2D, while the second and third ones are in 3D)
> a <- matrix(-1:2, ncol = 2)
> b <- array(c(-2:5), dim=c(2, 2, 2))
> c_list <- list(a,b)
> c_list
[[1]]
[,1] [,2]
[1,] -1 1
[2,] 0 2
[[2]]
, , 1
[,1] [,2]
[1,] -2 0
[2,] -1 1
, , 2
[,1] [,2]
[1,] 2 4
[2,] 3 5
I'd like to apply the function max(0,c_list) to each and every element (without a loop), in order to have the same type of object as "c_list" but with the negative values replaced by zeros.
> output
[[1]]
[,1] [,2]
[1,] 0 1
[2,] 0 2
[[2]]
, , 1
[,1] [,2]
[1,] 0 0
[2,] 0 1
, , 2
[,1] [,2]
[1,] 2 4
[2,] 3 5
I've managed to do it for a matrice or for a list with mapply or lapply, but not for a list of matrices.
Answer : either Sotos' answer
output <- lapply(c_list, function(i)replace(i, i < 0, 0))
or Moody_Mudskipper's answer
output <- lapply(c_list,pmax,0)
You can use pmax, it will preserve the format of the source matrix and vectorized so faster than looping with max.
lapply(c_list,pmax,0)
Using apply and lapply:
a <- matrix(-1:2, ncol = 2)
b <- matrix(-3:0, ncol = 2)
c <- list(a,b)
d <- lapply(c, function(m) {
apply(m, c(1, 2), function(x) max(0, x))
})
Output:
> d
[[1]]
[,1] [,2]
[1,] 0 1
[2,] 0 2
[[2]]
[,1] [,2]
[1,] 0 0
[2,] 0 0

Output converted from matrix to vector in apply

I want to apply a function over one margin (column in my example) of a matrix. The problem is that the function returns matrix and apply converts it to vector so that it returns a matrix. My goal is to get three-dimensional array. Here is the example (note that matrix() is not the function of interest, just an example):
x <- matrix(1:12, 4, 3)
apply(x, 2, matrix, nrow = 2, ncol = 2)
The output is exactly the same as the input. I have pretty dull solution to this:
library(abind)
abind2 <- function (x, ...)
abind(x, ..., along = dim(x) + 1)
apply(x, 2, list) %>%
lapply(unlist) %>%
lapply(matrix, nrow = 2, ncol = 2) %>%
do.call(what = 'abind2')
I believe there must exist something better than this. Something that does not include list()ing and unlist()ing columns.
Edit:
Also, the solution should be ready to be easily applicable to any-dimensional array with any choice of MARGIN which my solution is not.
This, for example, I want to return 4-dimensional array.
x <- array(1:24, c(4,3,2))
apply(x, 2:3, list) %>%
lapply(unlist) %>%
lapply(matrix, nrow = 2, ncol = 2) %>%
do.call(what = 'abind2')
Not that complicated at all. Simply use
array(x, dim = c(2, 2, ncol(x)))
Matrix and general arrays are stored by column into a 1D long array in physical address. You can just reallocate dimension.
OK, here is possibly what you want to do in general:
tapply(x, col(x), FUN = matrix, nrow = 2, ncol = 2)
#$`1`
# [,1] [,2]
#[1,] 1 3
#[2,] 2 4
#
#$`2`
# [,1] [,2]
#[1,] 5 7
#[2,] 6 8
#
#$`3`
# [,1] [,2]
#[1,] 9 11
#[2,] 10 12
You can try to convert your matrix into a data.frame and use lapply to apply your function on the columns (as a data.frame is a list), it will return a list, where each element represents the function result for a column:
lapply(as.data.frame(x), matrix, nrow = 2, ncol = 2)
# $V1
# [,1] [,2]
# [1,] 1 3
# [2,] 2 4
# $V2
# [,1] [,2]
# [1,] 5 7
# [2,] 6 8
# $V3
# [,1] [,2]
# [1,] 9 11
# [2,] 10 12
EDIT with the second definition of x:
x <- array(1:24, c(4,3,2))
lapply(as.data.frame(x), matrix, nrow = 2, ncol = 2)
# $V1
# [,1] [,2]
# [1,] 1 3
# [2,] 2 4
# $V2
# [,1] [,2]
# [1,] 5 7
# [2,] 6 8
# $V3
# [,1] [,2]
# [1,] 9 11
# [2,] 10 12
# $V4
# [,1] [,2]
# [1,] 13 15
# [2,] 14 16
# $V5
# [,1] [,2]
# [1,] 17 19
# [2,] 18 20
# $V6
# [,1] [,2]
# [1,] 21 23
# [2,] 22 24
EDIT2: a try to get an arry as result
Based on this similar question, you may try this code:
x <- array(1:24, c(4,3,2))
sapply(1:3,
function(y) sapply(1:ncol(x[, y, ]),
function(z) matrix(x[,y,z], ncol=2, nrow=2),
simplify="array"),
simplify="array")
Dimension of the result is 2 2 2 3.
Actually, the problem here is that it needs two different calls to apply when x is an array of more than 2 dimension. In the last example of the quesion (with x <- array(1:24, c(4,3,2))), we want to apply to each element of third dimension a function that apply to each element of second dimension the matrix function.

Combining matrices by alternating columns

I'm looking for a general approach to combine two matrices so that the columns from the two initial matrices alternate in the new matrix
col1m1...col1m2...col2m1...col2m2...col3m1...col3m2......
for example:
matrix.odd <- matrix(c(rep(1,3),rep(3,3),rep(5,3)),nrow=3,ncol=3)
matrix.even <- matrix(c(rep(2,3),rep(4,3),rep(6,3)),nrow=3,ncol=3)
# would look like
matrix.combined <- matrix(c(rep(1,3),rep(2,3),rep(3,3),rep(4,3),rep(5,3),rep(6,3)),
nrow=3,ncol=6)
I'm looking for a general approach because I will have matrix combinations with more than just 3 columns. I've tried some for loops and some if statements but it isn't really coming together for me. Searches on combining matrices with shuffle and with alternation have not proven fruitful either. Any thoughts?
Smth like this should do:
m <- cbind(matrix.odd, matrix.even) # combine
m <- m[, c(matrix(1:ncol(m), nrow = 2, byrow = T))] # then reorder
Another option for fun:
matrix(rbind(matrix.odd, matrix.even), nrow = nrow(matrix.odd))
And to play the many matrices game:
weave = function(...) {
l = list(...)
matrix(do.call(rbind, l), nrow = nrow(l[[1]]))
}
rows.combined <- nrow(matrix.odd)
cols.combined <- ncol(matrix.odd) + ncol(matrix.even)
matrix.combined <- matrix(NA, nrow=rows.combined, ncol=cols.combined)
matrix.combined[, seq(1, cols.combined, 2)] <- matrix.odd
matrix.combined[, seq(2, cols.combined, 2)] <- matrix.even
alternate.cols <- function(m1, m2) {
cbind(m1, m2)[, order(c(seq(ncol(m1)), seq(ncol(m2))))]
}
identical(matrix.combined, alternate.cols(matrix.odd, matrix.even))
# [1] TRUE
which also does the right thing (subjective) if m1 and m2 have a different number of columns:
alternate.cols(matrix.odd, matrix.even[, -3])
# [,1] [,2] [,3] [,4] [,5]
# [1,] 1 2 3 4 5
# [2,] 1 2 3 4 5
# [3,] 1 2 3 4 5
It is easy to generalize to any number of matrices:
alternate.cols <- function(...) {
l <- list(...)
m <- do.call(cbind, l)
i <- order(sequence(sapply(l, ncol)))
m[, i]
}
You could turn into a 3D array and then transpose...
arr <- array( c(m1,m2) , dim = c(dim(m1),2) )
matrix( aperm( arr , c(1,3,2) ) , nrow(m1) )
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 2 3 4 5 6
[2,] 1 2 3 4 5 6
[3,] 1 2 3 4 5 6
And as a function, generalisable to many matrices...
bindR <- function(...){
args <- list(...)
dims <- c( dim(args[[1]]) , length(args) )
arr <- array( unlist( args ) , dim = dims )
matrix( aperm( arr , c(1,3,2) ) , dims[1] )
}
bindR(m1,m2,m1,m2)
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
#[1,] 1 2 1 2 3 4 3 4 5 6 5 6
#[2,] 1 2 1 2 3 4 3 4 5 6 5 6
#[3,] 1 2 1 2 3 4 3 4 5 6 5 6
There is likely a more succinct way to do this. If the matrices are large, you will likely need to look for a more efficient method.
# Test data
(X <- matrix(1:16, nrow=4, ncol=4))
(Y <- matrix(-16:-1, nrow=4, ncol=4))
# Set indices for the new matrix
X.idx <- seq(1, ncol(X)*2, by=2)
Y.idx <- seq(2, ncol(Y)*2+1, by=2)
# Column bind the matrices and name columns according to the indices
XY <- cbind(X, Y)
colnames(XY) <- c(X.idx, Y.idx)
# Now order the columns
XY[, order(as.numeric(colnames(XY)))]

R apply on a matrix a function of columns and row index

I would like to apply on a matrix a function of both the value, the row index and the column index for every value in the matrix and get the transformed matrix.
For example
mat<-matrix(c(1,2,3,4),2,2)
mat
[,1] [,2]
[1,] 1 3
[2,] 2 4
f<-function(x,i,j){x+i+j}
mat2 <- my.apply(f,mat)
mat2
[,1] [,2]
[1,] 3 6
[2,] 5 8
The example above is for illustration purposes, f can be much more complex.
apply does not do the job, because of the way the extra arguments are handled.
apply(mat,1:2,f,seq_along(mat[,1]),seq_along(mat[1,]))
, , 1
[,1] [,2]
[1,] 3 4
[2,] 5 6
, , 2
[,1] [,2]
[1,] 5 6
[2,] 7 8
I can not find either a way with the lapply family. A for loop can do the job but it won't be efficient nor elegant.
Any suggestions?
Thanks
Try mapply
mat <- matrix(c(1, 2, 3, 4), 2, 2)
mat
## [,1] [,2]
## [1,] 1 3
## [2,] 2 4
matrix(mapply(function(x, i, j) x + i + j, mat, row(mat), col(mat)), nrow = nrow(mat))
## [,1] [,2]
## [1,] 3 6
## [2,] 5 8
Here is an ugly use of apply, just for some quick and dirty job. The trick is adding an additional column (or row) for row (or column) indices.
mat <- matrix(c(1, 2, 3, 4), 2, 2)
t(apply(cbind(mat, 1:nrow(mat)), 1, function(x){x[1:ncol(mat)] + 1:ncol(mat) + x[ncol(mat)+1]}))
## [,1] [,2]
##[1,] 3 5
##[2,] 6 8
If you have a function f(x, i, j) already, you can also try:
apply(cbind(mat, 1:nrow(mat)), 1, function(x){a = numeric(); for(j in 1:ncol(mat)){a[j] = f(x[j], x[ncol(mat)+1], j)}; a})

Resources