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

      

+3


source to share


3 answers


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

      

+4


source


Use rollapply

from the zoo :



library("zoo")
rollapply(x, 3, min)
# [1] -1 -5 -5 -5 15

      

+4


source


You can also use runmin

fromcaTools

library(caTools)
runmin(x,3,endrule='trim')
#[1] -1 -5 -5 -5 15

      

+2


source







All Articles