if-else, ifelse, and switch timings

Comparison of the speed of if-else, ifelse(), and switch() statements, as well as indexing with [.

ifelse() and [-indexing are vectorized, so they can take vectors as inputs, but that isn't used here because the main purpose of these tests is to compare against if-else and switch() statements.

# ========================
library(microbenchmark)
library(plyr)

# Test functions
if2 <- function(dat) {
  for (x in dat) {
    if (x == "a") 1
    else 2
  }
}
if3 <- function(dat) {
  for (x in dat) {
    if (x == "a") 10
    else if (x == "b") 20
    else 30
  }
}
if8 <- function(dat) {
  for (x in dat) {
    if (x == "a") 10
    else if (x == "b") 20
    else if (x == "c") 30
    else if (x == "d") 40
    else if (x == "e") 50
    else if (x == "f") 60
    else if (x == "g") 70
    else 80
  }
}
ifelse2 <- function(dat) {
  for (x in dat) {
    ifelse(x == "a", 10, 20)
  }
}
switch2 <- function(dat) {
  for (x in dat) switch(x, a=1, 2)
}
switch3 <- function(dat) {
  for (x in dat) switch(x, a=1, b=2, 3)
}
switch8 <- function(dat) {
  for (x in dat) switch(x, a=1, b=2, c=3, d=4, e=5, f=6, g=7, 8)
}
# These two functions aren't exactly the same as the above: there's no default
# case. Instead there's an `h` option. These are here for comparison.
index_dyn8 <- function(dat) {
  # Create the index vector inside the loop
  for (x in dat) c(a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8)[x]
}
index_saved8 <- function(dat) {
  # Create the index vector outside the loop
  idx <- c(a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8)
  for (x in dat) idx[x]
}


dat <- rep(letters[1:8], 1000)
num <- rep(1:8, 1000)
res <- microbenchmark(
  if2(dat), if3(dat), if8(dat),
  switch2(dat), switch3(dat), switch8(dat),
  ifelse2(dat),
  index_dyn8(dat),
  index_dyn8(num),
  index_saved8(dat),
  index_saved8(num),
  times = 20
)


# Summarize, convert to ms
res_summary <- ddply(res, c("expr"), summarise,
    N=length(time)/1e6, m_time=mean(time)/1e6, sd=sd(time)/1e6, se=sd/sqrt(N))

# Mark those above the mean as outliers (we'll remove them for a later plot)
res_summary$outlier <-  "Outlier"
res_summary$outlier[res_summary$m_time < mean(res_summary$m_time)] <- "Not outlier"

# Plot
library(ggplot2)
ggplot(res_summary, aes(x=expr, y=m_time, fill=outlier)) +
  geom_bar(stat="identity", colour="black") +
  scale_fill_manual(values = c("black", "#990000")) +
  ylab("mean time (ms)") +
  theme(axis.text.x = element_text(angle=70, hjust=1, size=12))

plot of chunk unnamed-chunk-1


# Again, but without the outliers
res_summary2 <- subset(res_summary, m_time < mean(m_time))
ggplot(res_summary2, aes(x=expr, y=m_time)) + geom_bar(stat="identity") +
  ylab("mean time (ms)") +
  theme(axis.text.x = element_text(angle=70, hjust=1, size=12))

plot of chunk unnamed-chunk-1