apply()
apply(X, MARGIN, FUN, ...) The apply() function is a base R function that applies a specified function to the margins (rows or columns) of an array or matrix. It provides a clean alternative to writing explicit for loops when working with matrix-style data structures.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| X | array/matrix | Required | An array or matrix to apply the function over |
| MARGIN | integer vector | Required | A vector indicating which margins to apply over. Use 1 for rows, 2 for columns, or c(1, 2) for both |
| FUN | function | Required | The function to apply to each margin |
| … | any | None | Additional arguments passed to FUN |
| simplify | logical | TRUE | If TRUE and the result is a scalar, attempts to simplify to a vector or matrix |
Return value
Returns a vector, matrix, or array depending on the value of MARGIN, the return type of FUN, and the simplify argument.
Basic example
# Create a sample matrix
m <- matrix(1:9, nrow = 3, ncol = 3)
print(m)
# [,1] [,2] [,3]
# [1,] 1 4 7
# [2,] 2 5 8
# [3,] 3 6 9
# Apply sum over rows (MARGIN = 1)
row_sums <- apply(m, 1, sum)
print(row_sums)
# [1] 12 15 18
# Apply sum over columns (MARGIN = 2)
col_sums <- apply(m, 2, sum)
print(col_sums)
# [1] 6 15 24
The basic example demonstrates apply() with a built-in function like sum(). When MARGIN = 1, R processes each row as a separate vector and passes it to the function — so sum(1, 4, 7) gives 12 for the first row. For column-wise operations with MARGIN = 2, each column is passed as a vector instead. This is the fundamental pattern behind all apply-family functions in R.
Using custom functions
# Calculate row means
row_means <- apply(m, 1, function(x) mean(x))
print(row_means)
# [1] 4 5 6
# Apply a custom function with additional arguments
apply(m, 1, function(x, threshold) sum(x > threshold), threshold = 4)
# [1] 2 2 2
Custom functions make apply() flexible — you can pass any function, including anonymous ones defined inline. Extra arguments after the function are forwarded to each call: apply(m, 1, function(x, threshold) ..., threshold = 4) sends threshold = 4 alongside each row vector. This pattern avoids hard-coding constants inside the anonymous function and keeps the logic parameterised.
Working with MARGIN = c(1, 2)
# Apply a function to each element (returns a matrix)
apply(m, c(1, 2), function(x) x^2)
# [,1] [,2] [,3]
# [1,] 1 16 49
# [2,] 4 25 64
# [3,] 9 36 81
Passing MARGIN = c(1, 2) applies the function to every individual element of the matrix — effectively an element-wise operation that returns a matrix of the same dimensions. This is equivalent to sapply(1:nrow(m), function(i) sapply(1:ncol(m), function(j) f(m[i, j]))) but is far more concise and considerably faster for large matrices because the iteration happens in compiled C code rather than interpreted R.
Practical example: standardize columns
# Standardize each column (mean = 0, sd = 1)
standardize <- function(x) (x - mean(x)) / sd(x)
m <- matrix(rnorm(30), nrow = 5, ncol = 6)
standardized_m <- apply(m, 2, standardize)
print(head(standardized_m))
Common pitfalls
Data frame coercion: apply() silently coerces data frames to matrices, which converts all columns to a common type. A data frame with one character column will coerce every numeric column to character, functions expecting numbers will fail or produce wrong results.
Simplification behavior: With simplify = TRUE (the default), results may become a matrix instead of a list when the function returns multiple values. Use simplify = FALSE or lapply() when you want consistent list output regardless of the result shape.
Margins greater than 2: For arrays with three or more dimensions, pass a vector like MARGIN = c(1, 3) to apply over the first and third dimensions simultaneously.
apply() operates on matrices and arrays. For a matrix, MARGIN = 1 applies the function to each row; MARGIN = 2 applies to each column. The result is a vector if the function returns a scalar, or a matrix if it returns a vector of the same length each time. For data frames, apply() coerces to a matrix first — this works only when all columns share a compatible type.
When to use apply()
apply() works best with matrices and arrays where all columns are the same type. For data frames, consider lapply() (returns a list) or sapply() (attempts to simplify). The MARGIN argument is what distinguishes apply() from other apply family functions: MARGIN = 1 processes one row at a time, MARGIN = 2 processes one column at a time.
For data frames with all-numeric columns, apply() is fine but colMeans(), colSums(), rowMeans(), and rowSums() are faster for the common cases. Reserve apply() for custom functions that do not have a vectorized base-R counterpart.