Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R cumsum with multiplication based on value

Tags:

r

dplyr

I have a vector of positive numbers such that - if the number is greater or equal to 1, it gets added - if the number is less than 1, it multiplies the cumulative sum.

For example

> set.seed(0)
> x <- sample(c(0.9, 0.9, 0.9, 1:3), 10, replace=T)
> x
 [1] 3.0 0.9 0.9 1.0 3.0 0.9 3.0 3.0 1.0 1.0

The resulting vector

3  2.7  2.43  3.43  6.43  5.787  8.787  11.787  12.787  13.787

Is there a way to do it without using a for loop?

like image 215
Vlad Avatar asked Mar 08 '23 23:03

Vlad


2 Answers

And here's a tidyverse alternative to @G.Grothendieck's answer using purrr::accumulate:

library(tidyverse)
accumulate(x, function(x, y) if_else(y < 1, x * y, x + y))
#>  [1]  3.000  2.700  2.430  3.430  6.430  5.787  8.787 11.787 12.787 13.787
like image 70
markdly Avatar answered Mar 11 '23 10:03

markdly


Here are some alternatives using only base R:

1) Reduce

sumprod <- function(x, y) if (y < 1) x * y else x + y
Reduce(sumprod, x, acc = TRUE)
## [1]  3.000  2.700  2.430  3.430  6.430  5.787  8.787 11.787 12.787 13.787

2) Recursion

cumsumprod_ <- function(x, init) {
      if (length(x)) c(init, Recall(x[-1], sumprod(tail(init, 1), x[1])))
      else init
}
cumsumprod <- function(x) {
      if (length(x) < 2) x
      else cumsumprod_(x[-1], x[1])
}

cumsumprod(x)
## [1]  3.000  2.700  2.430  3.430  6.430  5.787  8.787 11.787 12.787 13.787

Update: Fixed edge case in (2).

like image 42
G. Grothendieck Avatar answered Mar 11 '23 09:03

G. Grothendieck