Antti Antti - 3 months ago 20
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.

Answer

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

Add session info for comparison:

sessionInfo()
R version 3.3.0 (2016-05-03)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)