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()
-loopWe 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
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.