Lesson 6 of 17 · Week 03 · Thu Oct 8
Control Flow
Questions
- How can my code make choices?
- How do I repeat an operation for each value, or each group?
Objectives
- Write
if/else if/elsestatements, usingany()andall()to get a single condition. - Write
forloops over values and over groups, and store their results. - Pre-allocate a result vector instead of growing it.
- Write a
whileloop that is guaranteed to stop.
Making decisions: if / else
x <- 12
if (x > 10) {
print("big")
} else if (x > 5) {
print("medium")
} else {
print("small")
}
The condition inside if ( ) must be a single TRUE or FALSE. When
you have a whole vector of comparisons, squash it into one answer with any() (is at least one
TRUE?) or all() (are they all TRUE?). Combine conditions with &&
(and) and || (or).
any(gm$year == 1987) # TRUE: at least one row is from 1987
if (any(gm$lifeExp > 80)) print("Someone lives past 80 on average")
if can also return a value, which is handy for labels:
label <- if (x %% 2 == 0) "even" else "odd"Adapted from Software Carpentry's “Control Flow” episode (CC BY 4.0, The Carpentries). Text, examples and exercises have been rewritten for this course.
Repeating steps: for loops
for (yr in c(1967, 2007)) {
print(paste("The year is", yr))
}
The loop variable (yr) takes each value of the vector in turn. To loop over positions instead, use
seq_along(v), which is safer than 1:length(v) when v might be empty.
Saving results from a loop. You can grow a vector with out <- c(out, new_value), but
that gets slow for long loops. It's better to make the output first and fill it by position:
squares <- numeric(5) # five zeros, ready to be filled
for (i in 1:5) {
squares[i] <- i ^ 2
}
With a nested loop you have to work out each result's position yourself, e.g.
(i - 1) * n_inner + j.
A preview: picking values with a condition
Loops over groups need one trick that the next lesson covers fully: v[condition] keeps only the elements of
v where the condition is TRUE.
gm$lifeExp[gm$continent == "Europe"] # Europe's life expectancies
for (cont in levels(gm$continent)) {
m <- mean(gm$lifeExp[gm$continent == cont])
print(paste(cont, round(m, 1)))
}
To store one result per group, use a named vector: res <- character(0) before the loop and
res[cont] <- "something" inside it. startsWith(as.character(gm$country), "B") tests
whether names start with a letter.
while loops
balance <- 1000
years <- 0
while (balance < 2000) {
balance <- balance * 1.07
years <- years + 1
}
years
A while loop runs as long as its condition stays TRUE. Make sure something inside it
eventually makes the condition FALSE, or it runs forever (press Esc in Positron's console to stop it).
Loops are fine, but in R many loops can be replaced by one vectorized operation. That's the subject of Week 04.
Exercises
Each exercise gives you up to 10 checked attempts. Use Run as many times as you like to experiment first, then click Check answer when you're ready. Your exercises use numbers/subsets unique to you, so answers won't match a classmate's exactly.
Exercise 1 — Is that year in the data?
0 / 10 attempts usedClick Run to see two years, y1 and y2. For each one,
write an if/else that sets a variable (report1 for y1,
report2 for y2) to "found" if gm has any rows from that year and to
"missing" otherwise. Use any(), and use the variables, not typed-in years.
Exercise 2 — Grow vs. fill
0 / 10 attempts usedClick Run to see how grown was built by a nested loop that grows a vector
(your loop sizes n_outer and n_inner differ from your classmates'). Build the same
values in filled: create it at full length first with numeric(), then fill it by position
inside the same nested loop.
Exercise 3 — Continents above or below
0 / 10 attempts usedClick Run to see your cutoff. Write a for loop over the continents
(levels(gm$continent)) that computes each continent's mean lifeExp (all years together) and stores
"above" or "below" the cutoff in a named character vector verdict, e.g.
verdict["Asia"].
Exercise 4 — Three bands per country
0 / 10 attempts usedLoop over every country in gm and classify its 2007 life expectancy as
"low" (under lo), "mid" (from lo up to, but not including,
hi) or "high" (hi or more). Store the results in a named character vector
band, one element per country. Click Run for your lo and hi.
Exercise 5 — Advanced: flag some countries
0 / 10 attempts usedLoop over the countries whose names start with letter (click Run to see it; use
startsWith()). Collect in a character vector flagged the names of those whose mean
life expectancy over all four years is below limit.
Extra, on your computer: for each flagged country, draw life expectancy against year with
plot(..., type = "l").
Key Points
ifneeds a singleTRUE/FALSE; useany()/all()to get one from a vector.for (x in v)runs the body once per element ofv.- Create the output first (
numeric(n),character(0)) and fill it inside the loop. - Loop over
levels()orunique()to do something per group.
Proof of completion
Once you've attempted every exercise above, download a PDF summary of your work on this lesson — your name, the exercises you passed, attempt counts, timing, and your final code for each.