ifelse() vs if() ... else ...

Let’s see what happens when we use the ifelse() example from the slides with if() ... else ...

x <- rnorm(10)
ifelse(x < 0, "<0", ">0")
##  [1] "<0" "<0" "<0" ">0" ">0" "<0" "<0" "<0" ">0" "<0"
if (x < 0) {
  "<0"
} else {
  ">0"
}
## Warning in if (x < 0) {: the condition has length > 1 and only the first element will be used
## [1] "<0"

The problem here is that if() expects an expression that either returns TRUE or FALSE, but we gave it a vector:

x < 0
##  [1]  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE

In this case, only the first element of this vector is used.

On the other hand, the if () ... else ... example from the slides works in both cases:

if (length(x) > 5) {
  mean(x)
} else {
  x
}
## [1] -0.008715537
ifelse(length(x) > 5, mean(x), x)
## [1] -0.008715537

But for other cases it may not do what we expect it to do:

x <- list(a = 5, b = rnorm(7), c = rnorm(4))
x
## $a
## [1] 5
## 
## $b
## [1]  0.25331851 -0.02854676 -0.04287046  1.36860228 -0.22577099  1.51647060 -1.54875280
## 
## $c
## [1] 0.5846137 0.1238542 0.2159416 0.3796395
ifelse(length(x) > 5, mean(x), x)
## [[1]]
## [1] 5

Here, the “test” length(x) > 5 results in FALSE, but because ifelse() expects a vector of “tests” (and only receives one “test”), it will only return the first element of x.

But if() ... else ... also does not work per element:

if (length(x) > 5) {
  mean(x)
} else {
  x
}
## $a
## [1] 5
## 
## $b
## [1]  0.25331851 -0.02854676 -0.04287046  1.36860228 -0.22577099  1.51647060 -1.54875280
## 
## $c
## [1] 0.5846137 0.1238542 0.2159416 0.3796395

for()-loop

We could solve the above issue using a for()-loop. This allows us to look at each element of x separately. Here, the function seq_along() comes in handy:

seq_along(x)
## [1] 1 2 3

It produces a sequence with the same length as x has elements. Because we are using a loop, we need to add the print() function to see the printed output.

for (i in seq_along(x)) {
  
  if (length(x[[i]]) > 5) {
    print(mean(x[[i]]))
  } else {
    print(x[[i]])
  }

}
## [1] 5
## [1] 0.1846358
## [1] 0.5846137 0.1238542 0.2159416 0.3796395

We could also “collect” the output in a new object:

output <- list()

for (i in seq_along(x)) {
  
  output[[i]] <- if (length(x[[i]]) > 5) {
    mean(x[[i]])
  } else {
    x[[i]]
  }
  
}

output
## [[1]]
## [1] 5
## 
## [[2]]
## [1] 0.1846358
## 
## [[3]]
## [1] 0.5846137 0.1238542 0.2159416 0.3796395

An example with while()

while() requires a condition that at some point is FALSE for the loop to stop.

The following would run forever (stop it by pressing “Esc” when in the Console):

i <- 0
while (i < 5) {
  print(i)
}

This one here could also take very long. Instead of printing the output we save it in a vector that we can plot afterwards.

x <- 0
xvec <- c()

while (x < 5) {
  x <- x + rnorm(1, 0, 1)
  xvec <- c(xvec, x)
}

length(xvec)
## [1] 37
plot(xvec, type = 'l')

We could extend the syntax from above by adding a counter

x <- i <- 0
xvec <- c()

while (x < 5 & i < 25) {
  x <- x + rnorm(1, 0, 1)
  xvec <- c(xvec, x)
  i <- i + 1
}
xvec
##  [1] -0.2357004 -1.2621213 -1.9725278 -1.7156441 -1.9623360 -2.3098786 -3.2614972 -3.3065249 -4.0914294 -5.7593713
## [11] -6.1395978 -5.2206012 -5.7959482 -5.1879838 -6.8058665 -6.8614285 -6.3420213 -6.0408679 -5.9351918 -6.5758978
## [21] -7.4256021 -8.4497309 -8.3320843 -9.2795589 -9.7701164

Now we will get a maximum of 25 iterations. The loop stops as soon as x is larger than or equal to 5, or if 25 iterations are reached.