Steve Hwang
Steve Hwang

Reputation: 1649

How can I separate a matrix into smaller ones in R?

I have the following matrix

2    4    1
6    32   1
4    2    1
5    3    2
4    2    2

I want to make the following two matrices based on 3rd column

first

2    4
6    32
4    2

second

5    3
4    2

Best I can come up with, but I get an error

x <- cbind(mat[,1], mat[,2]) if mat[,3]=1

y <- cbind(mat[,1], mat[,2]) if mat[,3]=2

Upvotes: 10

Views: 16913

Answers (6)

GKi
GKi

Reputation: 39707

split.data.frame could be used also to split a matrix.

mat <- matrix(c(1:10,1,1,1,2,2), ncol=3)

x <- split.data.frame(mat[,-3], mat[,3])
x
#$`1`
#     [,1] [,2]
#[1,]    1    6
#[2,]    2    7
#[3,]    3    8
#
#$`2`
#     [,1] [,2]
#[1,]    4    9
#[2,]    5   10

str(x)
#List of 2
# $ 1: num [1:3, 1:2] 1 2 3 6 7 8
# $ 2: num [1:2, 1:2] 4 5 9 10

Or split the index and and use it in lapply to subset.

lapply(split(seq_along(mat[,3]), mat[,3]), \(i) mat[i, -3, drop=FALSE])
#$`1`
#     [,1] [,2]
#[1,]    1    6
#[2,]    2    7
#[3,]    3    8
#
#$`2`
#     [,1] [,2]
#[1,]    4    9
#[2,]    5   10

Upvotes: 4

ThomasIsCoding
ThomasIsCoding

Reputation: 102241

We can use by or tapply

> by(seq_along(mat[, 3]), mat[, 3], function(k) mat[k, -3])
mat[, 3]: 1
     [,1] [,2]
[1,]    1    6
[2,]    2    7
[3,]    3    8
------------------------------------------------------------
mat[, 3]: 2
     [,1] [,2]
[1,]    4    9
[2,]    5   10

> tapply(seq_along(mat[, 3]), mat[, 3], function(k) mat[k, -3])
$`1`
     [,1] [,2]
[1,]    1    6
[2,]    2    7
[3,]    3    8

$`2`
     [,1] [,2]
[1,]    4    9
[2,]    5   10

Upvotes: 0

thelatemail
thelatemail

Reputation: 93908

Yet another example:

#test data
mat <- matrix(1:15,ncol=3)
mat[,3] <- c(1,1,1,2,2)

#make a list storing a matrix for each id as components
result <- lapply(by(mat,mat[,3],identity),as.matrix)

Final product:

> result
$`1`
  V1 V2 V3
1  1  6  1
2  2  7  1
3  3  8  1

$`2`
  V1 V2 V3
4  4  9  2
5  5 10  2

Upvotes: 7

IRTFM
IRTFM

Reputation: 263411

This is a functional version of pedrosorio's idea:

 getthird <- function(mat, idx) mat[mat[,3]==idx, 1:2]
 sapply(unique(mat[,3]), getthird, mat=mat)  #idx gets sent the unique values
#-----------
[[1]]
     [,1] [,2]
[1,]    1    6
[2,]    2    7
[3,]    3    8

[[2]]
     [,1] [,2]
[1,]    4    9
[2,]    5   10

Upvotes: 2

pedrosorio
pedrosorio

Reputation: 870

If you have a matrix A, this will get the first two columns when the third column is 1:

A[A[,3] == 1,c(1,2)]

You can use this to obtain matrices for any value in the third column.

Explanation: A[,3] == 1 returns a vector of booleans, where the i-th position is TRUE if A[i,3] is 1. This vector of booleans can be used to index into a matrix to extract the rows we want.

Disclaimer: I have very little experience with R, this is the MATLAB-ish way to do it.

Upvotes: 4

Ari B. Friedman
Ari B. Friedman

Reputation: 72749

If mat is your matrix:

mat <- matrix(1:15,ncol=3)
mat[,3] <- c(1,1,1,2,2)
> mat
     [,1] [,2] [,3]
[1,]    1    6    1
[2,]    2    7    1
[3,]    3    8    1
[4,]    4    9    2
[5,]    5   10    2

Then you can use split:

> lapply( split( mat[,1:2], mat[,3] ), matrix, ncol=2)
$`1`
     [,1] [,2]
[1,]    1    6
[2,]    2    7
[3,]    3    8

$`2`
     [,1] [,2]
[1,]    4    9
[2,]    5   10

The lapply of matrix is necessary because split drops the attributes that make a vector a matrix, so you need to add them back in.

Upvotes: 16

Related Questions