title: Segment vector according to whether or not values are above a threshold in R
body: <p>I have a long vector and I need to divide it into segments according to a threshold. A segment is consecutive values over the threshold. When values drop below the threshold, the segment ends and the next segment begins where the values once again cross above the threshold. I need to record the start and end indices of each segment. </p>
  
  <p>Below is an inefficient implementation. What's the fastest and most appropriate way to write this? This is pretty ugly, I have to assume that there's a cleaner implementation.</p>
  
  <pre><code>set.seed(10)
  test.vec &lt;- rnorm(100, 8, 10)
  threshold &lt;- 0
  segments &lt;- list()
  in.segment &lt;- FALSE
  for(i in 1:length(test.vec)){
  
      # If we're in a segment
      if(in.segment){
          if(test.vec[i] &gt; threshold){
              next
          }else{
              end.ind &lt;- i - 1
              in.segment &lt;- FALSE
              segments[[length(segments) + 1]] &lt;- c(start.ind, end.ind)
          }
      }
  
      # if not in segment
      else{
          if(test.vec[i] &gt; threshold){        
              start.ind &lt;- i
              in.segment &lt;- TRUE
          }
      }
  }
  </code></pre>
  
  <p><strong>EDIT: Runtime of all solutions</strong></p>
  
  <p>Thanks for all the replies, this has been helpful and very instructive. A small test of all five solutions is below (the four provided plus the original example). As you can see, all four are a huge improvement over the original solution, but Khashaa's solution is by far the fastest. </p>
  
  <pre><code>set.seed(1)
  test.vec &lt;- rnorm(1e6, 8, 10);threshold &lt;- 0
  
  originalFunction &lt;- function(x, threshold){
      segments &lt;- list()
      in.segment &lt;- FALSE
      for(i in 1:length(test.vec)){
  
      # If we're in a segment
          if(in.segment){
              if(test.vec[i] &gt; threshold){
                  next
              }else{
                  end.ind &lt;- i - 1
                  in.segment &lt;- FALSE
                  segments[[length(segments) + 1]] &lt;- c(start.ind, end.ind)
              }
          }
  
      # if not in segment
          else{
              if(test.vec[i] &gt; threshold){        
                  start.ind &lt;- i
                  in.segment &lt;- TRUE
              }
          }
      }
      segments
  }
  
  SimonG &lt;- function(x, threshold){
  
    hit &lt;- which(x &gt; threshold)
    n &lt;- length(hit)
  
    ind &lt;- which(hit[-1] - hit[-n] &gt; 1)
  
    starts &lt;- c(hit[1], hit[ ind+1 ])
    ends &lt;- c(hit[ ind ], hit[n])
  
    cbind(starts,ends)
  }
  
  Rcpp::cppFunction('DataFrame Khashaa(NumericVector x, double threshold) {
    x.push_back(-1);
    int n = x.size(), startind, endind; 
    std::vector&lt;int&gt; startinds, endinds;
    bool insegment = false;
    for(int i=0; i&lt;n; i++){
      if(!insegment){
        if(x[i] &gt; threshold){        
          startind = i + 1;
          insegment = true;          }
      }else{
        if(x[i] &lt; threshold){
          endind = i;
          insegment = false;
          startinds.push_back(startind); 
          endinds.push_back(endind);
        }
      }
    }
    return DataFrame::create(_["start"]= startinds, _["end"]= endinds);
  }')
  
  bgoldst &lt;- function(x, threshold){
      with(rle(x&gt;threshold),
           t(matrix(c(0L,rep(cumsum(lengths),2L)[-length(lengths)]),2L,byrow=T)+1:0)[values,])   
  }
  
  ClausWilke &lt;- function(x, threshold){
      suppressMessages(require(dplyr, quietly = TRUE))
      in.segment &lt;- (x &gt; threshold)
      start &lt;- which(c(FALSE, in.segment) == TRUE &amp; lag(c(FALSE, in.segment) == FALSE)) - 1
      end &lt;- which(c(in.segment, FALSE) == TRUE &amp; lead(c(in.segment, FALSE) == FALSE))
      data.frame(start, end)    
  }
  
  system.time({ originalFunction(test.vec, threshold); })
   ## user  system elapsed 
   ## 66.539   1.232  67.770 
  system.time({ SimonG(test.vec, threshold); })
   ## user  system elapsed 
   ## 0.028   0.008   0.036 
  system.time({ Khashaa(test.vec, threshold); })
   ## user  system elapsed 
   ## 0.008   0.000   0.008 
  system.time({ bgoldst(test.vec, threshold); })
   ## user  system elapsed 
   ## 0.065   0.000   0.065 
  system.time({ ClausWilke(test.vec, threshold); })
   ## user  system elapsed 
   ## 0.274   0.012   0.285 
  </code></pre>