R objects and functions Søren Højsgaard Department of Mathematical Sciences Aalborg University, Denmark October 15, 2012 Printed: October 15, 2012 File: objects-slides.tex
2 Contents 1 Attributes 3 1.1 class attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2 More about classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2 The S3 object system 7 2.1 Example: Single exponential smoothing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2 Generic functions and methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.3 More about generic functions and methods . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.4 Multiple inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.5 Example: Double exponential smoothing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.6 EXERCISES: Exponential smoothing and the S3 system . . . . . . . . . . . . . . . . . . . . . 23
3 1 Attributes An attribute of an R object is another R object attached by a name to the object: R> x <- 123 R> attr(x,"foo") <- "something" R> attr(x,"bar") <- c(1,10) R> x [1] 123 attr(,"foo") [1] "something" attr(,"bar") [1] 1 10 R> attr(x,"foo") [1] "something" R> attributes(x) $foo [1] "something" $bar [1] 1 10
4 1.1 class attributes A class is an example of an attribute: R> (today <- Sys.Date()) [1] "2012-10-15" R> dput(today) structure(15628, class = "Date") R> ff <- factor(c(1,2,1,2)) R> dput(ff) structure(c(1L, 2L, 1L, 2L), .Label = c("1", "2"), class = "factor") The class can be retrieved with R> class(today) [1] "Date" R> class(ff) [1] "factor"
5 1.2 More about classes However not all objects have classes (in terms of a class attribute) – but class() still returns“the class”of an object. It all seems to be not entirely consistent, but there is a long history behind things R> MM <- matrix(1:4, nrow=2) R> dput(MM) structure(1:4, .Dim = c(2L, 2L)) R> class(MM) [1] "matrix" R> AA <- array(1:8, dim=c(2,2,2)) R> dput(AA) structure(1:8, .Dim = c(2L, 2L, 2L)) R> class(AA) [1] "array"
6 R> VV <- 1:8 R> dim(VV) <- 8 R> dput(VV) structure(1:8, .Dim = 8L) R> class(VV) [1] "array" • Neither of these objects have a class attribute. • It is the dim attribute that determines the class. • In fact, a matrix is also an array: R> is(MM, "array") [1] TRUE
7 2 The S3 object system There are two approaches to“object” –oriented programming in R : S3–classes and S4–classes. • The S3–system has been around for a very long time and it is easy to use (to program and to document). • The S4–system is much newer and somewhat more involved to use. We only look at the S3–system here: • Create generic functions (example: summary() ) • Create a summary() method for objects of class "foo" ; i.e. a function called summary.foo() . • When calling summary() on an object of class "foo" the call will dispatch to the summary() method for "foo" objects; i.e. to summary.foo() .
8 2.1 Example: Single exponential smoothing The Nile data contains measurements of the annual flow of the river Nile at Ashwan 1871–1970. There seems to be a drow around year 1900. Why? R> data(Nile) R> plot(Nile) 1200 Nile 800 600 1880 1900 1920 1940 1960 Time
9 We make a single exponential smoothing of data: S t = αy t + (1 − α ) S t − 1 , S 1 = y 1 where 0 < α < 1 . Given data up to time t the natural forecast h time steps ahead is y t + h | t = S t ˆ so the one–step–ahead forecast error is e t = y t − ˆ y t | t − 1 = y t − S t − 1 . An alternative form is: S t = S t − 1 + α ( y t − S t − 1 ) = S t + αe t
10 A simple function that does the steps above is: R> ses <- function(yvar, alpha=.1){ n.obs <- length(yvar) sss <- rep.int(NA, n.obs) sss[1] <- yvar[1] for (ii in 2:n.obs){ sss[ii] <- alpha * yvar[ii] + (1-alpha)*sss[ii-1] } res <- list(x=1:n.obs, y=sss, y.obs=yvar) class(res) <- c( ' sesObj ' , ' list ' ) res } R> sss <- ses(as.numeric(Nile)) R> str(sss) List of 3 $ x : int [1:100] 1 2 3 4 5 6 7 8 9 10 ... $ y : num [1:100] 1120 1124 1108 1118 1122 ... $ y.obs: num [1:100] 1120 1160 963 1210 1160 1160 813 1230 1370 1140 ... - attr(*, "class")= chr [1:2] "sesObj" "list" Notice: The result is a list but with an added class attributes.
11 The result (a list) can be plotted with R> plot(as.numeric(Nile)) R> lines(y~x, data=sss) ● 1200 ● ● ● ● ● ● ● as.numeric(Nile) ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● 800 ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● 600 ● 0 20 40 60 80 100 Index But we can also take a more object oriented approach.
12 2.2 Generic functions and methods The plot –function is a generic function. The action of plot depends on what we try to plot (a model object, a matrix, at dataframe). We create a plot –method for sesObj –objects: R> plot.sesObj <- function(x,...){ plot(x$x, x$y.obs,...) lines(x$x, x$y, col= ' red ' ,...) } R> plot(sss,pch=16)
13 ● 1200 ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● x$y.obs ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● 800 ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● 600 ● 0 20 40 60 80 100 x$x
14 Other methods that might be of relevance R> residuals.sesObj <- function(object, ...) { object$y.obs - object$y } R> fitted.sesObj <- function(object, ...) { object$y } Notice one detail: We can coerce the result to a dataframe even though we have not defined an as.data.frame() –method for sesObj –objects: R> aa <- as.data.frame(sss) R> head(aa) x y y.obs 1 1 1120.000 1120 2 2 1124.000 1160 3 3 1107.900 963 4 4 1118.110 1210 5 5 1122.299 1160 6 6 1126.069 1160
15 R> class(sss) [1] "sesObj" "list" This works because R first checks if there is a as.data.frame() –method for sesObj –objects. If so, this method is applied; if not then R checks if there ´ ıs a as.data.frame() –method for a list s. Such a method exists and is this method is applied.
16 2.3 More about generic functions and methods R> ## Generic function: R> goodFun <- function(x){ UseMethod( ' goodFun ' ) } R> ## Default method: R> goodFun.default <- function(x){ cat("Don ' t know what to do here\n") } R> zzz <- 123 R> goodFun(zzz) Don ' t know what to do here R> class(zzz) <- ' foo ' R> goodFun(zzz) This is a ' foo ' object: 123 The generic function goodFun() will look for an appropriate method to apply to the input. In absence of such an appropriate method the generic function will resort to a default method.
17 We create a goodFun method for foo objects R> ## Method for foo objects R> goodFun.foo <- function(x){ cat(sprintf("This is a ' foo ' object: %s\n", toString(x))) } R> goodFun(zzz) This is a ' foo ' object: 123
18 2.4 Multiple inheritance We can work with multiple inheritance: An object can have classes bar and foo , and in this setting the goodFun method for bar is called first: R> ## Method for bar objects R> goodFun.bar <- function(x){ cat(sprintf("This is a ' bar ' object: %s\n", toString(x))) } R> class(zzz) <- c( ' bar ' , ' foo ' ) R> goodFun(zzz) This is a ' bar ' object: 123
19 We can call the method for the next class in the hierarchy using NextMethod : R> ## Method for bar objects R> goodFun.bar <- function(x){ cat(sprintf("This is a ' bar ' object: %s\n", toString(x))) NextMethod(x) } R> class(zzz) <- c( ' bar ' , ' foo ' ) R> goodFun(zzz) This is a ' bar ' object: 123 This is a ' foo ' object: 123
20 2.5 Example: Double exponential smoothing Single expo- nential smoothing does not work well when there is a trend in data, i.e. if y t ≈ a + bt R> y <- log(as.numeric(JohnsonJohnson)) R> se <- ses(y,.1) R> plot(se,xlab= '' ) ●●● ●●● ●●● ● ●●●● ● ●●● ● ●●● 2 ●●●● ● ●●● ● ● ●●●● ●●● x$y.obs ●●●● ● 1 ●●●●●● ● ● ●●●●● ● ●●●●●●● ● 0 ● ●● ● ● ● ●● ●● ● ●● ●● ● ● ● 0 20 40 60 80
Recommend
More recommend