rguides

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

ParameterTypeDefaultDescription
Xarray/matrixRequiredAn array or matrix to apply the function over
MARGINinteger vectorRequiredA vector indicating which margins to apply over. Use 1 for rows, 2 for columns, or c(1, 2) for both
FUNfunctionRequiredThe function to apply to each margin
anyNoneAdditional arguments passed to FUN
simplifylogicalTRUEIf 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.

See also