R Apply Family
The apply family is a group of functions that replace loops by applying a function to every element of a structure — rows, columns, list items, or vector elements. They are faster to write than loops, often faster to run, and produce cleaner code. Each function in the family handles a different input type.
Apply Family Overview
Function Input Output Best For ────────────────────────────────────────────────────────────────── apply() Matrix/Array Vector/List Rows or columns of matrix lapply() List/Vector List Any list operation sapply() List/Vector Vector/Matrix Simplified lapply output vapply() List/Vector Typed vector Safe, typed version of sapply tapply() Vector+groups Array Summary stats by group mapply() Multiple lists Vector/List Multiple-argument function Map() Multiple lists List Cleaner mapply alternative
apply() — Rows and Columns of a Matrix
scores <- matrix(c(85,92,78, 90,76,88, 95,70,82), nrow=3,
dimnames=list(c("Asha","Balu","Cena"),
c("Math","Science","English")))
apply(scores, 1, mean) # MARGIN=1: apply to each ROW
# Asha Balu Cena
# 85.0 84.7 82.3
apply(scores, 2, mean) # MARGIN=2: apply to each COLUMN
# Math Science English
# 90.0 79.3 82.7
apply(scores, 1, max) # max score per student
apply(scores, 2, sd) # std dev per subject
Diagram: Matrix: apply(m, 1, fn) apply(m, 2, fn) [85 90 95] ──► row fn │↓ col fn [92 76 70] ──► row fn │↓ col fn [78 88 82] ──► row fn │↓ col fn
lapply() — Returns a List
prices <- list(laptops=c(45000,52000,48000),
phones =c(15000,25000,18000),
tablets=c(28000,32000,30000))
lapply(prices, mean)
# $laptops [1] 48333
# $phones [1] 19333
# $tablets [1] 30000
lapply(prices, function(x) c(min=min(x), max=max(x), avg=mean(x)))
sapply() — Simplified Output
# sapply tries to simplify to vector or matrix sapply(prices, mean) # laptops phones tablets # 48333 19333 30000 sapply(1:5, function(x) x^2) # [1] 1 4 9 16 25 # Returns matrix when each result has same length sapply(prices, range) # laptops phones tablets # [1,] 45000 15000 28000 # [2,] 52000 25000 32000
vapply() — Type-Safe Version
# vapply forces specific output type — safer than sapply vapply(prices, mean, FUN.VALUE=numeric(1)) # laptops phones tablets # 48333 19333 30000 # Will error if output doesn't match FUN.VALUE type — catches bugs early
tapply() — Apply by Groups
salaries <- c(45000,72000,68000,55000,50000,80000)
depts <- c("HR","IT","IT","Finance","HR","IT")
tapply(salaries, depts, mean)
# Finance HR IT
# 55000 47500 73333
tapply(salaries, depts, function(x) c(n=length(x), avg=mean(x)))
mapply() / Map() — Multiple Arguments
# Apply a function that takes two arguments
lengths <- c(4, 6, 8, 10)
widths <- c(3, 5, 2, 7)
mapply(function(l, w) l * w, lengths, widths)
# [1] 12 30 16 70
# Map() is cleaner syntax
Map(function(l, w) l * w, lengths, widths)
# [[1]] 12 [[2]] 30 [[3]] 16 [[4]] 70
# Practical: build a formatted report line for each pair
Map(function(l,w) paste("Area:", l*w, "sq units"), lengths, widths)
Apply vs Loop: When to Use Which
Use apply family when: Use a loop when: ────────────────────────────── ─────────────────────────────────── Same operation on every item Each iteration depends on the last Clean, functional style needed Building up results step by step Output size is predictable Complex branching logic per step
Practical: Normalize All Numeric Columns
students <- data.frame( score = c(78,85,92,65,88), hours = c(5,7,9,3,8), salary = c(30000,45000,60000,25000,50000) ) normalize <- function(x) (x - min(x)) / (max(x) - min(x)) # Apply to all numeric columns normalized <- as.data.frame(lapply(students, normalize)) print(round(normalized, 2))
The apply family makes R code more expressive and concise. Once you think in terms of "apply this function to every item" instead of "write a loop that iterates over every item," your R code becomes shorter, easier to read, and more aligned with R's vectorized philosophy.
