Antti - 1 month ago 7x
R Question

# R: Conditional summation of a numeric vector

I have vectors that have numeric values. For example:

``````inVector <- c(2, -10, 5, 34, 7)
``````

I need to transform this so that when I encounter a negative element, that negative element gets summed with subsequent elements until the element that turns the sum positive:

``````outVector <- c(2, 0, 0, 29, 7)
``````

The negative elements will be made zeros so that the overall sum remains. So the elements 2 and 3 will be zero and the fourth element equals 29 = -10 + 5 + 34. I tried a for loop solution like this:

``````outVector <- numeric(length = length(inVector))

for(i in 1:length(inVector)) {
outVector <- inVector
outVector[i] <- ifelse(outVector[i] < 0, 0, outVector[i])
outVector[i + 1] <- ifelse(outVector[i] == 0, sum(inVector[i:(i+1)]), outVector[i + 1])
outVector <- outVector[1:length(inVector)]
}
``````

but that didn't work. However, I would be most interested of a solution that works in dplyr pipe as well.

If we want to optimize, we can use the more efficient `Reduce` function to iterate through the vector:

``````#Help function
zeroElement <- function(vec) {
r <- Reduce(function(x,y) if(x >= 0) y else sum(x,y), vec, acc=TRUE)
r[r < 0] <- 0
return(r)
}

#Use function
zeroElement(x)
#[1]  2  0  0 29  7
``````

Speed Test: 25% faster:

``````t3 <- MakeNonNeg(BigVec)
t4 <- zeroElement(BigVec)
all.equal(t3, t4)
#[1] TRUE
library(microbenchmark)
microbenchmark(
makeNonNeg = MakeNonNeg(BigVec),
zeroElement = zeroElement(BigVec),
times=10)
# Unit: seconds
#        expr      min       lq     mean   median       uq      max neval cld
#  makeNonNeg 2.047484 2.099289 2.195988 2.111135 2.248381 2.531009    10   b
# zeroElement 1.529257 1.580789 1.666000 1.664855 1.725528 1.837825    10  a
``````

``````sessionInfo()