Writing Functions
Functions are the building blocks of well-organized code. They let you write logic once and reuse it across multiple scenarios — an essential skill when automating tasks like calculating BMI, summarizing lab results, or cleaning patient datasets.
Let’s say we want to create a function that greets a patient when their name is entered into a system.
greet <- function(name) {
# A simple function that greets someone.
return(paste0("Hello, ", name, "! Welcome to your appointment."))
}
Call the function:
message <- greet("Dr. Reyes")
print(message) # Output: Hello, Dr. Reyes! Welcome to your appointment.
Advanced Function Features
Let’s define a function that creates a basic patient profile — similar to what you’d store in an electronic medical record (EMR).
create_profile <- function(name, age, city = "Davao", diagnosis = "Not yet diagnosed") {
# Create a patient profile with default values.
profile <- list(
name = name,
age = age,
city = city,
diagnosis = diagnosis
)
return(profile)
}
Run the function using
- default parameters and
- custom parameters:
patient1 <- create_profile("Ana Santos", 35)
patient2 <- create_profile("Miguel Cruz", 58, "Davao", "Hypertension")
print(patient1) # city and diagnosis use defaults
# Output
# $name
# [1] "Ana Santos"
# $age
# [1] 35
# $city
# [1] "Davao"
# $diagnosis
# [1] "Not yet diagnosed"
print(patient2) # all parameters specified
# Output
# $name
# [1] "Miguel Cruz"
# $age
# [1] 58
# $city
# [1] "Davao"
# $diagnosis
# [1] "Hypertension"
R uses ... (the ellipsis) to pass a variable number of arguments to a function.
Example 1: Calculate average blood glucose readings:
calculate_average <- function(...) {
# Calculate average of any number of readings.
values <- c(...)
if (length(values) == 0) return(0)
return(mean(values))
}
Example 2: Create a patient record with flexible fields using a named list:
create_patient_record <- function(name, ...) {
# Create a patient record with flexible information.
extra <- list(...)
record <- c(list(name = name), extra)
return(record)
}
Use the functions:
avg_glucose <- calculate_average(92, 110, 87, 105)
cat(sprintf("Average Glucose: %.1f mg/dL\n", avg_glucose)) # Average Glucose: 98.5 mg/dL
patient <- create_patient_record(
"Maria Dela Cruz",
age = 47,
condition = "Diabetes",
medications = c("Metformin", "Insulin"),
last_visit = "2025-09-10"
)
print(patient)
# Output
# $name
# [1] "Maria Dela Cruz"
# $age
# [1] 47
# $condition
# [1] "Diabetes"
# $medications
# [1] "Metformin" "Insulin"
# $last_visit
# [1] "2025-09-10"
R supports anonymous (inline) functions — compact, one-line functions useful for quick calculations or passing logic to other functions.
# Named function
bmi <- function(weight, height) weight / (height ^ 2)
# Anonymous function equivalent (R 4.1+ shorthand)
bmi_anon <- \(weight, height) weight / (height ^ 2)
print(bmi_anon(70, 1.75)) # Output: 22.85714
print(bmi(70, 1.75)) # Same output
Filtering a list of patients using Filter():
patients <- list(
list(name = "Ana", bmi = 22.5),
list(name = "Ben", bmi = 29.8),
list(name = "Clara", bmi = 18.9)
)
# Get only overweight patients (BMI >= 25)
overweight <- Filter(\(p) p$bmi >= 25, patients)
print(overweight)
# Output
# [[1]]
# [[1]]$name
# [1] "Ben"
# [[1]]$bmi
# [1] 29.8
Advantages of anonymous functions:
- Defined inline without naming
- Ideal for short, single-use logic inside functions like
Filter(),Map(),sapply()
Disadvantages:
- Can reduce readability if overused or applied to complex logic
- For multi-step operations, a named function is usually clearer
Another example using sapply():
numbers <- 1:5
squared <- sapply(numbers, \(x) x ^ 2)
print(squared) # 1 4 9 16 25
students <- list(
list(name = "Alice", grade = 85),
list(name = "Bob", grade = 92),
list(name = "Charlie", grade = 78)
)
high_performers <- Filter(\(s) s$grade >= 85, students)
print(high_performers)
# Output
# [[1]]
# [[1]]$name
# [1] "Alice"
# [[1]]$grade
# [1] 85
# [[2]]
# [[2]]$name
# [1] "Bob"
# [[2]]$grade
# [1] 92
Functions let you automate repetitive tasks, from calculating averages of lab results to generating formatted patient summaries.
They make your code modular, reusable, and easier to understand, especially when analyzing multiple patient records or large datasets.