Shared behavior
A method is chosen from the class
UseMethod looks at the class of the argument and calls the function whose name is the generic, a dot, and that class. meters.pier runs because the value was given class "pier".
meters <- function(x) {
UseMethod("meters")
}
meters.pier <- function(x) {
x$span
}
pier <- structure(list(span = 12), class = "pier")
print(meters(pier))
What to notice. structure attaches the class. Nothing registers the method ahead of time beyond defining a function with that name.
Try itOne generic, two classes
The same generic can have a method for each class. The caller writes meters(x). The class of x decides which body runs.
meters <- function(x) UseMethod("meters")
meters.pier <- function(x) x$span
meters.span <- function(x) unclass(x)
pier <- structure(list(span = 12), class = "pier")
bare <- structure(8, class = "span")
print(c(meters(pier), meters(bare)))
What to notice. unclass drops the class attribute so the number 8 can sit in a numeric vector with the other result.
Try itA nested function sees the enclosing names
A function defined inside another function looks for names in the enclosing environment when its own body does not bind them.
outer_span <- 4
show <- function() {
inner <- function() outer_span
inner()
}
print(show())
What to notice. inner does not bind outer_span. The lookup continues outward and finds the name in the global environment.
Try itA package name and ::
:: calls a function from a package without attaching the package. stats ships with R, so median is available this way on a normal install.
print(stats::median(c(3, 9, 4)))
What to notice. This page does not install packages. :: is the call. library(stats) would attach the package so you could write median without the prefix.
Try itFunctions over vectors
lapply calls a function on each piece
lapply walks a list and calls a function on each element. The result is a list. unlist flattens that list into an atomic vector when every piece is a single number.
spans <- list(3, 5, 8)
doubled <- lapply(spans, function(span) span * 2)
print(unlist(doubled))
What to notice. The function is written where it is used. It has one argument, the current piece.
Try itA function can return a function
The returned function keeps the names from the call that built it. factor here is the argument of scale_by, and double still sees that value later.
scale_by <- function(factor) {
function(span) span * factor
}
double <- scale_by(2)
print(double(6))
What to notice. scale_by(2) returns a function. That function multiplies by the 2 from the call that created it.
Try itA list can hold different types
An atomic vector holds one type. A list can hold a number, text, and a logical value side by side. [[ picks one piece by position.
pieces <- list(12, "net", TRUE)
print(c(typeof(pieces[[1]]), typeof(pieces[[2]]), typeof(pieces[[3]])))
What to notice. The index starts at 1. pieces[[1]] is the number, not a list that contains the number.
Try itTables
A factor stores categories as codes
factor turns text into a set of levels and a vector of integer codes. The levels are sorted unless you set them yourself. as.integer shows the codes.
skies <- factor(c("clear", "cloud", "clear"))
print(as.integer(skies))
What to notice. The levels are clear and cloud, in alphabetical order. clear is code 1. cloud is code 2.
Try itA data frame is a list of columns
data.frame builds columns of equal length. $ still extracts a column by name. The column is a vector, so [1] is its first element.
piers <- data.frame(name = c("North", "South"), span = c(12, 8))
print(piers$span[1])
What to notice. name and span each have two values, one per row. A data frame will not accept columns of different lengths unless those lengths recycle cleanly.
Try itA formula names a relationship
knots ~ span is a formula. The left side is the value being described. The right side is what it depends on. lm fits a straight line and coef extracts the coefficients.
piers <- data.frame(span = c(2, 4, 6), knots = c(3, 5, 7))
fit <- lm(knots ~ span, data = piers)
print(coef(fit)[["span"]])
What to notice. These three points sit on the line knots = span + 1. The coefficient named span is the slope. This lesson is the shape of a model formula, not a statistics course.
Try itNA spreads through a summary
A missing number inside a vector makes mean return NA, unless you remove the missing values first. na.rm = TRUE drops them before the average.
spans <- c(4, NA, 10)
print(c(mean(spans), mean(spans, na.rm = TRUE)))
What to notice. NA is not zero, and it is not ignored by default. The average of the remaining numbers, 4 and 10, is 7.
Try it