lightsnail
lightsnail

Reputation: 788

Convert a matrix into a specific list in R

"a" is a matrix and "b" is a numeric. The row number of "a" is the same with the length of "b".

a<-matrix(1:24,6,4,byrow = T)
b<-c(3,1,2)

I want to convert "a" into a list of which the number of elements in each object is the same with "b". The expected result is as follows:

[[1]]
     [,1] [,2] [,3] [,4] 
[1,]    1    2    3    4 
[2,]    5    6    7    8  
[3,]    9   10   11   12 

[[2]]
     [,1] [,2] [,3] [,4] 
[1,]   13   14   15   16 

[[3]]
     [,1] [,2] [,3] [,4] 
[1,]   17   18   19   20  
[2,]   21   22   23   24 

Thank you so much for your help!

Upvotes: 1

Views: 71

Answers (3)

Joris Meys
Joris Meys

Reputation: 108593

You could also use mapply() like this:

id <- mapply(seq, to=cumsum(b), length.out =b)
lapply(id, function(i) a[i,, drop = FALSE])

Or, if you want to do everything in one go:

mapply(function(i,j){a[seq(to=i, length.out = j),,drop=FALSE]},
           i = cumsum(b),
           j = b)

Compared to using lapply(split(...)), this solution is about 1.5 times faster.

a<-matrix(1:(87*400),87,400,byrow = TRUE)
b<-c(3, 1, 2, 5, 2, 2, 1, 11, 19, 12, 9, 20)

benchmark(
  {
    id <- mapply(seq, to=cumsum(b), length.out =b)
    lapply(id, function(i)a[i,, drop = FALSE])
  },
  {
    lapply(split(a, rep(seq_along(b),b)), matrix, ncol = ncol(a))
  },
  replications = 10000
)

gives

  replications elapsed relative user.self sys.self user.child sys.child
1        10000    3.53    1.000      3.53        0         NA        NA
2        10000    6.02    1.705      5.95        0         NA        NA

Upvotes: 3

akrun
akrun

Reputation: 887851

We could use split

split(as.data.frame(a),cumsum(c(TRUE,diff(sequence(b))!=1)))
#$`1`
#  V1 V2 V3 V4
#1  1  2  3  4
#2  5  6  7  8
#3  9 10 11 12

#$`2`
#  V1 V2 V3 V4
#4 13 14 15 16

#$`3`
#  V1 V2 V3 V4
#5 17 18 19 20
#6 21 22 23 24

Upvotes: 1

talat
talat

Reputation: 70336

Here's an option that will split a according to b and since that will return a list of atomic vectors, we lapply over it to convert to matrix again.

lapply(split(a, rep(seq_along(b),b)), matrix, ncol = ncol(a))
#$`1`
#     [,1] [,2] [,3] [,4]
#[1,]    1    2    3    4
#[2,]    5    6    7    8
#[3,]    9   10   11   12
#
#$`2`
#     [,1] [,2] [,3] [,4]
#[1,]   13   14   15   16
#
#$`3`
#     [,1] [,2] [,3] [,4]
#[1,]   17   18   19   20
#[2,]   21   22   23   24

Or if you didn't mind a list of data.frames it could be:

split(as.data.frame(a), rep(seq_along(b),b))

Upvotes: 6

Related Questions