Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R idiom for switch/case

I've got some R code that looks basically like this:

compute.quantiles <- function(mu, type) {

  ## 'mu' and 'type' are vectors of the same length

  var <- ifelse(type=='a', 6.3523 * mu^2,
         ifelse(type=='b', 234.23 * mu,
         ifelse(type=='c', {s <- 9.8 * ((mu-0.3)/3)^(6/7)+0.19; mu + mu^2/s},
         ifelse(type=='d', 56.345 * mu^1.5,
         ifelse(type=='e', 0.238986 * mu^2,
         ifelse(type=='f', mu + 1.1868823 * mu^2,
         NA ))))))

  # ...then do something with var...
}

Some sample input & output:

print(compute.quantiles(2:4, c('c','d','e')))
[1]   2.643840 292.777208   3.823776

That works correctly, but it's kind of ugly with the deep nesting, so I'm wondering if there's a different idiom that works better. Anyone have a suggestion? If switch() accepted a vector as its first argument, that would work nicely, but it just takes a scalar.

like image 826
Ken Williams Avatar asked May 07 '12 18:05

Ken Williams


2 Answers

I think I came up with something I like better:

## Vector-switch
vswitch <- function(EXPR, ...) {
    vars <- cbind(...)
    vars[cbind(seq_along(EXPR), match(EXPR, names(list(...))))]
}

compute.quantiles <- function(mu, type) {
  stopifnot(length(mu) == length(type))

  vswitch( type,
    a = 6.3523 * mu^2,
    b = 234.23 * mu,
    c = mu + mu^2/(9.8 * ((mu-0.3)/3)^(6/7)+0.19),
    d = 56.345 * mu^1.5,
    e = 0.238986 * mu^2,
    f = mu + 1.1868823 * mu^2)
}

With the matrix-indexing code in just 2 lines, I think it's ok for my too-clever-code threshold. =)

like image 144
Ken Williams Avatar answered Sep 20 '22 17:09

Ken Williams


Here is an alternative approach:

library(data.table)
# Case selection table:
dtswitch <- data.table(type=letters[1:6],
                      result=c("6.3523 * mu^2",
                               "234.23 * mu",
                               "{s <- 9.8 * ((mu-0.3)/3)^(6/7)+0.19; mu + mu^2/s}",
                               "56.345 * mu^1.5",
                               "0.238986 * mu^2",
                               "mu + 1.1868823 * mu^2"),
                      key="type")

# Data to which you want the cases applied:
compute <- data.table(type=letters[3:5],mu=2:4,key="type")

# Join the data table with the case selection table, and evaluate the results:
dtswitch[compute,list(mu,result=eval(parse(text=result)))]
#>   type mu     result
#>1:    c  2   2.643840
#>2:    d  3 292.777208
#>3:    e  4   3.823776

Rather than creating the dtswitch table in R code, you could store it in an external spreadsheet or database and then load it into R. Might be handy if you have a lot of different cases or they are changing often and you want to control them from a central location.

like image 44
dnlbrky Avatar answered Sep 19 '22 17:09

dnlbrky