if (condition) {
# do something
} else {
# do something else
}Conditions and For Loop
Reading Assignments
Make sure to read the following sections in the textbook: R Coding Basics, https://www.gastonsanchez.com/R-coding-basics/
Conditionals and Iterations
There are two primary tools of control flow: conditionals and iterations.
- Conditionals (= choices):
- like
ifstatements andswitch()calls - Run different code depending on the input
- like
- Iterations (= loops):
- like
forandwhile - Repeatedly run code, typically with changing options.
- like
Conditionals
Conditionals: If-Then-Else Statement
- The syntax of if-then-else statement is
Example:
x = 42
if (x > 100) {
print("x is greater than 100")
} else {
print("x is less than or equal to 100")
}
#> [1] "x is less than or equal to 100"x <- c(42, 179, 89) # x is a vector
# The codes below can't run
if (x > 100) {
print("x is greater than 100")
} else {
print("x is less than or equal to 100")
}
#> Error in if (x > 100) {: the condition has length > 1Use Vectorized if statement ifelse
ifelse()is a vectorized function withtest,yes, andnovectors (that will be recycled to the same length).
ifelse(x > 100, "x is greater than 100", "x is less than or equal to 100")
#> [1] "x is less than or equal to 100" "x is greater than 100"
#> [3] "x is less than or equal to 100"Conditionals: Chain Multiple If-Else Statements
- The syntax of multiple if’s statement is
if (condition) {
# run this code if the condition evaluates to TRUE
} else if (condition){
# run this code if the condition evaluates to FALSE
} else {
}Example:
# setup data
m = 22
n = 0
# series of if-else if statements
if (m < 20) {
n = 20 # will run if m < 20
} else if (m < 40) {
n = 40 # will run if 20 <= m < 40
} else if (m < 60) {
n = 60 # will run if 40 <= m < 60
} else {
n = Inf # will run if 60 <= m
}
# check "result"
n
#> [1] 40Example: Place the if-else if statement within a function
# setup data
m = 22
# Create find.n() function
find.n <- function(m){
if (m < 20) {
return(20) # will run if m < 20
} else if (m < 40) {
return(40) # will run if 20 <= m < 40
} else if (m < 60) {
return(60) # will run if 40 <= m < 60
} else {
return(Inf) # will run if 60 <= m
}
}
# Apply find.n() on m = 22
find.n(m)
#> [1] 40Use switch() statement
switch()is closely related toif.switch()is a compact, special purpose equivalent.- Limitations: It is recommended to use
switch()only with character inputs.- It is also possible to use
switch()with a numeric x, but is harder to read, and has undesirable failure modes if x is not a whole number.
- It is also possible to use
Example: Convert the day of the week into a number.
- Use
ifandelse if
day <- "Tuesday" # Change this value!
if (day == 'Sunday') {
num_day <- 1
} else if (day == "Monday") {
num_day <- 2
} else if (day == "Tuesday") {
num_day <- 3
} else if (day == "Wednesday") {
num_day <- 4
} else if (day == "Thursday") {
num_day <- 5
} else if (day == "Friday") {
num_day <- 6
} else if (day == "Saturday") {
num_day <- 7
}
num_day
#> [1] 3day <- "Tuesday" # Change this value!
switch(day, # The expression to be evaluated.
Sunday = 1,
Monday = 2,
Tuesday = 3,
Wednesday = 4,
Thursday = 5,
Friday = 6,
Saturday = 7,
NA) # an (optional) default value if there are no matches
#> [1] 3Place switch() within a function
# Create function legs()
legs <- function(x) {
switch(x,
cow = ,
horse = ,
dog = 4,
human = ,
chicken = 2,
plant = 0,
stop("Unknown input")
)
}- If multiple inputs have the same output, we can leave the right hand side of = empty and the input will “fall through” to the next value.
legs("cow")
#> [1] 4
legs("dog")
#> [1] 4
legs("bear")
#> Error in legs("bear"): Unknown inputIterations
For Loop
- For loops are used when you know how many times a series of calculations need to be repeated.
The generic syntax of for loop is:
for (iterator in times) {
do_something
}Example 1:
for (i in 1:5){ # i, j, k are commonly used for the iterator
print(i)
}
#> [1] 1
#> [1] 2
#> [1] 3
#> [1] 4
#> [1] 5# Equivalent, but not recommended
for (some_index in 1:5){
print(some_index)
}
#> [1] 1
#> [1] 2
#> [1] 3
#> [1] 4
#> [1] 5Example 2:
- It’s recommended to use
seq_alongand iterate over indexes rather than elements of a vector.
The following four chunk of code are equivalent:
# Recommended
value <- 2
times <- c('one', 'two', 'three', 'four') # Character Vector
for (i in seq_along(times)) { # seq_along(times) gives c(1, 2, 3, 4)
value <- value * 2
print(value)
}
#> [1] 4
#> [1] 8
#> [1] 16
#> [1] 32value <- 2
times <- c('one', 'two', 'three', 'four')
for (i in 1:length(times)) { # 1:length(times) gives c(1, 2, 3, 4)
value <- value * 2
print(value)
}
#> [1] 4
#> [1] 8
#> [1] 16
#> [1] 32value <- 2
times <- c('one', 'two', 'three', 'four')
for (i in times) { # Works, but not recommended
value <- value * 2
print(value)
}
#> [1] 4
#> [1] 8
#> [1] 16
#> [1] 32value <- 2
times <- c(100, 200, 500, 800)
for (i in times) { # Works, but not recommended
value <- value * 2
print(value)
}
#> [1] 4
#> [1] 8
#> [1] 16
#> [1] 32Example 3:
The iterator i changes along with the “iterations vector”
# Recommended
i = 0
y = letters[4:1]
times <- c('one', 'two', 'three', 'four') # Character Vector
for (i in seq_along(times)) { # seq_along(times) gives c(1, 2, 3, 4)
print(i)
print(y[i])
}
#> [1] 1
#> [1] "d"
#> [1] 2
#> [1] "c"
#> [1] 3
#> [1] "b"
#> [1] 4
#> [1] "a"
i
#> [1] 4i = 0
y = letters[4:1]
times <- c('one', 'two', 'three', 'four')
for (i in 1:length(times)) { # 1:length(times) gives c(1, 2, 3, 4)
print(i)
print(y[i])
}
#> [1] 1
#> [1] "d"
#> [1] 2
#> [1] "c"
#> [1] 3
#> [1] "b"
#> [1] 4
#> [1] "a"
i
#> [1] 4i = 0
y = letters[4:1]
times <- c('one', 'two', 'three', 'four')
for (i in times) {
print(i)
print(y[i]) # doesn't work
}
#> [1] "one"
#> [1] NA
#> [1] "two"
#> [1] NA
#> [1] "three"
#> [1] NA
#> [1] "four"
#> [1] NA
i
#> [1] "four"i = 0
y = letters[4:1]
times <- c(100, 200, 500, 800)
for (i in times) {
print(i)
print(y[i]) # doesn't work
}
#> [1] 100
#> [1] NA
#> [1] 200
#> [1] NA
#> [1] 500
#> [1] NA
#> [1] 800
#> [1] NA
i
#> [1] 800Example 4 (using cat() instead of print()):
The iterator i changes along with the “iterations vector”
# Recommended
i = 0
y = letters[4:1]
times <- c('one', 'two', 'three', 'four') # Character Vector
for (i in seq_along(times)) { # seq_along(times) gives c(1, 2, 3, 4)
cat(i, "\n")
cat(y[i], "\n")
}
#> 1
#> d
#> 2
#> c
#> 3
#> b
#> 4
#> a
i
#> [1] 4i = 0
y = letters[4:1]
times <- c('one', 'two', 'three', 'four')
for (i in 1:length(times)) { # 1:length(times) gives c(1, 2, 3, 4)
cat(i, "\n")
cat(y[i], "\n")
}
#> 1
#> d
#> 2
#> c
#> 3
#> b
#> 4
#> a
i
#> [1] 4i = 0
y = letters[4:1]
times <- c('one', 'two', 'three', 'four')
for (i in times) {
cat(i, "\n")
cat(y[i], "\n") # doesn't work
}
#> one
#> NA
#> two
#> NA
#> three
#> NA
#> four
#> NA
i
#> [1] "four"i = 0
y = letters[4:1]
times <- c(100, 200, 500, 800)
for (i in times) {
cat(i, "\n")
cat(y[i], "\n") # doesn't work
}
#> 100
#> NA
#> 200
#> NA
#> 500
#> NA
#> 800
#> NA
i
#> [1] 800Next and Break Statements
next– skipping certain iterationsbreak– stop a loop from iterating
Example:
for (i in 1:10){
if (i == 2) {
next
}
if(i == 7){
break
}
print(i)
}
#> [1] 1
#> [1] 3
#> [1] 4
#> [1] 5
#> [1] 6Vectorized Alternative Solutions to For Loops
- In R, avoid using for loops unless you truly need them.
Example 1:
x = 1:10
for (i in seq_along(x)) { # Equivalently, for (i in 1:length(x))
x[i] = i^2
}
x
#> [1] 1 4 9 16 25 36 49 64 81 100However, using vectorization is way better in this case.
x = 1:10
x^2
#> [1] 1 4 9 16 25 36 49 64 81 100Example 2:
# Recore find.n() function
find.n <- function(m){
if (m < 20) {
return(20) # will run if m < 20
} else if (m < 40) {
return(40) # will run if 20 <= m < 40
} else if (m < 60) {
return(60) # will run if 40 <= m < 60
} else {
return(Inf) # will run if 60 <= m
}
}
# Now m is a vector
m <- c(42, 37, 8, 72)
# check "result"
find.n(m)
#> Error in if (m < 20) {: the condition has length > 1You may use for loop for this example (but not recommended).
n <- numeric()
### For loop in this case is NOT recommended
for (i in seq_along(m)){
n[i] <- find.n(m[i])
}
n
#> [1] 60 40 20 InfHowever, it’s better to use lapply() or sapply() in this case.
lapply(m, find.n)
#> [[1]]
#> [1] 60
#>
#> [[2]]
#> [1] 40
#>
#> [[3]]
#> [1] 20
#>
#> [[4]]
#> [1] Inf
sapply(m, find.n)
#> [1] 60 40 20 InfApply Functions (lapply(), sapply())
The lapply general syntax is
lapply(X = some_list, FUN = f)some_listis a vector (atomic vector or list)- The function
fwill be “applied” to each element ofsome_list. lapplyis returning a list.
Example:
# Create a list
set.seed(42)
ex_list = list(a = runif(5),
b = runif(5),
c = runif(5))
ex_list
#> $a
#> [1] 0.9148060 0.9370754 0.2861395 0.8304476 0.6417455
#>
#> $b
#> [1] 0.5190959 0.7365883 0.1346666 0.6569923 0.7050648
#>
#> $c
#> [1] 0.4577418 0.7191123 0.9346722 0.2554288 0.4622928# Use lapply() on the list above
## Find range
lapply(ex_list, range)
#> $a
#> [1] 0.2861395 0.9370754
#>
#> $b
#> [1] 0.1346666 0.7365883
#>
#> $c
#> [1] 0.2554288 0.9346722
## Find maximum
lapply(ex_list, max)
#> $a
#> [1] 0.9370754
#>
#> $b
#> [1] 0.7365883
#>
#> $c
#> [1] 0.9346722- Notice that, each element of the last returning list is an atomic vector of length one, of the same type. In this case, we could use
sapply(), wheresrefers to the simplifying action taken by the function.
sapply(ex_list, max)
#> a b c
#> 0.9370754 0.7365883 0.9346722sapply() is returning a vector.
For Loop vs Apply Functions
- How to decide you should use a loop or apply function?
- Use an apply function when the results of each iteration are independent.
- Use a loop when the result of the next iteration depends on the result of the previous iteration.
Example 13.4
set.seed(345) # for replication purposes
amount0 = 1000
rates = rnorm(n = 10, mean = 0.10, sd = 0.18)
# output vector (to be populated)
amounts = c(amount0, double(length = 10)) ### preallocate the output container
year = 1:10
# for loop
for (s in seq_along(year)) { ### Use seq_along(year) instead of 1:length(year) is recommended
amounts[s+1] = amounts[s] * (1 + rates[s])
}
amounts
#> [1] 1000.0000 958.7165 1006.3527 1077.7409 1129.1412 1228.3298 1211.0918
#> [8] 1129.9604 1590.9151 2223.8740 3170.9926Recommendations when using for loop:
- If you’re generating data, make sure to preallocate the output container. Otherwise the loop will be very slow. Do not grow vectors.
- It’s recommended to use
seq_along(x), instead of1:length(x)or vectorx, as the iterations vector.