8

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

Community
  • 1
  • 1
Steve Hwang
  • 1,589
  • 4
  • 14
  • 12
  • 2
    Please post [reproducible examples](http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) in the future. You'll get better responses that way. – Ari B. Friedman Oct 25 '12 at 00:49

6 Answers6

15

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.

Ari B. Friedman
  • 71,271
  • 35
  • 175
  • 235
7

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
thelatemail
  • 91,185
  • 12
  • 128
  • 188
4

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.

pedrosorio
  • 870
  • 5
  • 8
3

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

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

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
GKi
  • 37,245
  • 2
  • 26
  • 48
2

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
IRTFM
  • 258,963
  • 21
  • 364
  • 487
0

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
ThomasIsCoding
  • 96,636
  • 9
  • 24
  • 81