The Big R-Book. Philippe J. S. De Brouwer

Чтение книги онлайн.

Читать онлайн книгу The Big R-Book - Philippe J. S. De Brouwer страница 45

The Big R-Book - Philippe J. S. De Brouwer

Скачать книгу

two test objects: M <- matrix(1:16, nrow=4) df <- data.frame(M) # Test our new function: is.S3(M) ## [1] FALSE is.S3(df) ## [1] TRUE

      However, it is not really necessary to create such function by ourselves. We can leverage the library pryr, which provides a function otype() that returns the type of object.

       pryr

       otype()

      library(pryr) otype(M) ## [1] “base” otype(df) ## [1] “S3” otype(df$X1) # a vector is not S3 ## [1] “base” df$fac <-factor(df$X4) otype(df$fac) # a factor is S3 ## [1] “S3”

      If you would like to determine if a function is S3 generic, then you can check the source code for the use of the function useMethod(). This function will take care of the dispatching and hence decide which method to call for the given object.

       useMethod()

      However, this method is not foolproof because some primitive functions have this switch statement embedded in their C-code. For example, [, sum(), rbind(), and cbind() are generic functions, but this is not visible in their code in R.

      mean ## function (x, …) ## UseMethod(“mean”) ## <bytecode: 0x563423e48908> ## <environment: namespace:base> ftype(mean) ## [1] “s3” “generic” sum ## function (…, na.rm = FALSE) .Primitive(“sum”) ftype(sum) ## [1] “primitive” “generic”

      R calls the functions that have this switch in their C-code “internal” “generic”.

      The S3 generic function basically decides to what other function to dispatch its task. For example, the function print can be called with any base or S3 object and print will decide what to do based on its class. Try the function apropos() to find out what different methods exist (or type print. in RStudio.

      apropos(“print.”) ## [1] “print.AsIs” ## [2] “print.by” ## [3] “print.condition” ## [4] “print.connection” ## [5] “print.data.frame” ## [6] “print.Date” ## [7] “print.default” ## [8] “print.difftime” ## [9] “print.Dlist” ## [10] “print.DLLInfo” ## [11] “print.DLLInfoList” ## [12] “print.DLLRegisteredRoutines” ## [13] “print.eigen” ## [14] “print.factor” ## [15] “print.function” ## [16] “print.hexmode” ## [17] “print.libraryIQR” ## [18] “print.listof” ## [19] “print.NativeRoutineList” ## [20] “print.noquote” ## [21] “print.numeric_version” ## [22] “print.octmode” ## [23] “print.packageInfo” ## [24] “print.POSIXct” ## [25] “print.POSIXlt” ## [26] “print.proc_time” ## [27] “print.restart” ## [28] “print.rle” ## [29] “print.simple.list” ## [30] “print.srcfile” ## [31] “print.srcref” ## [32] “print.summary.table” ## [33] “print.summaryDefault” ## [34] “print.table” ## [35] “print.warnings” ## [36] “printCoefmat” ## [37] “sprintf” apropos(“mean.”) ## [1] “.colMeans” “.rowMeans” “colMeans” ## [4] “kmeans” “mean.Date” “mean.default” ## [7] “mean.difftime” “mean.POSIXct” “mean.POSIXlt” ## [10] “rowMeans”

       methods()

      methods(methods) ## no methods found methods(mean) ## [1] mean.Date mean.default mean.difftime ## [4] mean.POSIXct mean.POSIXlt ## see ‘?methods’ for accessing help and source code

      image Hint – Naming conventions

      Do not use the dot “.” in function names because it makes them look like S3 functional methods. This might lead to confusion with the convention that the methods are named as <<generic function>>.<<class name>>. Especially, if there is more than one dot in the name. For example, print.data.frame() is not univocal: is it a dataframe method for the generic function print or is it the frame method for the generic function print.data? Another example is the existence of the function t.test() to run t-tests as well as t.dataframe(), that is the S3 method for the generic function t() to transpose a data frame.

       t.test()

       t.data.frame()

       t()

      To access the source code of the class-specific methods, one can use the function getS3method().

       getS3method()

      getS3method(“print”,“table”) ## function (x, digits = getOption(“digits”), quote = FALSE, na.print = “”, ## zero.print = “0”, justify = “none”, …) ## { ## d <- dim(x) ## if (any(d == 0)) { ## cat(“< table of extent”, paste(d, collapse = “ x “), ## “>\n”) ## return(invisible(x)) ## } ## xx <- format(unclass(x), digits = digits, justify = justify) ## if (any(ina <- is.na(x))) ## xx[ina] <- na.print ## if (zero.print != “0” && any(i0 <- !ina & x == 0)) ## xx[i0] <- zero.print ## if (is.numeric(x) || is.complex(x)) ## print(xx, quote = quote, right = TRUE, …) ## else print(xx, quote = quote, …) ## invisible(x) ## } ## <bytecode: 0x5634250f12e8> ## <environment: namespace:base>

      The other way around it is possible to list all generic functions that have a specific method for a given class.

      methods(class = “data.frame”) ## [1] [ [[ [[<- ## [4] [<- $ $<- ## [7] aggregate anyDuplicated as.data.frame ## [10] as.list as.matrix by ## [13] cbind coerce dim ## [16] dimnames dimnames<- droplevels ## [19] duplicated edit format ## [22] formula head initialize ## [25] is.na Math merge ## [28] na.exclude na.omit Ops ## [31] plot print prompt ## [34] rbind row.names row.names<- ## [37] rowsum show slotsFromS3 ## [40] split split<- stack ## [43] str subset summary ## [46] Summary t tail ## [49] transform unique unstack ## [52] within ## see ‘?methods’ for accessing help and source code

      6.2.1 Creating S3 Objects

      S3 is a minimalistic and informal OO system; there is not even a formal definition of a class. In S3, you never create a class definition and start from the instance itself. Actually, to create an S3 object, it is sufficient to set its class attribute.

Скачать книгу