13.5 Object styles
So far I’ve focussed on vector style classes like
factor. These have the key property that
length(x) represents the number of observations in the vector. There are three variants that do not have this property:
Record style objects use a list of equal-length vectors to represent individual components of the object. The best example of this is
POSIXlt, which underneath the hood is a list of 11 date-time components like year, month, and day. Record style classes override
length()and subsetting methods to conceal this implementation detail.
as.POSIXlt(ISOdatetime(2020, 1, 1, 0, 0, 1:3)) x <- x#>  "2020-01-01 00:00:01 UTC" "2020-01-01 00:00:02 UTC" #>  "2020-01-01 00:00:03 UTC" length(x) #>  3 length(unclass(x)) #>  11 1]] # the first date time x[[#>  "2020-01-01 00:00:01 UTC" unclass(x)[] # the first component, the number of seconds #>  1 2 3
Data frames are similar to record style objects in that both use lists of equal length vectors. However, data frames are conceptually two dimensional, and the individual components are readily exposed to the user. The number of observations is the number of rows, not the length:
data.frame(x = 1:100, y = 1:100) x <-length(x) #>  2 nrow(x) #>  100
Scalar objects typically use a list to represent a single thing. For example, an
lmobject is a list of length 12 but it represents one model.
lm(mpg ~ wt, data = mtcars) mod <-length(mod) #>  12
Scalar objects can also be built on top of functions, calls, and environments50. This is less generally useful, but you can see applications in
stats::ecdf(), R6 (Chapter 14), and
Unfortunately, describing the appropriate use of each of these object styles is beyond the scope of this book. However, you can learn more from the documentation of the vctrs package (https://vctrs.r-lib.org); the package also provides constructors and helpers that make implementation of the different styles easier.
Categorise the objects returned by
I()into the styles described above.
What would a constructor function for
new_lm(), look like? Use
?lmand experimentation to figure out the required fields and their types.
You can also build an object on top of a pairlist, but I have yet to find a good reason to do so.↩︎