0

If I have the following vector:

x = c(1,1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,1,1,1,1,0,0,0,0,1,1,1)

how can I calculate the cumulative sum for all of the consecutive 1's, resetting each time I hit a 0?

So, the desired output would look like this:

> y
[1] 1 2 3 0 0 0 0 1 2 0 0 1 2 3 0 0 1 2 3 4 0 0 0 0 1 2 3
Nigel Stackhouse
  • 354
  • 2
  • 15
  • Today someone bumped an older version of this question. Marking as dupe. (Though I have to say I think your title is more descriptive.) – Gregor Thomas Apr 07 '16 at 19:44

3 Answers3

4

This works:

unlist(lapply(rle(x)$lengths, FUN = function(z) 1:z)) * x
# [1] 1 2 3 0 0 0 0 1 2 0 0 1 2 3 0 0 1 2 3 4 0 0 0 0 1 2 3

It relies pretty heavily on your special case of only having 1s and 0s, but for that case it works great! Even better, with @nicola's suggested improvements:

sequence(rle(x)$lengths) * x
# [1] 1 2 3 0 0 0 0 1 2 0 0 1 2 3 0 0 1 2 3 4 0 0 0 0 1 2 3
Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
1

I read this post about how to split a vector, and use splitAt2 by @Calimo.

So it's like this:

splitAt2 <- function(x, pos) {
        out <- list()
        pos2 <- c(1, pos, length(x)+1)
        for (i in seq_along(pos2[-1])) {
                out[[i]] <- x[pos2[i]:(pos2[i+1]-1)]
        }
        return(out)
}

x = c(1,1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,1,1,1,1,0,0,0,0,1,1,1)

where_split = which(x == 0)

x_split = splitAt2(x, where_split)

unlist(sapply(x_split, cumsum))
# [1] 1 2 3 0 0 0 0 1 2 0 0 1 2 3 0 0 1 2 3 4 0 0 0 0 1 2 3
Community
  • 1
  • 1
YJZ
  • 3,934
  • 11
  • 43
  • 67
1

Here is another option

library(data.table)
ave(x, rleid(x), FUN=seq_along)*x
#[1] 1 2 3 0 0 0 0 1 2 0 0 1 2 3 0 0 1 2 3 4 0 0 0 0 1 2 3

Or without any packages

ave(x, cumsum(c(TRUE, x[-1]!= x[-length(x)])), FUN=seq_along)*x
#[1] 1 2 3 0 0 0 0 1 2 0 0 1 2 3 0 0 1 2 3 4 0 0 0 0 1 2 3
akrun
  • 874,273
  • 37
  • 540
  • 662