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.

Leave a Comment

Your email address will not be published. Required fields are marked *