Resetting Cumsum If Value Goes to Negative in R

Conditional cumsum with reset when accumulating and substracting at once

Here is one way you might do this:

ve$DO <- Reduce(function(x,y) pmax(x + y, 0), with(ve, V2-V3*(V1 >  0)), accumulate = TRUE)

ve
V1 V2 V3 DO
1 -2 6 0 6
2 4 0 5 1
3 3 0 8 0
4 -5 1 0 1
5 -5 0 0 1
6 -7 2 0 3
7 -8 3 0 6
8 -2 5 0 11
9 -3 7 0 18
10 -5 7 0 25
11 -7 8 0 33
12 -8 2 0 35
13 -9 0 0 35
14 -2 0 7 35
15 1 0 9 26
16 2 0 12 14
17 4 0 0 14

Equivalent using purrr/dplyr:

library(purrr)
library(dplyr)

ve %>%
mutate(DO = accumulate(V2-V3*(V1 > 0), .f = ~pmax(.x + .y, 0)))

cumsum with a condition to restart in R

You may use cumsum to create groups as well.

library(dplyr)

df <- df %>%
group_by(group = cumsum(dplyr::lag(port == 0, default = 0))) %>%
mutate(cumsum_G = cumsum(G)) %>%
ungroup

df

# inv ass port G group cumsum_G
# <chr> <chr> <int> <int> <dbl> <int>
#1 i x 2 1 0 1
#2 i x 2 0 0 1
#3 i x 0 1 0 2
#4 i x 3 0 1 0
#5 i x 3 1 1 1

You may remove the group column from output using %>% select(-group).

data

df <- structure(list(inv = c("i", "i", "i", "i", "i"), ass = c("x", 
"x", "x", "x", "x"), port = c(2L, 2L, 0L, 3L, 3L), G = c(1L,
0L, 1L, 0L, 1L)), class = "data.frame", row.names = c(NA, -5L))

How do I fix a cumulative sum in R when dealing with negative values

We can calculate the increments with diff and use pmax to set all negative increments to 0. Then we cumsum this corrected increment:

df %>%
group_by(player) %>%
mutate(
increment = c(first(LifeTimeCumulative), pmax(diff(LifeTimeCumulative), 0)),
corrected = cumsum(increment)
) %>%
ungroup()
# # A tibble: 12 × 6
# player date LifeTimeCumulative LifeTimeCumulativeCorrect increment corrected
# <fct> <date> <dbl> <dbl> <dbl> <dbl>
# 1 A 2022-01-01 0 0 0 0
# 2 A 2022-01-02 2 2 2 2
# 3 A 2022-01-03 0 2 0 2
# 4 A 2022-01-04 1 3 1 3
# 5 B 2022-01-01 0 0 0 0
# 6 B 2022-01-02 2 2 2 2
# 7 B 2022-01-03 2 2 0 2
# 8 B 2022-01-04 1 2 0 2
# 9 C 2022-01-01 1 1 1 1
# 10 C 2022-01-02 2 2 1 2
# 11 C 2022-01-03 2 2 0 2
# 12 C 2022-01-04 0 2 0 2

Cumulative sum with potential resets

We can use Reduce in base R

csum2 <- Reduce(function(u, v) max(u + v, v), x, accumulate = TRUE)

-checking with OP's output

identical(csum, csum2)
#[1] TRUE

Or another option is accumulate from purrr

library(purrr)
accumulate(x, ~ max(.x + .y, .y))

dplyr / R cumulative sum with reset

I think you can use accumulate() here to help. And i've also made a wrapper function to use for different thresholds

sum_reset_at <- function(thresh) {
function(x) {
accumulate(x, ~if_else(.x>=thresh, .y, .x+.y))
}
}

tib %>% mutate(c = sum_reset_at(5)(a))
# t a c
# <dbl> <dbl> <dbl>
# 1 1 2 2
# 2 2 3 5
# 3 3 1 1
# 4 4 2 3
# 5 5 2 5
# 6 6 3 3
tib %>% mutate(c = sum_reset_at(4)(a))
# t a c
# <dbl> <dbl> <dbl>
# 1 1 2 2
# 2 2 3 5
# 3 3 1 1
# 4 4 2 3
# 5 5 2 5
# 6 6 3 3
tib %>% mutate(c = sum_reset_at(6)(a))
# t a c
# <dbl> <dbl> <dbl>
# 1 1 2 2
# 2 2 3 5
# 3 3 1 6
# 4 4 2 2
# 5 5 2 4
# 6 6 3 7

Cumsum with criteria in R

A looping (and less fast) alternative to cumsum is Reduce(`+`, x, accumulate = TRUE). However, this Reduce approach can then be modified to your resetting cumulative sum:

Reduce(function(x, y) if (x + y > 0) x + y else 0, d, accumulate = TRUE)


Related Topics



Leave a reply



Submit