Further

From a method chosen by its class to a missing value in a column.

Eleven programs past the first page: S3 methods, packages, functions over lists, factors, data frames, and NA. Each one still has a check you answer here.

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.

What number is printed?
Why is meters.pier the function that runs?
Try it

One 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.

What two numbers are printed?
What does the caller have to name?
Try it

A 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.

What number is printed?
Where is outer_span found?
Try it

A 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.

What number is printed?
What does stats:: do?
Try it

Functions 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.

What numbers are printed?
What does lapply return before unlist?
Try it

A 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.

What number is printed?
Where does the 2 come from when double(6) runs?
Try it

A 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.

What three types are printed?
What does pieces[[2]] return?
Try it

Tables

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.

What numbers are printed?
Why is clear the code 1?
Try it

A 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.

What number is printed?
What is a data frame made of?
Try it

A 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.

What number is printed?
In knots ~ span, which name is being described?
Try it

NA 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.

What two values are printed?
What does na.rm = TRUE do?
Try it

Practice

Packages that ship with R, plots, and a few summaries each have their own program on the next page.

Continue