3

Hi I'm trying to add up values in a vector since a zero value in R. I have a vector like this:

wdnew
 [1] 1 0 0 1 1 0 0 1 1 1 1 1

And I want to create a new vector that counts the number of iterations or places since a zero value. If I did this properly I'd get a vector like this:

wdseq
 [1] 1 0 0 1 2 0 0 1 2 3 4 5

This ought to be straightforward but I can't figure out how to generate the sequence, any suggestions? Thanks,

Psidom
  • 209,562
  • 33
  • 339
  • 356
Rose
  • 55
  • 2
  • It doesn't matter for this time, but next time please remember to use `dput` to share your example data. – Hack-R Aug 19 '16 at 23:05
  • 1
    You have people answering who are assuming there are only zeros and 1s in the vector. Please be more specific. Are there always only zeros and 1s? Or can the nonzero values be any number? – Rich Scriven Aug 19 '16 at 23:16
  • 1
    Lots of answers, @Rose. Can you please "accept" your preferred answer (and potentially upvote any and all you find particularly good)? – r2evans Aug 20 '16 at 00:10
  • 1
    See [this identical post](http://stackoverflow.com/questions/32501902/cumulative-sum-that-resets-when-0-is-encountered) – alexis_laz Aug 20 '16 at 08:52

6 Answers6

5

One option is to use rleid to create a group variable and do cumsum by group:

wdnew <- c(1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1)
ave(wdnew, data.table::rleid(wdnew != 0), FUN=cumsum)
# [1] 1 0 0 1 2 0 0 1 2 3 4 5
Hack-R
  • 22,422
  • 14
  • 75
  • 131
Psidom
  • 209,562
  • 33
  • 339
  • 356
  • 1
    I think the `!= 0` is not necessary.. – Arun Aug 19 '16 at 23:53
  • 2
    @Arun It's not very clear if the original vector contains only 1 and 0. This is to handle that kind of case as pointed out by another user. – Psidom Aug 20 '16 at 00:03
4

An option without data.table:

wdnew <- c(1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1)
rl <- rle(wdnew)
unlist(mapply(function(a,b) b * seq_len(a), rl$lengths, rl$values))
#  [1] 1 0 0 1 2 0 0 1 2 3 4 5

As @DirtySockSniffer suggested, this can be combined into a single line:

with(rle(wdnew), unlist(mapply(function(a,b) b * seq_len(a), lengths, values)))

Or stepping off of @user20650's suggestion:

wdnew * sequence(rle(wdnew)$lengths)

I think this last one may win the codegolf ...

(All assuming that wdnew is all 0s and 1s.)

r2evans
  • 141,215
  • 6
  • 77
  • 149
  • not yet, though there are many advocates and many good reasons to use it, it just hasn't reached my "learn-priority" yet – r2evans Aug 19 '16 at 23:09
  • I would recommend the simplest answer that doesn't rely on external packages as the solution. Looks like this one. This one or the one by @ZheyuanLi – Hack-R Aug 19 '16 at 23:13
3

If you want to use a loop 'cause you like using loops, you can do this:

nums = c(1,0,0,1,1,0,0,1,1,1,1,1)

runningCount = 0
for(i in 1:length(nums)){
   if(nums[i] == 1){ 
      runningCount = runningCount + 1
      nums[i] = runningCount
   } else {
      runningCount = 0
   }
}

nums
 [1] 1 0 0 1 2 0 0 1 2 3 4 5
cgage1
  • 579
  • 5
  • 15
3

I'm trying to add up values in a vector since a zero

Maybe

k <- rle(as.logical(x))$lengths
ave(x, rep.int(1:length(k), k), FUN = cumsum)

Note, I am taking a more general setting, where your x is not just 0 /1 binary. But as.logical(x) will give a TRUE / FALSE binary result, indicating chunks of zeros (FALSE) and non-zeros (TRUE). If your x is surely 0 / 1 binary, just using

k <- rle(x)$lengths
ave(x, rep.int(1:length(k), k), FUN = cumsum)

is legitimate.


Example

set.seed(0); x <- sample(0:3, 20, TRUE)
# [1] 3 1 1 2 3 0 3 3 2 2 0 0 0 2 1 3 1 2 3 1

k <- rle(as.logical(x))$lengths    ## chunk size
# [1] 5 1 4 3 7

ave(x, rep.int(1:length(k), k), FUN = cumsum)    ## cumulative sum each chunk
# [1]  3  4  5  7 10  0  3  6  8 10  0  0  0  2  3  6  7  9 12 13
Zheyuan Li
  • 71,365
  • 17
  • 180
  • 248
3

I may win the award for longest answer on this one.

Well, at least I didn't call any packages and stuck to basic functions (FWIW).

vec <-  c(1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1); res  <- vec

for(i in 2:length(vec)){m <- 1
  while(vec[i-m] > 0 & vec[i]>0){m <- m+1}
      res[i] <- m; if(vec[i]==0) res[i] <- 0}

[1] 1 0 0 1 2 0 0 1 2 3 4 5

Hack-R
  • 22,422
  • 14
  • 75
  • 131
1

Here is another approach with diff and ave

ave(wdnew, cumsum(c(TRUE, diff(wdnew == 1)!=0)), FUN = seq) * wdnew
#[1] 1 0 0 1 2 0 0 1 2 3 4 5

data

wdnew <- c(1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1)
akrun
  • 874,273
  • 37
  • 540
  • 662