Big idea: Functions are not about being fancy — they’re about avoiding copy‑paste errors and making intent explicit.
Learning Objectives
By the end of this lesson, you will be able to:
- Explain what a function is in R.
- Read and understand simple function definitions.
- Write small functions with arguments and defaults.
- Return values intentionally.
- Recognize when a function improves PMx code clarity.
Why Functions Matter in PMx
In PMx work, you often repeat tasks like:
- checking data structure
- making the same QC plot
- applying the same transformation rule
Copy‑pasting code is risky.
Functions let you write logic once and reuse it safely.
What Is a Function?
A function:
- takes inputs (arguments)
- performs operations
- returns an output
You already use functions all the time:
The Basic Structure of a Function
my_function <- function(x) {
x * 2
}
my_function is the function name
x is the argument
- the last expression is returned automatically
Use it like this:
Functions with Multiple Arguments
Functions can accept more than one input (called arguments).
add_two_numbers <- function(x, y) {
x + y
}
When calling the function, you can pass arguments by position:
Here:
3 is assigned to x
4 is assigned to y
You can also pass arguments by name (sometimes called keyword arguments):
add_two_numbers(x = 3, y = 4)
Named arguments make your code clearer and safer — especially when functions have many inputs.
They also allow you to change the order:
add_two_numbers(y = 4, x = 3)
The result is the same because the names determine the mapping.
Default Argument Values
Functions can define default values for arguments.
This makes them easier to use and safer in practice.
scale_dose <- function(dose, factor = 1) {
dose * factor
}
Because factor has a default value of 1, we can call the function with just a dose:
R automatically uses factor = 1.
If we want to override the default, we supply a different value:
scale_dose(100, factor = 0.8)
A PMx‑Style Example: Safe Mean
Instead of repeating na.rm = TRUE everywhere:
safe_mean <- function(x) {
mean(x, na.rm = TRUE)
}
safe_mean(c(1.2, NA, 2.8))
This makes your intent explicit.
Functions Returning Data Frames
Functions can return anything — including tibbles.
filter_subject <- function(data, id) {
data[data$ID == id, ]
}
Usage:
pk <- data.frame(
ID = c(1, 1, 1, 2, 2, 2),
TIME = c(0.5, 1, 2, 0.5, 1, 2),
DV = c(2.1, 3.8, 3.0, 1.6, 2.9, 2.4)
)
filter_subject(pk, id = 1)
ID TIME DV
1 1 0.5 2.1
2 1 1.0 3.8
3 1 2.0 3.0
Notice we didn’t modify pk inside the function — functions should avoid side effects.
When Should You Write a Function?
Good candidates:
- code you copy more than twice
- logic that must be consistent
- steps you want to name clearly
Bad candidates:
- one‑off experiments
- very short throwaway code
- highly interactive exploration
Strategies
- Start with simple functions.
- Use descriptive names.
- Add defaults for common cases.
- Test functions on small inputs first.
- Prefer clarity over cleverness.
Practice Problems
- Write a function that multiplies a dose by a factor.
- Add a default factor value.
- Write a function that returns rows for a single subject ID.
- Write a function that safely computes a median.
- Explain in one sentence why functions reduce PMx risk.
scale_dose <- function(dose, factor = 1) {
dose * factor
}
filter_subject <- function(data, id) {
data[data$ID == id, ]
}
safe_median <- function(x) {
median(x, na.rm = TRUE)
}
Summary
You now know how to:
- read and write simple R functions
- use arguments and defaults
- return values intentionally
- reduce repetition and error risk in PMx workflows
Functions are a quiet superpower — you don’t need many, but the ones you write matter.
- If you copy‑paste code, consider a function.
- Keep functions small and focused.
- Avoid modifying objects outside the function’s scope.
- Test functions before trusting them.