R Conditionals — if/else, switch, and Vectorized Conditionals
Learning Objectives
By the end of this tutorial, you will be able to:
- Use
if/elsefor single and chained conditions - Apply
ifelse()for vectorized conditional operations - Use
switch()for multi-way branching - Work with
case_when()for complex vectorized conditions - Understand vectorized vs scalar conditionals
if/else Statements
Basic if
x <- 10
if (x > 5) {
cat("x is greater than 5\n")
}
# x is greater than 5
if/else
x <- 3
if (x > 5) {
cat("x is greater than 5\n")
} else {
cat("x is 5 or less\n")
}
# x is 5 or less
Chained if/else if/else
score <- 85
if (score >= 90) {
grade <- "A"
} else if (score >= 80) {
grade <- "B"
} else if (score >= 70) {
grade <- "C"
} else if (score >= 60) {
grade <- "D"
} else {
grade <- "F"
}
cat("Grade:", grade, "\n")
# Grade: B
One-Liner if/else
x <- 10
result <- if (x > 5) "big" else "small"
result # [1] "big"
# Equivalent to
if (x > 5) result <- "big" else result <- "small"
ifelse() — Vectorized Conditional
ifelse() works element-wise on vectors:
x <- c(1, 5, 10, 15, 20)
ifelse(x > 10, "big", "small")
# [1] "small" "small" "small" "big" "big"
# With NA handling
x <- c(1, NA, 10, NA, 20)
ifelse(is.na(x), 0, x)
# [1] 1 0 10 0 20
# Nested ifelse
score <- 85
grade <- ifelse(score >= 90, "A",
ifelse(score >= 80, "B",
ifelse(score >= 70, "C",
ifelse(score >= 60, "D", "F"))))
grade # [1] "B"
ifelse() vs if/else
| Feature | if/else | ifelse() |
|---|---|---|
| Scalar | Yes | No |
| Vectorized | No | Yes |
| Speed | Slower for vectors | Faster for vectors |
| Return Type | Any | Same type as test |
# WRONG — only tests first element
x <- c(1, 5, 10, 15, 20)
if (x > 10) "big" else "small"
# Warning: the condition has length > 1 and only the first element will be used
# RIGHT — vectorized
ifelse(x > 10, "big", "small")
# [1] "small" "small" "small" "big" "big"
switch() — Multi-Way Branching
# By position
x <- 2
switch(x,
"first",
"second",
"third",
"fourth"
)
# [1] "second"
# By name
x <- "apple"
switch(x,
apple = "red",
banana = "yellow",
grape = "purple",
"unknown"
)
# [1] "red"
# Default value
x <- "unknown_fruit"
switch(x,
apple = "red",
banana = "yellow",
grape = "purple",
"unknown" # Default (no name)
)
# [1] "unknown"
# In function
calculate <- function(op, x, y) {
switch(op,
add = x + y,
subtract = x - y,
multiply = x * y,
divide = if (y != 0) x / y else "Cannot divide by zero",
"Unknown operation"
)
}
calculate("add", 10, 5) # [1] 15
calculate("multiply", 10, 5) # [1] 50
calculate("power", 10, 5) # [1] "Unknown operation"
case_when() — Tidyverse Multi-Condition
library(dplyr)
x <- c(1, 5, 10, 15, 20, 25)
case_when(
x < 5 ~ "low",
x < 15 ~ "medium",
x < 25 ~ "high",
TRUE ~ "very high"
)
# [1] "low" "medium" "medium" "high" "high" "very high"
# With data frames
df <- data.frame(score = c(85, 92, 78, 95, 88))
df |>
mutate(grade = case_when(
score >= 90 ~ "A",
score >= 80 ~ "B",
score >= 70 ~ "C",
score >= 60 ~ "D",
TRUE ~ "F"
))
case_match() — Exact Matching (R 4.2+)
x <- c("a", "b", "c", "d")
case_match(x,
"a" ~ "apple",
"b" ~ "banana",
.default = "other"
)
# [1] "apple" "banana" "other" "other"
Nested and Complex Conditions
# Nested if/else
x <- 15
y <- 20
if (x > 10) {
if (y > 15) {
result <- "both big"
} else {
result <- "only x big"
}
} else {
if (y > 15) {
result <- "only y big"
} else {
result <- "neither big"
}
}
result # [1] "both big"
# Compound conditions
age <- 25
income <- 50000
if (age >= 18 && income >= 30000) {
cat("Eligible for loan\n")
}
# Multiple conditions
x <- 10
if (x > 5 && x < 15 && x != 10) {
cat("Condition met\n")
} else {
cat("Condition not met\n")
}
# Condition not met
Vectorized vs Scalar
# Scalar: if/else
x <- 10
if (x > 5) "big" else "small"
# Vectorized: ifelse
x <- c(1, 5, 10, 15, 20)
ifelse(x > 5, "big", "small")
# Vectorized: case_when
x <- c(1, 5, 10, 15, 20)
case_when(
x < 5 ~ "small",
x < 15 ~ "medium",
TRUE ~ "large"
)
# Vectorized: dplyr
df <- data.frame(x = c(1, 5, 10, 15, 20))
df |>
mutate(category = case_when(
x < 5 ~ "small",
x < 15 ~ "medium",
TRUE ~ "large"
))
Practice Exercises
Exercise 1: Grade Calculator
Write a function that takes a numeric score (0-100) and returns the letter grade using if/else.
Solution
grade_calc <- function(score) {
if (score >= 90) {
"A"
} else if (score >= 80) {
"B"
} else if (score >= 70) {
"C"
} else if (score >= 60) {
"D"
} else {
"F"
}
}
grade_calc(85) # [1] "B"
grade_calc(92) # [1] "A"
grade_calc(55) # [1] "F"
Exercise 2: Vectorized Category
Given x <- c(1, 5, 10, 15, 20, 25, 30), create a vector of categories: "low" (less than 10), "medium" (10-20), "high" (greater than 20).
Solution
x <- c(1, 5, 10, 15, 20, 25, 30)
# Using ifelse (nested)
categories <- ifelse(x < 10, "low",
ifelse(x <= 20, "medium", "high"))
categories
# Using case_when (cleaner)
library(dplyr)
case_when(
x < 10 ~ "low",
x <= 20 ~ "medium",
TRUE ~ "high"
)
Key Takeaways
if/elseis for scalar (single value) decisionsifelse()is vectorized — works on entire vectorsswitch()is for multi-way branching by valuecase_when()is the tidyverse alternative for complex conditions- R is vectorized — prefer
ifelse()andcase_when()over loops if/elseonly tests the first element of a vector (with warning)- Use
TRUE ~as default incase_when()
Next: Learn about R Loops — for, while, and repeat loops.