user189035 user189035 - 2 months ago 10
R Question

Increment values in vector conditionally (conditional restarting running sum)-- vectorized version?

Given a vector of binary values

out_2
the code below returns a vector
of same length as
out_2
called
out_1
.
The entries of
out_1
counts the number of successive similar items left in
out_2
before the next sign switch. If you print the final
cbind()
I think you will see what I mean.

library(zoo)
n = 10
out_2 = rep(NA, n)
out_2[sample.int(n, 3)] = sample(c(-1, 1), 3, replace = TRUE)
out_2 = zoo::na.locf(out_2)
out_1 = out_2
out_1[length(out_2)] = 1
for(i in (length(out_2) - 1):1){
out_1[i] = ifelse(out_2[i + 1] == out_2[i], out_1[i + 1] + 1, 1)
}
cbind(out_1, out_2)


I was wondering if there is a one liner vectorized way to get
out_1
from
out_2
(i.e. vectoring the explicit for loop)?

Answer Source

I would use rle from base R. The tricky part is to get the reverse order for the out_1 vector, so it has (?) to go through `lapply'

out_1<- unlist(lapply(rle(out_2)$lengths, function(x) seq(x, by=-1)))

And the result is:

cbind(out_1, out_2)
      out_1 out_2
 [1,]     2    -1
 [2,]     1    -1
 [3,]     1     1
 [4,]     6    -1
 [5,]     5    -1
 [6,]     4    -1
 [7,]     3    -1
 [8,]     2    -1
 [9,]     1    -1

If you're flexible with the output and you don't need it to be in reverse order, you can simply use the sequence function.

out_1<- sequence(rle(out_2)$lengths)
cbind(out_1, out_2)
      out_1 out_2
 [1,]     1    -1
 [2,]     2    -1
 [3,]     1     1
 [4,]     1    -1
 [5,]     2    -1
 [6,]     3    -1
 [7,]     4    -1
 [8,]     5    -1
 [9,]     6    -1