Specific sequence creation in R - r

I want to create the following sequences in a smart way instead of hard-coding them:
'0-0-0-0-0-0'
'0-1-0-0-0-0'
'0-0-1-0-0-0'
'0-0-0-1-0-0'
'0-0-0-0-1-0'
'0-0-0-0-0-1'
'1-0-0-0-0-0'
'1-1-0-0-0-0'
'1-0-1-0-0-0'
'1-0-0-1-0-0'
'1-0-0-0-1-0'
'1-0-0-0-0-1'
'1-1-1-1-1-1'
'2-0-0-0-0-0'
'2-1-0-0-0-0'
'2-0-1-0-0-0'
'2-0-0-1-0-0'
'2-0-0-0-1-0'
'2-0-0-0-0-1'
'3-0-0-0-0-0'
'3-1-0-0-0-0'
'3-0-1-0-0-0'
'3-0-0-1-0-0'
'3-0-0-0-1-0'
'3-0-0-0-0-1'
'0-2-0-0-0-0'
'0-0-2-0-0-0'
'0-0-0-2-0-0'
'0-0-0-0-2-0'
'0-0-0-0-0-2'
and so on...
Elaborating more on the details of the pattern that presents: I have 4 states {0,1,2,3} and I want to find all the possible combinations for sequences of length=6 starting with any of the states and allowing only one intermediate position of the sequence to be present in any of the next positions.

Here's one method. I generate a simple description of each sequence, then build the sequences (and de-duplicate, which is needed because of the all-intermediate-0 items).
dd = expand.grid(first = 0:3, inter_value = 0:3, inter_position = 2:6)
result = t(apply(dd, 1, function(x) {
z = c(x["first"], rep(0L, 5))
z[x["inter_position"]] = x["inter_value"]
z
}))
result = result[!duplicated(result), ]
dim(result)
# [1] 64 6
head(result, 10)
# first
# [1,] 0 0 0 0 0 0
# [2,] 1 0 0 0 0 0
# [3,] 2 0 0 0 0 0
# [4,] 3 0 0 0 0 0
# [5,] 0 1 0 0 0 0
# [6,] 1 1 0 0 0 0
# [7,] 2 1 0 0 0 0
# [8,] 3 1 0 0 0 0
# [9,] 0 2 0 0 0 0
# [10,] 1 2 0 0 0 0
Getting the dashes:
apply(result, 1, paste, collapse = "-")
# [1] "0-0-0-0-0-0" "1-0-0-0-0-0" "2-0-0-0-0-0" "3-0-0-0-0-0" "0-1-0-0-0-0" "1-1-0-0-0-0" "2-1-0-0-0-0"
# [8] "3-1-0-0-0-0" "0-2-0-0-0-0" "1-2-0-0-0-0" "2-2-0-0-0-0" "3-2-0-0-0-0" "0-3-0-0-0-0" "1-3-0-0-0-0"
# [15] "2-3-0-0-0-0" "3-3-0-0-0-0" "0-0-1-0-0-0" "1-0-1-0-0-0" "2-0-1-0-0-0" "3-0-1-0-0-0" "0-0-2-0-0-0"
# [22] "1-0-2-0-0-0" "2-0-2-0-0-0" "3-0-2-0-0-0" "0-0-3-0-0-0" "1-0-3-0-0-0" "2-0-3-0-0-0" "3-0-3-0-0-0"
# [29] "0-0-0-1-0-0" "1-0-0-1-0-0" "2-0-0-1-0-0" "3-0-0-1-0-0" "0-0-0-2-0-0" "1-0-0-2-0-0" "2-0-0-2-0-0"
# [36] "3-0-0-2-0-0" "0-0-0-3-0-0" "1-0-0-3-0-0" "2-0-0-3-0-0" "3-0-0-3-0-0" "0-0-0-0-1-0" "1-0-0-0-1-0"
# [43] "2-0-0-0-1-0" "3-0-0-0-1-0" "0-0-0-0-2-0" "1-0-0-0-2-0" "2-0-0-0-2-0" "3-0-0-0-2-0" "0-0-0-0-3-0"
# [50] "1-0-0-0-3-0" "2-0-0-0-3-0" "3-0-0-0-3-0" "0-0-0-0-0-1" "1-0-0-0-0-1" "2-0-0-0-0-1" "3-0-0-0-0-1"
# [57] "0-0-0-0-0-2" "1-0-0-0-0-2" "2-0-0-0-0-2" "3-0-0-0-0-2" "0-0-0-0-0-3" "1-0-0-0-0-3" "2-0-0-0-0-3"
# [64] "3-0-0-0-0-3"

Here's a general nested for-loop solution. Not the most efficient in the world, but gets the desired result (Note: You can change states and/or sequence_len and the sequences will be generated automatically):
states <- 0:3
states_len <- length(states)
sequence_len <- 6
sequence_mat <- matrix(0, states_len*{{states_len-1}*{sequence_len-1}+1}, sequence_len)
rw <- 1
for(ii in states){
for(jj in states){
for(kk in 2:sequence_len){
if(jj != 0){
rw = rw + 1
}
sequence_mat[rw, 1] <- ii
sequence_mat[rw, kk] <- jj
if(jj == rev(states)[1] && kk == sequence_len){
rw = rw + 1
}
}
}
}
Output:
> head(sequence_mat, 20)
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 0 0 0 0 0 0
[2,] 0 1 0 0 0 0
[3,] 0 0 1 0 0 0
[4,] 0 0 0 1 0 0
[5,] 0 0 0 0 1 0
[6,] 0 0 0 0 0 1
[7,] 0 2 0 0 0 0
[8,] 0 0 2 0 0 0
[9,] 0 0 0 2 0 0
[10,] 0 0 0 0 2 0
[11,] 0 0 0 0 0 2
[12,] 0 3 0 0 0 0
[13,] 0 0 3 0 0 0
[14,] 0 0 0 3 0 0
[15,] 0 0 0 0 3 0
[16,] 0 0 0 0 0 3
[17,] 1 0 0 0 0 0
[18,] 1 1 0 0 0 0
[19,] 1 0 1 0 0 0
[20,] 1 0 0 1 0 0

Related

Find number of neighbouring values in a matrice (R)

My matrice is like this:
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] 0 0 0 0 0 0 0 0 0
[2,] 0 0 0 0 0 0 0 0 0
[3,] 0 0 0 0 0 0 0 0 0
[4,] 0 0 0 0 0 0 0 1 1
[5,] 0 0 0 0 0 0 1 1 0
[6,] 0 0 0 0 0 1 0 0 0
[7,] 0 0 0 0 1 1 0 0 0
[8,] 0 0 0 0 1 0 0 0 0
[9,] 0 0 0 0 1 0 0 0 0
[10,] 0 0 0 0 1 1 0 0 0
[11,] 0 0 0 0 0 1 0 0 0
[12,] 0 0 0 0 0 1 1 1 1
[13,] 0 0 0 0 0 0 0 0 0
[14,] 0 0 0 0 0 0 0 0 0
[15,] 0 0 0 0 0 0 0 0 0
[16,] 0 0 0 0 0 0 0 0 0
[17,] 0 0 0 0 0 0 0 0 0
[,10] [,11] [,12] [,13] [,14] [,15] [,16] [,17]
[1,] 0 0 0 0 0 0 0 0
[2,] 0 0 0 0 0 0 0 0
[3,] 0 0 0 0 0 0 0 0
[4,] 1 1 0 0 0 0 0 0
[5,] 0 1 0 0 0 0 0 0
[6,] 0 1 0 0 0 0 0 0
[7,] 0 1 0 0 0 0 0 0
[8,] 0 1 0 0 0 1 0 0
[9,] 0 1 0 0 0 1 0 0
[10,] 1 1 0 0 0 1 0 0
[11,] 1 1 0 1 1 0 0 0
[12,] 1 1 1 1 0 0 0 0
[13,] 0 0 0 0 0 0 0 0
[14,] 0 0 0 0 0 0 0 0
[15,] 0 0 0 0 0 0 0 0
[16,] 0 0 0 0 0 0 0 0
[17,] 0 0 0 0 0 0 0 0
[,18]
[1,] 0
[2,] 0
[3,] 0
[4,] 0
[5,] 0
[6,] 0
[7,] 0
[8,] 0
[9,] 0
[10,] 0
[11,] 0
[12,] 0
[13,] 0
[14,] 0
[15,] 0
[16,] 0
[17,] 0
How can I find the number of values which has 1 in neighbour? (neighbour of a pixel is the value above the value, below the value, to the right, to the left, top right, top left, below left, below right).
I just need to get a way of devising how I can even find the number of values which has 1 above/below it. If I get that, I'll be able to solve the other variables of the problem (top right and such).
I've been experimenting around with which such as which(imageMatrix == 1, arr.ind = TRUE)[1,1]. But I cannot figure it out. (ImageMatrix is the name of the matrix)
Can anyone lend me a hand on how I can begin with the problem so I get a jump?
In the following we use the example matrix m generated reproducibly in the Note at the end.
1) Append a row and column of zeros on each end and then apply rollsum , transpose, apply it again and transpose again. Finally subtract the original matrix so that only neighbors are counted. This solution is the most compact of those here.
library(zoo)
m2 <- rbind(0, cbind(0, m, 0), 0)
t(rollsum(t(rollsum(m2, 3)), 3)) - m
## [,1] [,2] [,3] [,4] [,5]
## [1,] 2 3 5 3 2
## [2,] 3 5 6 4 2
## [3,] 2 6 5 5 2
## [4,] 2 5 2 2 1
## [5,] 1 3 2 2 1
2) A second approach is the following. It would be much faster than the others here.
nr <- nrow(m)
nc <- ncol(m)
mm <- cbind(m[, -1], 0) + m + cbind(0, m[, -nc])
rbind(mm[-1, ], 0) + mm + rbind(0, mm[-nr, ]) - m
## [,1] [,2] [,3] [,4] [,5]
## [1,] 2 3 5 3 2
## [2,] 3 5 6 4 2
## [3,] 2 6 5 5 2
## [4,] 2 5 2 2 1
## [5,] 1 3 2 2 1
3) Using loops we can write the following. It is probably the most straight forward. Note that subscripting by 0 omits that element and i %% 6 equals i if i is in 1, 2, 3, 4, 5 and equals 0 if i equals 0 or 6.
nr <- nrow(m); nr1 <- nr + 1
nc <- ncol(m); nc1 <- nc + 1
mm <- 0 * m # initialize result
for(i in seq_len(nr))
for(j in seq_len(nc))
mm[i, j] <- sum(m[seq(i-1, i+1) %% nr1, seq(j-1, j+1) %% nc1]) - m[i,j]
mm
## [,1] [,2] [,3] [,4] [,5]
## [1,] 2 3 5 3 2
## [2,] 3 5 6 4 2
## [3,] 2 6 5 5 2
## [4,] 2 5 2 2 1
## [5,] 1 3 2 2 1
Note
set.seed(123)
m <- +matrix(rnorm(25) > 0, 5)
m
## [,1] [,2] [,3] [,4] [,5]
## [1,] 0 1 1 1 0
## [2,] 0 1 1 1 0
## [3,] 1 0 1 0 0
## [4,] 1 0 1 1 0
## [5,] 1 0 0 0 0

R: all possible combinations of a binary vector of length n, where only one value is “1” in each combination of m

Is there a way in R to create binary sets of m, filled with all combinations of n by columns, while by row only 1 value can be "1"?
For example, for n=2 and m=2, we would have the following combinations of m each:
(00, 00), (10,00), (01,00), (00,10), (00,01), (10,01), (01,10), (10,10), (01,01)
But these, for example, are not allowed:
(11,00), (01,11), (00,11), (11,10), (11,11)
This is very similar to your other question. In my answer to that question, we see that rephrasing the question, makes it much easier to attack. So for this question, we can reduce it to: "How to generate all pairwise permutations of powers of 2 with repeats?"
We can use almost exactly the same setup as before, only this time we set the argument repeats.allowed = TRUE.
library(gtools)
bitPairwise2 <- function(numBits, groupSize) {
t(sapply(t(permutations(numBits + 1, groupSize,
c(0, 2^(0:(numBits-1))), repeats.allowed = TRUE)),
function(x) {as.integer(intToBits(x))})[1:numBits, ])
}
bitPairwise2(2,2)
[,1] [,2]
[1,] 0 0 ## (00,00)
[2,] 0 0
[3,] 0 0 ## (00,10)
[4,] 1 0
[5,] 0 0 ## (00,01)
[6,] 0 1
[7,] 1 0 ## (10,00)
[8,] 0 0
[9,] 1 0 ## (10,10)
[10,] 1 0
[11,] 1 0 ## (10,01)
[12,] 0 1
This function generalizes to any number of bits as well as any number of groups. For example, all possible 3-tuples of 3 bits is given by:
## first 9 groups
bitPairwise2(3, 3)[1:27, ]
[,1] [,2] [,3]
[1,] 0 0 0 ## (000,000,000)
[2,] 0 0 0
[3,] 0 0 0
[4,] 0 0 0 ## (000,000,100)
[5,] 0 0 0
[6,] 1 0 0
[7,] 0 0 0 ## (000,000,010)
[8,] 0 0 0
[9,] 0 1 0
[10,] 0 0 0 ## (000,000,001)
[11,] 0 0 0
[12,] 0 0 1
[13,] 0 0 0 ## (000,100,000)
[14,] 1 0 0
[15,] 0 0 0
[16,] 0 0 0 ## (000,100,100)
[17,] 1 0 0
[18,] 1 0 0
[19,] 0 0 0 ## (000,100,010)
[20,] 1 0 0
[21,] 0 1 0
[22,] 0 0 0 ## (000,100,001)
[23,] 1 0 0
[24,] 0 0 1
[25,] 0 0 0 ## (000,010,000)
[26,] 0 1 0
[27,] 0 0 0
And here are the last 9 groups:
a <- bitPairwise2(3, 3)[166:192, ]
row.names(a) <- 166:192
a
[,1] [,2] [,3]
166 0 0 1 ## (001,100,001)
167 1 0 0
168 0 0 1
169 0 0 1 ## (001,010,000)
170 0 1 0
171 0 0 0
172 0 0 1 ## (001,010,100)
173 0 1 0
174 1 0 0
175 0 0 1 ## (001,010,010)
176 0 1 0
177 0 1 0
178 0 0 1 ## (001,010,001)
179 0 1 0
180 0 0 1
181 0 0 1 ## (001,001,000)
182 0 0 1
183 0 0 0
184 0 0 1 ## (001,001,100)
185 0 0 1
186 1 0 0
187 0 0 1 ## (001,001,010)
188 0 0 1
189 0 1 0
190 0 0 1 ## (001,001,001)
191 0 0 1
192 0 0 1
If you need the output in a list, try this:
test <- bitPairwise2(4, 3)
numGroups <- nrow(test)/3
makeGroupList <- function(mat, nG, groupSize) {
lapply(1:nG, function(x) {
s <- groupSize*(x-1) + 1
e <- s + (groupSize - 1)
mat[s:e, ]
})
}
makeGroupList(test, numGroups, 3)
[[1]]
[,1] [,2] [,3] [,4]
[1,] 0 0 0 0
[2,] 0 0 0 0
[3,] 0 0 0 0
[[2]]
[,1] [,2] [,3] [,4]
[1,] 0 0 0 0
[2,] 0 0 0 0
[3,] 1 0 0 0
[[3]]
[,1] [,2] [,3] [,4]
[1,] 0 0 0 0
[2,] 0 0 0 0
[3,] 0 1 0 0
. . . . .
. . . . .
. . . . .

Populate vector down or up with unique element value (like na.locf)

I have a large dataframe with each column containing one flag from the set {-1,1}, all the rest of the values are set to zero. I want to fill up or down the rest of the column entries with a value corresponding to that flag value. for example, given a vector to represent 1 column, I have
v <- rep(0,15)
v[12] <- 1
#I'd want a function that is something like:
f <- function(v,flag){
for(i in 2:length(v)){ if(v[i-1]==flag) v[i] <- flag else v[i]<-v[i]}
v
}
> v
[1] 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
> f(v,1)
[1] 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1
The example works fine for filling forward some v and a flag 1. I'd also want to be able to fill backwards with 1 based on a -1 flag. The obvious solution that comes to mind is na.locf, except I can't get it to work with a 1 in the middle and filling forward and backwards. Even if I populate the 0 elements with NA, it will still not partially fill up or down based on a flag.
Are there any simple and fast vectorized functions that could do this with a matrix or zoo object populated with all zeros, except where there is one element with 1 or -1 in each column, telling it to fill down or up with 1s depending on the value?
edit: thinking about it a bit more, I came up with a possible solution, that along with an illustration, (hopefully) makes it more clear what I want.
Also, the overall goal is to create a mask for Additions/Deletions to a fund index, by date, that fill forwards for additions (+1) and fill backwards for removals (-1). Also, why I thought of na.locf right away. Still not sure if this is the best approach for this block, though. Any thoughts appreciated.
#generate random matrix of flags
v.mtx <- matrix(0,15,10)
for(i in 1:10){
v.mtx[sample(1:15,1),i] <- sample(c(-1,1),1)
}
fill.flag <- function(v) {
if(any(-1 %in% v)) {v[1:which(v!=0)] <- 1}
else
if(any(1 %in% v)) {v[which(v!=0):length(v)] <- 1}
v
}
> v.mtx
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 0 0 0 0 1 0 0 0 0
[2,] 0 0 0 0 0 0 0 0 0 0
[3,] 0 0 0 0 0 0 0 0 0 0
[4,] 0 0 0 0 0 0 0 0 0 0
[5,] 0 0 0 0 0 0 0 0 0 0
[6,] 0 0 0 0 1 0 -1 0 0 0
[7,] 0 0 0 -1 0 0 0 0 0 0
[8,] 0 0 0 0 0 0 0 0 0 0
[9,] 0 0 0 0 0 0 0 1 0 -1
[10,] 0 0 0 0 0 0 0 0 -1 0
[11,] 0 0 0 0 0 0 0 0 0 0
[12,] 0 0 0 0 0 0 0 0 0 0
[13,] 0 0 1 0 0 0 0 0 0 0
[14,] 0 0 0 0 0 0 0 0 0 0
[15,] 1 -1 0 0 0 0 0 0 0 0
> apply(v.mtx,2,fill.flag)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 1 0 1 0 1 1 0 1 1
[2,] 0 1 0 1 0 1 1 0 1 1
[3,] 0 1 0 1 0 1 1 0 1 1
[4,] 0 1 0 1 0 1 1 0 1 1
[5,] 0 1 0 1 0 1 1 0 1 1
[6,] 0 1 0 1 1 1 1 0 1 1
[7,] 0 1 0 1 1 1 0 0 1 1
[8,] 0 1 0 0 1 1 0 0 1 1
[9,] 0 1 0 0 1 1 0 1 1 1
[10,] 0 1 0 0 1 1 0 1 1 0
[11,] 0 1 0 0 1 1 0 1 0 0
[12,] 0 1 0 0 1 1 0 1 0 0
[13,] 0 1 1 0 1 1 0 1 0 0
[14,] 0 1 1 0 1 1 0 1 0 0
[15,] 1 1 1 0 1 1 0 1 0 0
As #G. Grothendieck commented, you can try cummax and cummin, i.e.
f1 <- function(x){
if(sum(x) == 1){
return(cummax(x))
}else{
return(rev(cummin(rev(x)))* -1)
}
}
#apply as usual
apply(v.mtx, 2, f1)

Count the number of appeareances in a list, trick?

I have a matrix of 1 and 0. The rules concerning this table is as follows.
I would like to count the number of times a serie of 1,1 appears (where the 1 are not separated by 0!) and make the same thing for a serie of 1,1,1. I have tried colSums but it's seemed not to be very appropriate.
the matrix final is
t1 t2 t3 t4 t5 t6 t7
[1,] 0 0 0 0 1 1 0
[2,] 0 0 1 1 0 0 1
[3,] 1 1 0 0 0 0 0
[4,] 0 0 1 1 1 0 0
[5,] 0 0 1 1 0 0 0
[6,] 1 1 0 0 0 0 0
[7,] 0 0 0 0 0 0 1
[8,] 0 0 0 0 1 1 0
[9,] 1 1 0 0 1 1 0
[10,] 0 0 0 0 0 1 1
[11,] 1 1 0 0 0 0 0
[12,] 0 0 1 1 0 0 0
[13,] 0 0 0 0 0 0 0
[14,] 0 0 0 0 0 0 1
[15,] 0 0 0 0 0 0 0
Therefore for the first row I would like to have 1 time a serie of 1,1 and 0 time a serie of 1,1,1. For row 4 I would like to have 0 time a serie of 1,1 but 1 time a serie of 1,1,1.
Can anyone tell me whats wrong with the following code for a serie of 1,1?
occ <- matrix()
occ_temp <- matrix
for (j in 1:nrow(final)){
for (i in 2:7){
if (sum(final[j,i-1:i])==2){occ_temp[j,i-1]=1}
}
occ[j] <- sum(occ_temp)
}
We can loop through the rows with apply, get the run-length-type with rle, extract the lengths where the values are 1, check that are equal to 'n1' and 'n2', and get the sum.
n1 <- 2
n2 <- 3
res <- t(apply(m1, 1, FUN=function(x) {
x1 <- with(rle(x), lengths[!!values])
c(sum(x1==n1), sum(x1==n2))
}))
colnames(res) <- paste0("count", c(11, 111))
res
# count11 count111
# [1,] 1 0
# [2,] 1 0
# [3,] 1 0
# [4,] 0 1
# [5,] 1 0
# [6,] 1 0
# [7,] 0 0
# [8,] 1 0
# [9,] 2 0
#[10,] 1 0
#[11,] 1 0
#[12,] 1 0
#[13,] 0 0
#[14,] 0 0
#[15,] 0 0

R : Updating a matrix given a set of indices

I have a matrix(initialized to zeros) and a set of indices. If the i'th value in indices is j, then I want to set the (j,i)th entry of the matrix to 1.
For eg:
> m = matrix(0, 10, 7)
> indices
[1] 2 9 3 4 5 1 10
And the result should be
> result
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 0 0 0 0 0 1 0
[2,] 1 0 0 0 0 0 0
[3,] 0 0 1 0 0 0 0
[4,] 0 0 0 1 0 0 0
[5,] 0 0 0 0 1 0 0
[6,] 0 0 0 0 0 0 0
[7,] 0 0 0 0 0 0 0
[8,] 0 0 0 0 0 0 0
[9,] 0 1 0 0 0 0 0
[10,] 0 0 0 0 0 0 1
I asked a somewhat related question a little while back, which used a vector instead of a matrix. Is there a similar simple solution to this problem?
## OP's example data
m = matrix(0, 10, 7)
j <- c(2, 9, 3, 4, 5, 1, 10)
## Construct a two column matrix of indices (1st column w. rows & 2nd w. columns)
ij <- cbind(j, seq_along(j))
## Use it to subassign into the matrix
m[ij] <- 1
m
# [,1] [,2] [,3] [,4] [,5] [,6] [,7]
# [1,] 0 0 0 0 0 1 0
# [2,] 1 0 0 0 0 0 0
# [3,] 0 0 1 0 0 0 0
# [4,] 0 0 0 1 0 0 0
# [5,] 0 0 0 0 1 0 0
# [6,] 0 0 0 0 0 0 0
# [7,] 0 0 0 0 0 0 0
# [8,] 0 0 0 0 0 0 0
# [9,] 0 1 0 0 0 0 0
# [10,] 0 0 0 0 0 0 1
For the record, the answer in your linked question can easily be adapted to suit this scenario too by using sapply:
indices <- c(2, 9, 3, 4, 5, 1, 10)
sapply(indices, tabulate, nbins = 10)
# [,1] [,2] [,3] [,4] [,5] [,6] [,7]
# [1,] 0 0 0 0 0 1 0
# [2,] 1 0 0 0 0 0 0
# [3,] 0 0 1 0 0 0 0
# [4,] 0 0 0 1 0 0 0
# [5,] 0 0 0 0 1 0 0
# [6,] 0 0 0 0 0 0 0
# [7,] 0 0 0 0 0 0 0
# [8,] 0 0 0 0 0 0 0
# [9,] 0 1 0 0 0 0 0
# [10,] 0 0 0 0 0 0 1
For small datasets you might not notice the performance difference, but Josh's answer, which uses matrix indexing, would definitely be much faster, even if you changed my answer here to use vapply instead of sapply.

Resources