Writing Functions

Topics: 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.

Function Basics Review

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

Default Parameters

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

  1. default parameters and
  2. 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"
Variable-Length Arguments with ...

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"
Anonymous Functions

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