How do I find the min of the previous n values for each element of the vector?
So far, I've used loops inside a function like this:
# x is a vector of numbers
# [1] 0 1 -1 -5 100 20 15
function(x,n){
results <- numeric(length(x)-n+1)
for(i in 1:(length(x)+1-n)){
results[i] <- min(x[i:(i+n-1)])
}
return(results)
}
## outputs this for x and n = 3
# [1] -1 -5 -5 -5 15
I was wondering if there is a more efficient solution that does not potentially require a loop.
EDIT ::
I ran two microbenchmark solutions on a vector with 6019 observations. When I get the time (/ figure out how), I can try each solution with different observation sizes to see the effectiveness of each solution. But for now:
Rcpp Solution:
> microbenchmark(nmin(x,3))
Unit: microseconds
expr min lq mean median uq max neval
nmin(x, 3) 53.885 54.313 57.01953 54.7405 56.023 93.656 100
CaTools solution:
microbenchmark(runmin(x[[1]],3,endrule='trim'))
Unit: microseconds
expr min lq mean median uq max neval
runmin(x[[1]], 3, endrule = "trim") 231.788 241.8385 262.6348 249.964 262.5795 833.923 100
zoo Solution:
> microbenchmark(rollapply(x[[1]],3,min))
Unit: milliseconds
expr min lq mean median uq max neval
rollapply(x[[1]], 3, min) 42.2123 47.2926 50.40772 50.33941 52.50033 98.46828 100
My decision:
> microbenchmark(nDayLow(x[[1]],3))
Unit: milliseconds
expr min lq mean median uq max neval
nDayLow(x[[1]], 3) 13.64597 14.51581 15.67343 15.33006 15.71324 63.68687 100
source to share
Sounds like a good use case for Rcpp. Copy-paste this and use it like any other function. I'm sure there are many ways to make this even more efficient (I mean, I'm not very good with C ++ and I'm sure you can use some pretty STLs here):
require(Rcpp)
Rcpp::cppFunction( 'IntegerVector nmin( NumericVector x , int n ){
int N = x.size();
IntegerVector out(N-n+1);
for( int i = 0; i < out.size(); ++i){
int nmin=x[i];
for( int j = 0; j < n; ++j){
int tmp=x[j+i];
if( tmp < nmin ){
nmin=tmp;
}
}
out[i]=nmin;
}
return out;
}')
nmin(x,3)
#[1] -1 -5 -5 -5 15
nmin(x,7)
#[1] -5
This is about 30 times faster runmin
:
print( microbenchmark(runmin(x,3,endrule='trim'),nmin(x,3),unit="relative") , digits = 1 )
#Unit: relative
# expr min lq median uq max neval
# runmin(x, 3, endrule = "trim") 55 41 36 34 19 100
# nmin(x, 3) 1 1 1 1 1 100
source to share