Reputation: 1467
How can I do a cumulative sum over a vector (like cumsum
), but bounded so that the summation never goes below a lower bound or above an upper bound?
The standard cumsum function would result in the following.
foo <- c(100, -200, 400, 200)
cumsum(foo)
# [1] 100 -100 300 500
I am looking for something as efficient as the base cumsum
function. I would expect the output to look like the following.
cumsum.bounded(foo, lower.bound = 0, upper.bound = 500)
# [1] 100 0 400 500
Thanks
Upvotes: 11
Views: 1571
Reputation: 1467
I suppose this might work.
library ("Rcpp")
cumsum.bounded <- cppFunction(
'NumericVector cumsum_bounded (NumericVector x, const double lower, const double upper) {
double acc = 0;
NumericVector result(x.size());
for(int i = 0; i < x.size(); i++) {
acc += x[i];
if (acc < lower) acc = lower;
if (acc > upper) acc = upper;
result[i] = acc;
}
return result;
}')
Upvotes: 6
Reputation: 270170
Here are a couple of pure R versions. Not likely to be as fast as going to C/C++ but one of them might be fast enough for your needs and would be easier to maintain:
# 1 Reduce
cumsum.bounded <- function(x, lower.bound = 0, upper.bound = 500) {
bsum <- function(x, y) min(upper.bound, max(lower.bound, x+y))
if (length(x) > 1) Reduce(bsum, x, acc = TRUE) else x
}
# 2 for loop
cumsum.bounded2 <- function(x, lower.bound = 0, upper.bound = 500) {
if (length(x) > 1)
for(i in 2:length(x)) x[i] <- min(upper.bound, max(lower.bound, x[i] + x[i-1]))
x
}
These may be need to be enhanced slightly if x
has length 0 or 1 depending on how strict the requirements are.
Upvotes: 3
Reputation: 44340
As mentioned in the comments, Rcpp
is a good way to go.
cumsumBounded.cpp
:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector cumsumBounded(NumericVector x, double low, double high) {
NumericVector res(x.size());
double acc = 0;
for (int i=0; i < x.size(); ++i) {
acc += x[i];
if (acc < low) acc = low;
else if (acc > high) acc = high;
res[i] = acc;
}
return res;
}
Compile and use new function:
library(Rcpp)
sourceCpp(file="cumsumBounded.cpp")
foo <- c(100, -200, 400, 200)
cumsumBounded(foo, 0, 500)
# [1] 100 0 400 500
Upvotes: 14