class: center, middle, inverse, title-slide # Lec 04 - Attributes & S3 ##
Statistical Programming ### Fall 2021 ###
Dr. Colin Rundel --- exclude: true --- class: middle count: false # Generic Vectors --- ## Lists Lists are the final 1 dimensional (i.e. have a length) data structure in R, the different from atomic vectors in that they can contain a heterogeneous collection of R object (e.g. atomic vectors, functions, other lists, etc.). ```r list("A", c(TRUE,FALSE), (1:4)/2, list(TRUE, 1), function(x) x^2) ``` ``` ## [[1]] ## [1] "A" ## ## [[2]] ## [1] TRUE FALSE ## ## [[3]] ## [1] 0.5 1.0 1.5 2.0 ## ## [[4]] ## [[4]][[1]] ## [1] TRUE ## ## [[4]][[2]] ## [1] 1 ## ## ## [[5]] ## function(x) x^2 ``` --- ## List Structure Often we want a more compact representation of a complex object, the `str` function is useful for this particular task ```r str(c(1,2)) ``` ``` ## num [1:2] 1 2 ``` ```r str(1:100) ``` ``` ## int [1:100] 1 2 3 4 5 6 7 8 9 10 ... ``` ```r str("A") ``` ``` ## chr "A" ``` -- ```r str( list("A", c(TRUE,FALSE), (1:4)/2, list(TRUE, 1), function(x) x^2) ) ``` ``` ## List of 5 ## $ : chr "A" ## $ : logi [1:2] TRUE FALSE ## $ : num [1:4] 0.5 1 1.5 2 ## $ :List of 2 ## ..$ : logi TRUE ## ..$ : num 1 ## $ :function (x) ## ..- attr(*, "srcref")= 'srcref' int [1:8] 1 55 1 69 55 69 1 1 ## .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x558b9d3a8550> ``` --- ## Recursive lists Lists can contain other lists, meaning they don't have to be flat ```r str( list(1, list(2, list(3, 4), 5)) ) ``` ``` ## List of 2 ## $ : num 1 ## $ :List of 3 ## ..$ : num 2 ## ..$ :List of 2 ## .. ..$ : num 3 ## .. ..$ : num 4 ## ..$ : num 5 ``` .footnote[Because of this, lists become the most natural way of representing tree-like structures within R] --- ## List Coercion By default a vector will be coerced to a list (as a list is more general) if needed ```r str( c(1, list(4, list(6, 7))) ) ``` ``` ## List of 3 ## $ : num 1 ## $ : num 4 ## $ :List of 2 ## ..$ : num 6 ## ..$ : num 7 ``` -- We can coerce a list into an atomic vector using `unlist` - the usual type coercion rules then apply to determine the final type. ```r unlist(list(1:3, list(4:5, 6))) ``` ``` ## [1] 1 2 3 4 5 6 ``` ```r unlist( list(1, list(2, list(3, "Hello"))) ) ``` ``` ## [1] "1" "2" "3" "Hello" ``` .footnote[`as.intger` and similar functions can be used, but only if the list is flat (i.e. no nested lists)] --- ## Named lists Because of their more complex structure we often want to name the elements of a list (we can also do this with atomic vectors). This can make accessing list elements more straight forward. ```r str(list(A = 1, B = list(C = 2, D = 3))) ``` ``` ## List of 2 ## $ A: num 1 ## $ B:List of 2 ## ..$ C: num 2 ## ..$ D: num 3 ``` -- More complex names need to be quoted, ```r list("knock knock" = "who's there?") ``` ``` ## $`knock knock` ## [1] "who's there?" ``` --- ## Exercise 1 Represent the following JSON data as a list in R. ```json { "firstName": "John", "lastName": "Smith", "age": 25, "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": 10021 }, "phoneNumber": [ { "type": "home", "number": "212 555-1239" }, { "type": "fax", "number": "646 555-4567" } ] } ``` --- class: middle count: false # NULL Values --- ## `NULL`s `NULL` is a special value within R that represents nothing - it always has length zero and type `"NULL"` and cannot have any attributes. .pull-left[ ```r NULL ``` ``` ## NULL ``` ```r typeof(NULL) ``` ``` ## [1] "NULL" ``` ```r mode(NULL) ``` ``` ## [1] "NULL" ``` ```r length(NULL) ``` ``` ## [1] 0 ``` ] .pull-right[ ```r c() ``` ``` ## NULL ``` ```r c(NULL) ``` ``` ## NULL ``` ```r c(1, NULL, 2) ``` ``` ## [1] 1 2 ``` ```r c(NULL, TRUE, "A") ``` ``` ## [1] "TRUE" "A" ``` ] .footnote[Note - If you're familiar with SQL, its `NULL` is more like R's `NA` this `NULL`] --- ## 0-length coercion 0-length length coercion is a special case of length coercion when one of the arugments has length 0. In this case the longer vector's length is not used and result will have length 0. .pull-left[ ```r integer() + 1 ``` ``` ## numeric(0) ``` ```r log(numeric()) ``` ``` ## numeric(0) ``` ] .pull-right[ ```r logical() | TRUE ``` ``` ## logical(0) ``` ```r character() > "M" ``` ``` ## logical(0) ``` ] -- <br/> As a `NULL` values always have length 0, this coercion rule will apply (note type coercion is also occurring here) <div> .pull-left[ ```r NULL + 1 ``` ``` ## numeric(0) ``` <br/> ```r log(NULL) ``` ``` ## Error in log(NULL): non-numeric argument to mathematical function ``` ] .pull-right[ ```r NULL | TRUE ``` ``` ## logical(0) ``` ```r NULL > "M" ``` ``` ## logical(0) ``` ] </div> --- ## `NULL`s and comparison Given the previous issue, comparisons and conditional with `NULL`s can be problematic. ```r x = NULL ``` <br/> ```r if (x > 0) print("Hello") ``` ``` ## Error in if (x > 0) print("Hello"): argument is of length zero ``` -- <br/> ```r if (!is.null(x) & (x > 0)) print("Hello") ``` ``` ## Error in if (!is.null(x) & (x > 0)) print("Hello"): argument is of length zero ``` -- <br/> ```r if (!is.null(x) && (x > 0)) print("Hello") ``` .footnote[This is due to short circuit evaluation which occurs with `&&` and `||` but not `&` or `|`.] --- class: middle count: false # Attributes --- ## Attributes Attributes are metadata that can be attached to objects in R. Some are special, e.g. `class`, `comment`, `dim`, `dimnames`, `names`, etc., as they change the way in which the object behaves. -- Attributes are implemented as a named list that is attached to an object. They can be interacted with via the `attr` and `attributes` functions. ```r (x = c(L=1,M=2,N=3)) ``` ``` ## L M N ## 1 2 3 ``` -- .pull-left[ ```r attributes(x) ``` ``` ## $names ## [1] "L" "M" "N" ``` ```r str(attributes(x)) ``` ``` ## List of 1 ## $ names: chr [1:3] "L" "M" "N" ``` ] .pull-right[ ```r attr(x, "names") ``` ``` ## [1] "L" "M" "N" ``` ```r attr(x, "something") ``` ``` ## NULL ``` ] --- ## Assigning attributes ```r names(x) = c("Z","Y","X") x ``` ``` ## Z Y X ## 1 2 3 ``` -- ```r names(x) ``` ``` ## [1] "Z" "Y" "X" ``` -- ```r attr(x, "names") = c("A","B","C") x ``` ``` ## A B C ## 1 2 3 ``` -- ```r names(x) ``` ``` ## [1] "A" "B" "C" ``` --- ## Helpers functions vs attr .pull-left[ ```r names(x) = 1:3 x ``` ``` ## 1 2 3 ## 1 2 3 ``` ```r attributes(x) ``` ``` ## $names ## [1] "1" "2" "3" ``` ] .pull-right[ ```r names(x) = c(TRUE, FALSE, TRUE) x ``` ``` ## TRUE FALSE TRUE ## 1 2 3 ``` ```r attributes(x) ``` ``` ## $names ## [1] "TRUE" "FALSE" "TRUE" ``` ] -- <br/> ```r attr(x, "names") = 1:3 x ``` ``` ## 1 2 3 ## 1 2 3 ``` ```r attributes(x) ``` ``` ## $names ## [1] "1" "2" "3" ``` --- ## Factors Factor objects are how R represents categorical data (e.g. a variable where there are a fixed # of possible outcomes). ```r (x = factor(c("Sunny", "Cloudy", "Rainy", "Cloudy", "Cloudy"))) ``` ``` ## [1] Sunny Cloudy Rainy Cloudy Cloudy ## Levels: Cloudy Rainy Sunny ``` -- ```r str(x) ``` ``` ## Factor w/ 3 levels "Cloudy","Rainy",..: 3 1 2 1 1 ``` -- ```r typeof(x) ``` ``` ## [1] "integer" ``` ```r mode(x) ``` ``` ## [1] "numeric" ``` --- ## Composition A factor is just an integer vector with two attributes: `class` and `levels`. ```r x ``` ``` ## [1] Sunny Cloudy Rainy Cloudy Cloudy ## Levels: Cloudy Rainy Sunny ``` ```r attributes(x) ``` ``` ## $levels ## [1] "Cloudy" "Rainy" "Sunny" ## ## $class ## [1] "factor" ``` -- <br/> We can build our own factor from scratch using, ```r y = c(3L, 1L, 2L, 1L, 1L) attr(y, "levels") = c("Cloudy", "Rainy", "Sunny") attr(y, "class") = "factor" y ``` ``` ## [1] Sunny Cloudy Rainy Cloudy Cloudy ## Levels: Cloudy Rainy Sunny ``` --- ## Building objects The approach we just used is a bit clunky - generally the prefered method for construction an object with attributes from scratch is to use the `structure` function. ```r y = structure( c(3L, 1L, 2L, 1L, 1L), levels = c("Cloudy", "Rainy", "Sunny"), class = "factor" ) y ``` ``` ## [1] Sunny Cloudy Rainy Cloudy Cloudy ## Levels: Cloudy Rainy Sunny ``` ```r class(y) ``` ``` ## [1] "factor" ``` ```r is.factor(y) ``` ``` ## [1] TRUE ``` .footnote[Named arguments become attributes] --- Knowning factors are stored as integers help explain some of their more interesting behaviors: ```r x+1 ``` ``` ## Warning in Ops.factor(x, 1): '+' not meaningful for factors ``` ``` ## [1] NA NA NA NA NA ``` ```r is.integer(x) ``` ``` ## [1] FALSE ``` ```r as.integer(x) ``` ``` ## [1] 3 1 2 1 1 ``` ```r as.character(x) ``` ``` ## [1] "Sunny" "Cloudy" "Rainy" "Cloudy" "Cloudy" ``` ```r as.logical(x) ``` ``` ## [1] NA NA NA NA NA ``` --- class: middle count: false # S3 Object System --- ## `class` The `class` attribute is an additional layer to R's type hierarchy, <br/> value | `typeof()` | `mode()` | `class()` :-----------------|:-----------------|:---------------|:--------------- `TRUE` | logical | logical | logical `1` | double | numeric | numeric `1L` | integer | numeric | integer `"A"` | character | character | character `NULL` | NULL | NULL | NULL `list(1, "A")` | list | list | list `factor("A")` | integer | numeric | factor `function(x) x^2` | closure | function | function ` `+` ` | builtin | function | function ` `[` ` | special | function | function --- ## S3 class specialization ```r x = c("A","B","A","C") ``` -- ```r print( x ) ``` ``` ## [1] "A" "B" "A" "C" ``` -- ```r print( factor(x) ) ``` ``` ## [1] A B A C ## Levels: A B C ``` -- ```r print( unclass( factor(x) ) ) ``` ``` ## [1] 1 2 1 3 ## attr(,"levels") ## [1] "A" "B" "C" ``` -- ```r print.default( factor(x) ) ``` ``` ## [1] 1 2 1 3 ``` -- ```r print ``` ``` ## function (x, ...) ## UseMethod("print") ## <bytecode: 0x558b9d0893c8> ## <environment: namespace:base> ``` --- ## Other examples .pull-left[ ```r mean ``` ``` ## function (x, ...) ## UseMethod("mean") ## <bytecode: 0x558b9d5f13c0> ## <environment: namespace:base> ``` ```r t.test ``` ``` ## function (x, ...) ## UseMethod("t.test") ## <bytecode: 0x558b9b77d460> ## <environment: namespace:stats> ``` ] .pull-right[ ```r summary ``` ``` ## function (object, ...) ## UseMethod("summary") ## <bytecode: 0x558b9c3e1418> ## <environment: namespace:base> ``` ```r plot ``` ``` ## function (x, y, ...) ## UseMethod("plot") ## <bytecode: 0x558b9bf79960> ## <environment: namespace:base> ``` ] <br/> Not all base functions use this approach, ```r sum ``` ``` ## function (..., na.rm = FALSE) .Primitive("sum") ``` --- ## What is S3? <br/> > S3 is R’s first and simplest OO system. It is the only OO system used in the base and stats packages, and it’s the most commonly used system in CRAN packages. S3 is informal and ad hoc, but it has a certain elegance in its minimalism: you can’t take away any part of it and still have a useful OO system. >— Hadley Wickham, Advanced R .footnote[ * S3 should not be confused with R's other object oriented systems: S4, Reference classes, and R6*. ] --- ## What's going on? S3 objects and their related functions work using a very simple dispatch mechanism - a generic function is created whose sole job is to call the `UseMethod` function which then calls a class specialized function using the naming convention: `<generic>.<class>` -- We can see all of the specialized versions of the generic using the `methods` function. ```r methods("plot") ``` ``` ## [1] plot.acf* plot.data.frame* plot.decomposed.ts* ## [4] plot.default plot.dendrogram* plot.density* ## [7] plot.ecdf plot.factor* plot.formula* ## [10] plot.function plot.hclust* plot.histogram* ## [13] plot.HoltWinters* plot.isoreg* plot.lm* ## [16] plot.medpolish* plot.mlm* plot.ppr* ## [19] plot.prcomp* plot.princomp* plot.profile.nls* ## [22] plot.R6* plot.raster* plot.spec* ## [25] plot.stepfun plot.stl* plot.table* ## [28] plot.ts plot.tskernel* plot.TukeyHSD* ## see '?methods' for accessing help and source code ``` --- .small[ ```r methods("print") ``` ``` ## [1] print.acf* ## [2] print.AES* ## [3] print.anova* ## [4] print.aov* ## [5] print.aovlist* ## [6] print.ar* ## [7] print.Arima* ## [8] print.arima0* ## [9] print.AsIs ## [10] print.aspell* ## [11] print.aspell_inspect_context* ## [12] print.bibentry* ## [13] print.Bibtex* ## [14] print.browseVignettes* ## [15] print.bslib_fragment* ## [16] print.bslib_page* ## [17] print.by ## [18] print.changedFiles* ## [19] print.check_bogus_return* ## [20] print.check_code_usage_in_package* ## [21] print.check_compiled_code* ## [22] print.check_demo_index* ## [23] print.check_depdef* ## [24] print.check_details* ## [25] print.check_details_changes* ## [26] print.check_doi_db* ## [27] print.check_dotInternal* ## [28] print.check_make_vars* ## [29] print.check_nonAPI_calls* ## [30] print.check_package_code_assign_to_globalenv* ## [31] print.check_package_code_attach* ## [32] print.check_package_code_data_into_globalenv* ## [33] print.check_package_code_startup_functions* ## [34] print.check_package_code_syntax* ## [35] print.check_package_code_unload_functions* ## [36] print.check_package_compact_datasets* ## [37] print.check_package_CRAN_incoming* ## [38] print.check_package_datalist* ## [39] print.check_package_datasets* ## [40] print.check_package_depends* ## [41] print.check_package_description* ## [42] print.check_package_description_encoding* ## [43] print.check_package_license* ## [44] print.check_packages_in_dir* ## [45] print.check_packages_used* ## [46] print.check_po_files* ## [47] print.check_pragmas* ## [48] print.check_Rd_line_widths* ## [49] print.check_Rd_metadata* ## [50] print.check_Rd_xrefs* ## [51] print.check_RegSym_calls* ## [52] print.check_S3_methods_needing_delayed_registration* ## [53] print.check_so_symbols* ## [54] print.check_T_and_F* ## [55] print.check_url_db* ## [56] print.check_vignette_index* ## [57] print.checkDocFiles* ## [58] print.checkDocStyle* ## [59] print.checkFF* ## [60] print.checkRd* ## [61] print.checkRdContents* ## [62] print.checkReplaceFuns* ## [63] print.checkS3methods* ## [64] print.checkTnF* ## [65] print.checkVignettes* ## [66] print.citation* ## [67] print.codoc* ## [68] print.codocClasses* ## [69] print.codocData* ## [70] print.colorConverter* ## [71] print.compactPDF* ## [72] print.condition ## [73] print.connection ## [74] print.CRAN_package_reverse_dependencies_and_views* ## [75] print.css* ## [76] print.data.frame ## [77] print.Date ## [78] print.default ## [79] print.dendrogram* ## [80] print.density* ## [81] print.difftime ## [82] print.dist* ## [83] print.Dlist ## [84] print.DLLInfo ## [85] print.DLLInfoList ## [86] print.DLLRegisteredRoutines ## [87] print.dummy_coef* ## [88] print.dummy_coef_list* ## [89] print.ecdf* ## [90] print.eigen ## [91] print.factanal* ## [92] print.factor ## [93] print.family* ## [94] print.fileSnapshot* ## [95] print.findLineNumResult* ## [96] print.formula* ## [97] print.frame* ## [98] print.fseq* ## [99] print.ftable* ## [100] print.function ## [101] print.getAnywhere* ## [102] print.glm* ## [103] print.hclust* ## [104] print.help_files_with_topic* ## [105] print.hexmode ## [106] print.HoltWinters* ## [107] print.hsearch* ## [108] print.hsearch_db* ## [109] print.htest* ## [110] print.html* ## [111] print.html_dependency* ## [112] print.htmltools.selector* ## [113] print.htmltools.selector.list* ## [114] print.infl* ## [115] print.integrate* ## [116] print.isoreg* ## [117] print.json* ## [118] print.key_missing* ## [119] print.kmeans* ## [120] print.knitr_kable* ## [121] print.Latex* ## [122] print.LaTeX* ## [123] print.libraryIQR ## [124] print.listof ## [125] print.lm* ## [126] print.loadings* ## [127] print.loess* ## [128] print.logLik* ## [129] print.ls_str* ## [130] print.medpolish* ## [131] print.MethodsFunction* ## [132] print.mtable* ## [133] print.NativeRoutineList ## [134] print.news_db* ## [135] print.nls* ## [136] print.noquote ## [137] print.numeric_version ## [138] print.object_size* ## [139] print.octmode ## [140] print.packageDescription* ## [141] print.packageInfo ## [142] print.packageIQR* ## [143] print.packageStatus* ## [144] print.pairwise.htest* ## [145] print.person* ## [146] print.POSIXct ## [147] print.POSIXlt ## [148] print.power.htest* ## [149] print.ppr* ## [150] print.prcomp* ## [151] print.princomp* ## [152] print.proc_time ## [153] print.quosure* ## [154] print.quosures* ## [155] print.R6* ## [156] print.R6ClassGenerator* ## [157] print.raster* ## [158] print.Rd* ## [159] print.recordedplot* ## [160] print.restart ## [161] print.RGBcolorConverter* ## [162] print.rlang_box_done* ## [163] print.rlang_box_splice* ## [164] print.rlang_data_pronoun* ## [165] print.rlang_envs* ## [166] print.rlang_error* ## [167] print.rlang_fake_data_pronoun* ## [168] print.rlang_lambda_function* ## [169] print.rlang_trace* ## [170] print.rlang_zap* ## [171] print.rle ## [172] print.roman* ## [173] print.sass* ## [174] print.sass_bundle* ## [175] print.sass_layer* ## [176] print.scalar* ## [177] print.sessionInfo* ## [178] print.shiny.tag* ## [179] print.shiny.tag.env* ## [180] print.shiny.tag.list* ## [181] print.shiny.tag.query* ## [182] print.simple.list ## [183] print.smooth.spline* ## [184] print.socket* ## [185] print.srcfile ## [186] print.srcref ## [187] print.stepfun* ## [188] print.stl* ## [189] print.StructTS* ## [190] print.subdir_tests* ## [191] print.summarize_CRAN_check_status* ## [192] print.summary.aov* ## [193] print.summary.aovlist* ## [194] print.summary.ecdf* ## [195] print.summary.glm* ## [196] print.summary.lm* ## [197] print.summary.loess* ## [198] print.summary.manova* ## [199] print.summary.nls* ## [200] print.summary.packageStatus* ## [201] print.summary.ppr* ## [202] print.summary.prcomp* ## [203] print.summary.princomp* ## [204] print.summary.table ## [205] print.summary.warnings ## [206] print.summaryDefault ## [207] print.table ## [208] print.tables_aov* ## [209] print.terms* ## [210] print.ts* ## [211] print.tskernel* ## [212] print.TukeyHSD* ## [213] print.tukeyline* ## [214] print.tukeysmooth* ## [215] print.undoc* ## [216] print.vignette* ## [217] print.warnings ## [218] print.xfun_raw_string* ## [219] print.xfun_rename_seq* ## [220] print.xfun_strict_list* ## [221] print.xgettext* ## [222] print.xngettext* ## [223] print.xtabs* ## see '?methods' for accessing help and source code ``` ] --- ```r print.factor ``` ``` ## function (x, quote = FALSE, max.levels = NULL, width = getOption("width"), ## ...) ## { ## ord <- is.ordered(x) ## if (length(x) == 0L) ## cat(if (ord) ## "ordered" ## else "factor", "(0)\n", sep = "") ## else { ## xx <- character(length(x)) ## xx[] <- as.character(x) ## keepAttrs <- setdiff(names(attributes(x)), c("levels", ## "class")) ## attributes(xx)[keepAttrs] <- attributes(x)[keepAttrs] ## print(xx, quote = quote, ...) ## } ## maxl <- if (is.null(max.levels)) ## TRUE ## else max.levels ## if (maxl) { ## n <- length(lev <- encodeString(levels(x), quote = ifelse(quote, ## "\"", ""))) ## colsep <- if (ord) ## " < " ## else " " ## T0 <- "Levels: " ## if (is.logical(maxl)) ## maxl <- { ## width <- width - (nchar(T0, "w") + 3L + 1L + ## 3L) ## lenl <- cumsum(nchar(lev, "w") + nchar(colsep, ## "w")) ## if (n <= 1L || lenl[n] <= width) ## n ## else max(1L, which.max(lenl > width) - 1L) ## } ## drop <- n > maxl ## cat(if (drop) ## paste(format(n), ""), T0, paste(if (drop) ## c(lev[1L:max(1, maxl - 1)], "...", if (maxl > 1) lev[n]) ## else lev, collapse = colsep), "\n", sep = "") ## } ## if (!isTRUE(val <- .valid.factor(x))) ## warning(val) ## invisible(x) ## } ## <bytecode: 0x558b9c274d50> ## <environment: namespace:base> ``` --- ```r print.integer ``` ``` ## Error in eval(expr, envir, enclos): object 'print.integer' not found ``` -- .pad-top[] ```r print.default ``` ``` ## function (x, digits = NULL, quote = TRUE, na.print = NULL, print.gap = NULL, ## right = FALSE, max = NULL, width = NULL, useSource = TRUE, ## ...) ## { ## args <- pairlist(digits = digits, quote = quote, na.print = na.print, ## print.gap = print.gap, right = right, max = max, width = width, ## useSource = useSource, ...) ## missings <- c(missing(digits), missing(quote), missing(na.print), ## missing(print.gap), missing(right), missing(max), missing(width), ## missing(useSource)) ## .Internal(print.default(x, args, missings)) ## } ## <bytecode: 0x558b9bb6dd88> ## <environment: namespace:base> ``` --- ## The other way If instead we have a class and want to know what specialized functions exist for that class, then we can again use the `methods` function with the `class` argument. ```r methods(class="factor") ``` ``` ## [1] [ [[ [[<- [<- all.equal ## [6] as.character as.data.frame as.Date as.list as.logical ## [11] as.POSIXlt as.vector c coerce droplevels ## [16] format initialize is.na<- length<- levels<- ## [21] Math Ops plot print relevel ## [26] relist rep show slotsFromS3 summary ## [31] Summary xtfrm ## see '?methods' for accessing help and source code ``` --- ## Adding methods .pull-left[ ```r x = structure(c(1,2,3), class="class_A") x ``` ``` ## [1] 1 2 3 ## attr(,"class") ## [1] "class_A" ``` ] .pull-right[ ```r y = structure(c(6,5,4), class="class_B") y ``` ``` ## [1] 6 5 4 ## attr(,"class") ## [1] "class_B" ``` ] -- <div> .pull-left[ ```r print.class_A = function(x) { cat("Class A!\n") print.default(unclass(x)) } x ``` ``` ## Class A! ## [1] 1 2 3 ``` ] .pull-right[ ```r print.class_B = function(x) { cat("Class B!\n") print.default(unclass(x)) } y ``` ``` ## Class B! ## [1] 6 5 4 ``` ] </div> -- <div> .pull-left[ ```r class(x) = "class_B" x ``` ``` ## Class B! ## [1] 1 2 3 ``` ] .pull-right[ ```r class(y) = "class_A" y ``` ``` ## Class A! ## [1] 6 5 4 ``` ] </div> --- ## Defining a new S3 Generic ```r shuffle = function(x) { UseMethod("shuffle") } ``` -- ```r shuffle.default = function(x) { stop("Class ", class(x), " is not supported by shuffle.\n", call. = FALSE) } ``` -- ```r shuffle.factor = function(f) { factor( sample(as.character(f)), levels = sample(levels(f)) ) } shuffle.integer = function(x) { sample(x) } ``` -- .pull-left[ ```r shuffle( 1:10 ) ``` ``` ## [1] 1 9 3 6 2 8 7 5 10 4 ``` ```r shuffle( factor(c("A","B","C","A")) ) ``` ``` ## [1] A B C A ## Levels: C B A ``` ] .pull-right[ ```r shuffle( c(1, 2, 3, 4, 5) ) ``` ``` ## Error: Class numeric is not supported by shuffle. ``` ```r shuffle( letters[1:5] ) ``` ``` ## Error: Class character is not supported by shuffle. ``` ] --- ## Exercise 2 - classes, modes, and types Below we have defined an S3 method called `report`, it is designed to return a message about the type/mode/class of an object passed to it. .pull-left[ ```r report = function(x) { UseMethod("report") } report.default = function(x) { "This class does not have a method defined." } ``` ] .pull-right[ ```r report.integer = function(x) { "I'm an integer!" } report.double = function(x) { "I'm a double!" } report.numeric = function(x) { "I'm a numeric!" } ``` ] <br/> Try running the `report` function with different input types, what happens? Now run `rm("report.integer")` in your Console and try using the `report` function again, what has changed? What does this tell us about S3, types, modes, and classes? What if we also `rm("report.double")`? --- ## Conclusion? From `UseMethod`s R documentation: > If the object does not have a class attribute, it has an implicit class. Matrices and arrays have class "matrix" or "array" followed by the class of the underlying vector. Most vectors have class the result of `mode(x)`, except that integer vectors have class `c("integer", "numeric")` and real vectors have class `c("double", "numeric")`. From Advanced R: > How does UseMethod() work? It basically creates a vector of method names, paste0("generic", ".", c(class(x), "default")), and then looks for each potential method in turn. -- <br/> .center[ .large[Why?] See https://twitter.com/WhyDoesR ]