Convert R 3D array to stacked matrix - r

I have a 3D R array, e.g.:
a <- array(1:27, dim = c(3,3,3))
How can I (efficiently) convert this into a matrix in which the 3rd dimension is bound / stacked below each other, i.e.:
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
[4,] 10 13 16
[5,] 11 14 17
[6,] 12 15 18
[7,] 19 22 25
[8,] 20 23 26
[9,] 21 24 27
I can clumsily achieve this with:
rbind(a[,,1], a[,,2], a[,,3])
but this is not generalizable well if I have many entries in the 3rd dimension (except with looping).
There must be a more elegant way to achieve this, but I could not find it. Ideas like
apply(a, 3, rbind)
apply(a, 3, c)
create a matrix, but the 3rd dimension simply become the columns. I want to keep the 2D matrices of the first 2 dimensions and just bind them together. I am aware this will mess up the indices, but we can disregard this for my use case.
I would be especially happy about a base R solution, but am also interested if this can be achieved with a (lightweight) package.
Edit:
This answer to a seemingly unrelated question provided a useful hint. This approach seems to achieve the desired result:
matrix(aperm(a, c(1, 3, 2)), nrow = dim(a)[1] * dim(a)[3])
Are there other ideas?

With aperm we transpose an array by permuting its dimensions and optionally resizing it:
y <- aperm(a, c(1, 3, 2))
dim(y) <- c(prod(dim(a)[-2]), dim(a)[2])
y
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
[4,] 10 13 16
[5,] 11 14 17
[6,] 12 15 18
[7,] 19 22 25
[8,] 20 23 26
[9,] 21 24 27

Related

One-liner to create a list of iterating sequences?

I need to create a list of sequences that always goes back to the first digit in the sequence. I've written the code below but it seems clunky. Is there a solution that uses fewer characters?
(i = seq(1, 24, by = 3))
#> [1] 1 4 7 10 13 16 19 22
(i_list = purrr::map(i, ~c(.:(. + 2), .)))
#> [[1]]
#> [1] 1 2 3 1
#>
#> [[2]]
#> ...
Edit: here's a way with lapply(). Not sure why this is getting downvotes, any advice on how to improve the question welcome!
(i_list = lapply(i, function(x) c(x:(x+2), x)))
I was wondering if there's a way with replicate() so have added that tag.
In matrix, rather than list form, theres:
cbind(matrix(1:24, ncol=3,byrow=TRUE),seq(1, 24, by = 3))
[,1] [,2] [,3] [,4]
[1,] 1 2 3 1
[2,] 4 5 6 4
[3,] 7 8 9 7
[4,] 10 11 12 10
[5,] 13 14 15 13
[6,] 16 17 18 16
[7,] 19 20 21 19
[8,] 22 23 24 22
and then you'd iterate over rows of the matrix instead of elements of the list.
Or if you are into code golf:
> seq(1,24,by=3) + t(matrix(c(0,1,2,0),ncol=8,nrow=4))
[,1] [,2] [,3] [,4]
[1,] 1 2 3 1
[2,] 4 5 6 4
[3,] 7 8 9 7
[4,] 10 11 12 10
...
but then how much work do you put into constructing the RHS of the + in this case? How is your question parameterised?
This depends on i having a regular pattern (with some adjustment for step size), it doesn't work for arbitrary i sequences.

R - Randomize each row in a matrix separately

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

Add columns of a matrix based on values of another vector

Suppose I have the following matrix:
mat <- matrix(1:20, ncol=5)
[,1] [,2] [,3] [,4] [,5]
[1,] 1 5 9 13 17
[2,] 2 6 10 14 18
[3,] 3 7 11 15 19
[4,] 4 8 12 16 20
and the following vector
counts=c(2,1,2)
I need to collapse this matrix by adding the columns based on each value of that vector counts. That means that the first two columns most be added, the third remain equal and sum the last two columns. My resulting matrix must be like this
[,1] [,2] [,3]
[1,] 6 9 30
[2,] 8 10 32
[3,] 10 11 34
[4,] 12 12 36
How could I do this in an automatic way, given that in my case I have a very big matrix and with a vector of counts with different values?
One way would be to replicate the sequence of 'counts' by 'counts' vector, use that to split the column sequence of 'mat' to return a list, loop through the list with sapply, use the column index to subset the 'mat' for each list element and get the rowSums.
mat2 <- sapply(split(1:ncol(mat), rep(seq_along(counts), counts)),
function(i) rowSums(mat[,i,drop=FALSE]))
dimnames(mat2) <- NULL
mat2
# [,1] [,2] [,3]
#[1,] 6 9 30
#[2,] 8 10 32
#[3,] 10 11 34
#[4,] 12 12 36
Another idea, conceptually similar to akrun's:
t(rowsum(t(mat), rep(seq_along(counts), counts)))
# 1 2 3
#[1,] 6 9 30
#[2,] 8 10 32
#[3,] 10 11 34
#[4,] 12 12 36

Adding combinations of rows to columns

I have 17 square matrices of order 430 and a large matrix of dimension 92235 x 34
For each square matrix, I wish to add each row's value (from 1 to 430) to a column in the large matrix, taking only those values above the main diagonal. So [1,2][1,3][1,4]..[1,430][2,3][2,4]...[2,430]...[429,430] -- hence, the 92235 row length of the large matrix (A sample of Step 1 is shown here http://imgur.com/4SlUenK)
The square matrix is transposed
Step 1 is repeated but the row values are added to the next column in the large matrix
Repeat Steps 1-3 16 more times until the large matrix is filled
How do I go about doing this?
TIA
EDIT FOR COMMENT
mat = matrix(1:25,5,5)
mat
[,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
mat2 = cbind(mat[upper.tri(mat)])
mat2
[,1]
[1,] 6
[2,] 11
[3,] 12
[4,] 16
[5,] 17
[6,] 18
[7,] 21
[8,] 22
[9,] 23
[10,] 24
This reads columns then rows. Instead, I would like to read rows then columns so that the result should be:
[,1]
[1,] 6
[2,] 11
[3,] 16
[4,] 21
[5,] 12
[6,] 17
[7,] 22
[8,] 18
[9,] 23
[10,] 24
If you have 17 square matrices ('m1', 'm2',...'m17'), keep them in a list and then use upper.tri to extract the elements above the diagonal and cbind with the elements from the transpose
lst1 <- mget(paste0('m', 1:17))
Out <- do.call(cbind,lapply(lst1, function(x) {x1 <- t(x)
cbind(x[upper.tri(x)], x1[upper.tri(x1)]) }))
dim(Out)
#[1] 92235 34
Here I created the matrices in a list.
Update
Based on the row order of the data,
mat1 <- mat
mat1[lower.tri(mat1, diag=TRUE)] <- NA
as.vector(na.omit(unlist(tapply(mat1, row(mat1), FUN=I))))
#[1] 6 11 16 21 12 17 22 18 23 24
Or as #David Arenburg mentioned in the comments
temp <- t(mat)
temp[lower.tri(temp)]
#[1] 6 11 16 21 12 17 22 18 23 24
You can replace the steps as here in the lapply.
data
set.seed(24)
lst1 <- replicate(17, matrix(sample(1:200, 430*430, replace=TRUE),
430, 430), simplify=FALSE)

R: matrix by vector multiplication

I have following problem:
myvec <- c(1:3)
mymat <- as.matrix(cbind(a = 6:15, b = 16:25, c= 26:35))
mymat
a b c
[1,] 6 16 26
[2,] 7 17 27
[3,] 8 18 28
[4,] 9 19 29
[5,] 10 20 30
[6,] 11 21 31
[7,] 12 22 32
[8,] 13 23 33
[9,] 14 24 34
[10,] 15 25 35
I want to multiply the mymat with myvec and construct new vector such that
sum(6*1, 16*2, 26*3)
sum(7*1, 17*2, 27*3)
....................
sum(15*1, 25*2, 35*3)
Sorry, this is simple question that I do not know...
Edit: typo corrected
The %*% operator in R does matrix multiplication:
> mymat %*% myvec
[,1]
[1,] 116
[2,] 122
...
[10,] 170
An alternative, but longer way can be this one:
rowSums(t(apply(mymat, 1, function(x) myvec*x)),na.rm=T)
Is the only way that I found that can ignore NA's inside the matrix.
Matrices are vectors in column major order:
colSums( t(mymat) * myvec )
(Edited after hopefully reading question correctly this time.)

Resources