Reputation: 351
I have this matrix
mdat <- matrix(c(0,1,1,1,0,0,1,1,0,1,1,1,1,0,1,1,1,1,0,1), nrow = 4, ncol = 5, byrow = TRUE)
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 1 1 0
[2,] 0 1 1 0 1
[3,] 1 1 1 0 1
[4,] 1 1 1 0 1
and I'm trying to build T:
T1 T2 T3
row1 1 2 4
row2 2 2 3
row3 2 5 5
row4 3 1 3
row5 3 5 5
row6 4 1 3
row7 4 5 5
where for each row in mdat: T1 shows mdat row number T2 shows mdat column where there's the first 1 T3 shows mdat column where there's the last consecutive 1.
Therefore
row1 in T is [1 2 4] because for row 1 in mdat the first 1 is in column 2 and the last consecutive 1 is in column 4.
row2 in T is [2 2 3] because for row 2 in mdat the first 1 is in column 2 and the last consecutive 1 is in column 3.
This is my try:
for (i in 1:4){
for (j in 1:5) {
if (mdat[i,j]==1) {T[i,1]<-i;T[i,2]<-j;
cont<-0;
while (mdat[i,j+cont]==1){
cont<-cont+1;
T[i,3]<-cont}
}
}
}
Upvotes: 1
Views: 72
Reputation: 1021
Try the Bioconductor IRanges package:
library(IRanges)
r <- unlist(slice(split(Rle(mdat), row(mdat)), 1, rangesOnly=TRUE)))
r
IRanges of length 7
start end width names
[1] 2 4 3 1
[2] 2 3 2 2
[3] 5 5 1 2
[4] 1 3 3 3
[5] 5 5 1 3
[6] 1 3 3 4
[7] 5 5 1 4
EDIT: optimized
Upvotes: 2
Reputation: 206401
Here's a strategy using apply/rle
as Richard suggested.
xx<-apply(mdat, 1, function(x) {
r <- rle(x)
w <- which(r$values==1)
l <- r$lengths[w]
s <- cumsum(c(0,r$lengths))[w]+1
cbind(start=s,stop=s+l-1)
})
do.call(rbind, Map(cbind, row=seq_along(xx), xx))
We start by finding the runs of 1 on each row using the "values" property of the rle
and we calculate their start and stop positions using the "lengths" property. We turn this data into a list of two column matrices with one list item per row of the original matrix.
Now we use Map to add the row number back onto the matrix and then we rbind all the results. That seems to give you the data you're after
row start stop
[1,] 1 2 4
[2,] 2 2 3
[3,] 2 5 5
[4,] 3 1 3
[5,] 3 5 5
[6,] 4 1 3
[7,] 4 5 5
Upvotes: 3