tags:

views:

73

answers:

2

the function I am writing specifies the behaviour of a physical switch: it should be turned ON if a value goes above an upper threshold and can go again OFF if it goes under the lower threshold. a similar logic would describe a normal thermostat in a household oven. obviously I want it to work on vectors, that's the whole point!

so if I have the data

S <- c(50, 100, 150, 180, 210, 200, 190, 182, 175, 185, 195, 205)

my function tells if the oven temperature is all right. the logical inverse of "switch the oven on".

R> thresholdOnOff(S, 180, 200)
 [1] FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE  TRUE

the question is about programming style: I first tried to write it with an 'apply' function in it but I had forgotten to take environments into account... so I wrote a working version with a 'for' loop - which I didn't like, then remembered about the environments and I'm not sure about the two versions:

thresholdOnOff <- local( {
  ## following the R inferno
  f <- function(series, lower, upper, initialValue=FALSE) {
    status <<- initialValue

    switchOnOff <- function(x) {
      if(x > upper)
        status <<- TRUE
      if(x < lower)
        status <<- FALSE
      return(status)
    }

    sapply(series, switchOnOff)
  }
} )


thresholdOnOff <- function(series, lower, upper, initialValue=FALSE) {
  ## just guessing and reading from the documentation
  status <- initialValue

  switchOnOff <- function(x) {
    if(x > upper)
      assign('status', TRUE, inherits=TRUE)
    if(x < lower)
      assign('status', FALSE, inherits=TRUE)
    return(status)
  }

  sapply(series, switchOnOff)
}
+1  A: 

Here's one efficient solution without loops:

library("zoo")
S <- c(50, 100, 150, 180, 210, 200, 190, 182, 175, 185, 195, 205)

thresholdOnOff <- function(x, low, high, initial.value=FALSE) {
    require("zoo")
    s <- rep(NA, length(x))
    s[1] <- initial.value
    s[x > high] <- TRUE
    s[x < low] <- FALSE
    return(na.locf(s))
}

thresholdOnOff(S, 180, 200)

Alternatively, you can use ifelse in one line to solve this, but that will be much slower if you have big data:

na.locf(ifelse(S > 200, TRUE, ifelse(S < 180, FALSE, NA)))
Shane
well, you're right, I didn't *explain* what should happen between the two thresholds, I only gave the definition 'by example'... as in a physical household "oven logic", it should stay in its previous state. (S > 200 does not compute the logical vector I need). but your reply does remind me of the na.locf function and [this other question](http://stackoverflow.com/questions/1782704 "propagating data within a vector").
mariotomo
+1  A: 

Stick with a for loop. If the values are not independent then there's no advantage in using apply.

Unless you have a very good reason you should avoid side-effects in functions.

hadley