Antti - 11 months ago 68

R Question

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 Source

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)
```