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
if
statements andswitch()
calls - Run different code depending on the input
- like
- Iterations (= loops):
- like
for
andwhile
- Repeatedly run code, typically with changing options.
- like
Conditionals
Conditionals: If-Then-Else Statement
- The syntax of if-then-else statement is
Example:
= 42
x
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"
<- c(42, 179, 89) # x is a vector
x
# 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 > 1
Use Vectorized if statement ifelse
ifelse()
is a vectorized function withtest
,yes
, andno
vectors (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
= 22
m = 0
n
# series of if-else if statements
if (m < 20) {
= 20 # will run if m < 20
n else if (m < 40) {
} = 40 # will run if 20 <= m < 40
n else if (m < 60) {
} = 60 # will run if 40 <= m < 60
n else {
} = Inf # will run if 60 <= m
n
}
# check "result"
n#> [1] 40
Example: Place the if-else if statement within a function
# setup data
= 22
m
# Create find.n() function
<- function(m){
find.n 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] 40
Use 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
if
andelse if
<- "Tuesday" # Change this value!
day
if (day == 'Sunday') {
<- 1
num_day else if (day == "Monday") {
} <- 2
num_day else if (day == "Tuesday") {
} <- 3
num_day else if (day == "Wednesday") {
} <- 4
num_day else if (day == "Thursday") {
} <- 5
num_day else if (day == "Friday") {
} <- 6
num_day else if (day == "Saturday") {
} <- 7
num_day
}
num_day#> [1] 3
<- "Tuesday" # Change this value!
day
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] 3
Place switch()
within a function
# Create function legs()
<- function(x) {
legs 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 input
Iterations
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] 5
Example 2:
- It’s recommended to use
seq_along
and iterate over indexes rather than elements of a vector.
The following four chunk of code are equivalent:
# Recommended
<- 2
value <- c('one', 'two', 'three', 'four') # Character Vector
times
for (i in seq_along(times)) { # seq_along(times) gives c(1, 2, 3, 4)
<- value * 2
value print(value)
}#> [1] 4
#> [1] 8
#> [1] 16
#> [1] 32
<- 2
value <- c('one', 'two', 'three', 'four')
times
for (i in 1:length(times)) { # 1:length(times) gives c(1, 2, 3, 4)
<- value * 2
value print(value)
}#> [1] 4
#> [1] 8
#> [1] 16
#> [1] 32
<- 2
value <- c('one', 'two', 'three', 'four')
times
for (i in times) { # Works, but not recommended
<- value * 2
value print(value)
}#> [1] 4
#> [1] 8
#> [1] 16
#> [1] 32
<- 2
value <- c(100, 200, 500, 800)
times
for (i in times) { # Works, but not recommended
<- value * 2
value print(value)
}#> [1] 4
#> [1] 8
#> [1] 16
#> [1] 32
Example 3:
The iterator i changes along with the “iterations vector”
# Recommended
= 0
i = letters[4:1]
y <- c('one', 'two', 'three', 'four') # Character Vector
times
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] 4
= 0
i = letters[4:1]
y <- c('one', 'two', 'three', 'four')
times
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] 4
= 0
i = letters[4:1]
y <- c('one', 'two', 'three', 'four')
times
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"
= 0
i = letters[4:1]
y <- c(100, 200, 500, 800)
times
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] 800
Example 4 (using cat() instead of print()):
The iterator i changes along with the “iterations vector”
# Recommended
= 0
i = letters[4:1]
y <- c('one', 'two', 'three', 'four') # Character Vector
times
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] 4
= 0
i = letters[4:1]
y <- c('one', 'two', 'three', 'four')
times
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] 4
= 0
i = letters[4:1]
y <- c('one', 'two', 'three', 'four')
times
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"
= 0
i = letters[4:1]
y <- c(100, 200, 500, 800)
times
for (i in times) {
cat(i, "\n")
cat(y[i], "\n") # doesn't work
}#> 100
#> NA
#> 200
#> NA
#> 500
#> NA
#> 800
#> NA
i#> [1] 800
Next 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] 6
Vectorized Alternative Solutions to For Loops
- In R, avoid using for loops unless you truly need them.
Example 1:
= 1:10
x
for (i in seq_along(x)) { # Equivalently, for (i in 1:length(x))
= i^2
x[i]
}
x#> [1] 1 4 9 16 25 36 49 64 81 100
However, using vectorization is way better in this case.
= 1:10
x ^2
x#> [1] 1 4 9 16 25 36 49 64 81 100
Example 2:
# Recore find.n() function
<- function(m){
find.n 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
<- c(42, 37, 8, 72)
m
# check "result"
find.n(m)
#> Error in if (m < 20) {: the condition has length > 1
You may use for loop for this example (but not recommended).
<- numeric()
n
### For loop in this case is NOT recommended
for (i in seq_along(m)){
<- find.n(m[i])
n[i]
}
n#> [1] 60 40 20 Inf
However, 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 Inf
Apply Functions (lapply()
, sapply()
)
The lapply
general syntax is
lapply(X = some_list, FUN = f)
some_list
is a vector (atomic vector or list)- The function
f
will be “applied” to each element ofsome_list
. lapply
is returning a list.
Example:
# Create a list
set.seed(42)
= list(a = runif(5),
ex_list 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()
, wheres
refers to the simplifying action taken by the function.
sapply(ex_list, max)
#> a b c
#> 0.9370754 0.7365883 0.9346722
sapply()
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
= 1000
amount0 = rnorm(n = 10, mean = 0.10, sd = 0.18)
rates
# output vector (to be populated)
= c(amount0, double(length = 10)) ### preallocate the output container
amounts
= 1:10
year
# for loop
for (s in seq_along(year)) { ### Use seq_along(year) instead of 1:length(year) is recommended
+1] = amounts[s] * (1 + rates[s])
amounts[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.9926
Recommendations 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.