Jan Kislinger
Jan Kislinger

Reputation: 1573

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

Upvotes: 0

Views: 893

Answers (2)

Cath
Cath

Reputation: 24074

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.

Upvotes: 2

Zheyuan Li
Zheyuan Li

Reputation: 73395

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

Upvotes: 4

Related Questions