Lists, Attributes, & S3

Lecture 03

Dr. Colin Rundel

Generic Vectors

Lists

Lists are the other vector data structure in R, they differ from atomic vectors in that they can contain a heterogeneous collection of R object (e.g. atomic vectors, other lists, functions, etc.).

list("A", c(TRUE,FALSE), (1:4)/2, list(1L), 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] 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, particularly for lists.

str(c(1,2))
 num [1:2] 1 2
str(1:100)
 int [1:100] 1 2 3 4 5 6 7 8 9 10 ...
str("A")
 chr "A"
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)  

Recursive lists

Lists can contain other lists, meaning they don’t have to be flat

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

List Coercion

By default an atomic vector concatenated with a list will be coerced to part of the list (as the list is more generic)

str( c(1, list(4, list(6, 7))) )
List of 3
 $ : num 1
 $ : num 4
 $ :List of 2
  ..$ : num 6
  ..$ : num 7
str( list(1, list(4, list(6, 7))) )
List of 2
 $ : num 1
 $ :List of 2
  ..$ : num 4
  ..$ :List of 2
  .. ..$ : num 6
  .. ..$ : num 7

We can coerce a list into an atomic vector using unlist() - typical atomic type coercion rules then apply to determine the final type.

unlist(list(1:3, list(4:5, 6)))
[1] 1 2 3 4 5 6
unlist( list(1, list(2, list(3, "Hello"))) )
[1] "1"     "2"     "3"     "Hello"

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 and avoids the use of magic numbers - more on this next lecture.

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 (i.e. non-valid object names) must be quoted,

list("knock knock" = "who's there?")
$`knock knock`
[1] "who's there?"

Aside - Variable names vs. value names

We have seen how to assign a name to an R object (via = or <-). The general rule for these names is that it must start with a letter (upper or lower) or a . and then be followed additional letters, numbers, . or _.

These names are unambiguous to the interpreter / parser and so do not need any additional decoration. However if you want to use a name that does not follow these rules, then you must quote it using backticks.

a b = 1
a b
Error in parse(text = input): <text>:1:3: unexpected symbol
1: a b
      ^
"a b" = 1
"a b"
[1] "a b"
`a b` = 1
`a b`
[1] 1

Exercise 1

Represent the following JSON data as a list in R.

{
  "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"
  } ]
}
03:00

NULL Values

NULLs

NULL is a special value / object within R that represents nothing - it always has length zero and a type and mode of "NULL". It also cannot have any attributes.

NULL
NULL
typeof(NULL)
[1] "NULL"
mode(NULL)
[1] "NULL"
length(NULL)
[1] 0
c()
NULL
c(NULL)
NULL
c(1, NULL, 2)
[1] 1 2
c(NULL, TRUE, "A")
[1] "TRUE" "A"   
double()
numeric(0)
length(double())
[1] 0
typeof(double())
[1] "double"
mode(double())
[1] "numeric"

0-length coercion

0-length length coercion is a special case of length coercion when one of the arguments has length 0.

In this special case the longer vector will have its length coerced to 0 resulting in a 0-length vector being returned.

integer() + 1
numeric(0)
log(numeric())
numeric(0)
logical() | TRUE
logical(0)
character() > "M"
logical(0)

As a NULL values always have length 0, this rule will apply most of the time (note the types)

NULL + 1
numeric(0)
NULL | TRUE
logical(0)
NULL > "M"
logical(0)
log(NULL)
Error in log(NULL): non-numeric argument to mathematical function

NULLs and comparison

Given the previous issue, comparisons and conditionals with NULLs can be problematic.

x = NULL
if (x > 0)
  print("Hello")
Error in if (x > 0) print("Hello"): argument is of length zero
if (!is.null(x) & (x > 0))
  print("Hello")
Error in if (!is.null(x) & (x > 0)) print("Hello"): argument is of length zero
if (!is.null(x) && (x > 0))
  print("Hello")

The null coalescing operator (%||%)

The %||% operator is a useful infix operator that returns the left hand side if it is not NULL and the right hand side otherwise.

`%||%`
function (x, y) 
if (is.null(x)) y else x
<bytecode: 0x125190cd8>
<environment: namespace:base>
if (x %||% 0 > 0)
  print("Hello")
if (x %||% 1 > 0)
  print("Hello")
[1] "Hello"

Attributes

Attributes

Attributes are metadata that can be attached to objects in R.

Certain attributes are special ( e.g. class, comment, dim, dimnames, names, …) because they change the behavior of the object(s).

Attributes are implemented as a named list that is attached to the object. They can be interacted with via the attr() and attributes() functions.

(x = c(L=1, M=2, N=3))
L M N 
1 2 3 
str(attributes(x))
List of 1
 $ names: chr [1:3] "L" "M" "N"
attr(x, "names")
[1] "L" "M" "N"
attr(x, "other")
NULL

Assigning attributes

The most commonly used / important attributes will usually have helper functions for getting and setting,

x
L M N 
1 2 3 
names(x)
[1] "L" "M" "N"
names(x) = c("Z","Y","X")
x
Z Y X 
1 2 3 
names(x)
[1] "Z" "Y" "X"
attr(x, "names") = c("A","B","C")
x
A B C 
1 2 3 
names(x)
[1] "A" "B" "C"

Helpers functions vs attr

names(x) = 1:3
x
1 2 3 
1 2 3 
attributes(x)
$names
[1] "1" "2" "3"
names(x) = c(TRUE, FALSE, TRUE)
x
 TRUE FALSE  TRUE 
    1     2     3 
attributes(x)
$names
[1] "TRUE"  "FALSE" "TRUE" 
attr(x, "names") = 1:3
x
1 2 3 
1 2 3 
attributes(x)
$names
[1] "1" "2" "3"

Factors

Factor objects are how R represents categorical data (e.g. a variable where there is a discrete set of possible outcomes).

(x = factor(c("Sunny", "Cloudy", "Rainy", "Cloudy", "Cloudy")))
[1] Sunny  Cloudy Rainy  Cloudy Cloudy
Levels: Cloudy Rainy Sunny
str(x)
 Factor w/ 3 levels "Cloudy","Rainy",..: 3 1 2 1 1
typeof(x)
[1] "integer"
mode(x)
[1] "numeric"
class(x)
[1] "factor"

Composition

A factor is just an integer vector with two attributes: class and levels.

x
[1] Sunny  Cloudy Rainy  Cloudy Cloudy
Levels: Cloudy Rainy Sunny
str(attributes(x))
List of 2
 $ levels: chr [1:3] "Cloudy" "Rainy" "Sunny"
 $ class : chr "factor"

We can build our own factor from scratch using attr(),

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 preferred method for construction an object with attributes from scratch is to use the structure() function.

( y = structure(
    c(3L, 1L, 2L, 1L, 1L),
    levels = c("Cloudy", "Rainy", "Sunny"),
    class = "factor"
) )
[1] Sunny  Cloudy Rainy  Cloudy Cloudy
Levels: Cloudy Rainy Sunny
class(y)
[1] "factor"
is.factor(y)
[1] TRUE

Factors are integer vectors?

Knowing factors are stored as integers help explain some of their more interesting behaviors:

x+1
Warning in Ops.factor(x, 1): '+' not meaningful for factors
[1] NA NA NA NA NA
is.integer(x)
[1] FALSE
as.integer(x)
[1] 3 1 2 1 1
as.character(x)
[1] "Sunny"  "Cloudy" "Rainy"  "Cloudy" "Cloudy"
as.logical(x)
[1] NA NA NA NA NA

S3 Object System

class

The class attribute is an additional layer to R’s type hierarchy,

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

x = c("A","B","A","C")
print( x )
[1] "A" "B" "A" "C"
print( factor(x) )
[1] A B A C
Levels: A B C
print( unclass( factor(x) ) )
[1] 1 2 1 3
attr(,"levels")
[1] "A" "B" "C"
print.default( factor(x) )
[1] 1 2 1 3

What’s up with print?

print
function (x, ...) 
UseMethod("print")
<bytecode: 0x10576b358>
<environment: namespace:base>
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: 0x106ca3d50>
<environment: namespace:base>

Other examples

mean
function (x, ...) 
UseMethod("mean")
<bytecode: 0x105d117a0>
<environment: namespace:base>
t.test
function (x, ...) 
UseMethod("t.test")
<bytecode: 0x10630a280>
<environment: namespace:stats>
summary
function (object, ...) 
UseMethod("summary")
<bytecode: 0x1201c6668>
<environment: namespace:base>
plot
function (x, y, ...) 
UseMethod("plot")
<bytecode: 0x120219ba0>
<environment: namespace:base>

A lot but not all base functions use this approach,

sum
function (..., na.rm = FALSE)  .Primitive("sum")

What is S3?


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

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.

methods("plot")
 [1] plot.acf*           plot.colors*        plot.data.frame*   
 [4] plot.decomposed.ts* plot.default        plot.dendrogram*   
 [7] plot.density*       plot.ecdf           plot.factor*       
[10] plot.formula*       plot.function       plot.hclust*       
[13] plot.histogram*     plot.HoltWinters*   plot.isoreg*       
[16] plot.lm*            plot.medpolish*     plot.mlm*          
[19] plot.ppr*           plot.prcomp*        plot.princomp*     
[22] plot.profile*       plot.profile.nls*   plot.raster*       
[25] plot.spec*          plot.stepfun        plot.stl*          
[28] plot.table*         plot.ts             plot.tskernel*     
[31] plot.TukeyHSD*     
see '?methods' for accessing help and source code

Other examples

methods("print")
  [1] print.acf*                                          
  [2] print.activeConcordance*                            
  [3] print.AES*                                          
  [4] print.anova*                                        
  [5] print.aov*                                          
  [6] print.aovlist*                                      
  [7] print.ar*                                           
  [8] print.Arima*                                        
  [9] print.arima0*                                       
 [10] print.AsIs                                          
 [11] print.aspell*                                       
 [12] print.aspell_inspect_context*                       
 [13] print.bibentry*                                     
 [14] print.Bibtex*                                       
 [15] print.browseVignettes*                              
 [16] print.by                                            
 [17] print.changedFiles*                                 
 [18] print.check_bogus_return*                           
 [19] print.check_code_usage_in_package*                  
 [20] print.check_compiled_code*                          
 [21] print.check_demo_index*                             
 [22] print.check_depdef*                                 
 [23] print.check_details*                                
 [24] print.check_details_changes*                        
 [25] print.check_doi_db*                                 
 [26] print.check_dotInternal*                            
 [27] print.check_make_vars*                              
 [28] print.check_nonAPI_calls*                           
 [29] print.check_package_code_assign_to_globalenv*       
 [30] print.check_package_code_attach*                    
 [31] print.check_package_code_data_into_globalenv*       
 [32] print.check_package_code_startup_functions*         
 [33] print.check_package_code_syntax*                    
 [34] print.check_package_code_unload_functions*          
 [35] print.check_package_compact_datasets*               
 [36] print.check_package_CRAN_incoming*                  
 [37] print.check_package_datalist*                       
 [38] print.check_package_datasets*                       
 [39] print.check_package_depends*                        
 [40] print.check_package_description*                    
 [41] print.check_package_description_encoding*           
 [42] print.check_package_license*                        
 [43] print.check_packages_in_dir*                        
 [44] print.check_packages_used*                          
 [45] print.check_po_files*                               
 [46] print.check_pragmas*                                
 [47] print.check_Rd_line_widths*                         
 [48] print.check_Rd_metadata*                            
 [49] print.check_Rd_xrefs*                               
 [50] print.check_RegSym_calls*                           
 [51] print.check_S3_methods_needing_delayed_registration*
 [52] print.check_so_symbols*                             
 [53] print.check_T_and_F*                                
 [54] print.check_url_db*                                 
 [55] print.check_vignette_index*                         
 [56] print.checkDocFiles*                                
 [57] print.checkDocStyle*                                
 [58] print.checkFF*                                      
 [59] print.checkRd*                                      
 [60] print.checkRdContents*                              
 [61] print.checkReplaceFuns*                             
 [62] print.checkS3methods*                               
 [63] print.checkTnF*                                     
 [64] print.checkVignettes*                               
 [65] print.citation*                                     
 [66] print.cli_ansi_html_style*                          
 [67] print.cli_ansi_string*                              
 [68] print.cli_ansi_style*                               
 [69] print.cli_boxx*                                     
 [70] print.cli_diff_chr*                                 
 [71] print.cli_doc*                                      
 [72] print.cli_progress_demo*                            
 [73] print.cli_rule*                                     
 [74] print.cli_sitrep*                                   
 [75] print.cli_spark*                                    
 [76] print.cli_spinner*                                  
 [77] print.cli_tree*                                     
 [78] print.codoc*                                        
 [79] print.codocClasses*                                 
 [80] print.codocData*                                    
 [81] print.colorConverter*                               
 [82] print.colors*                                       
 [83] print.compactPDF*                                   
 [84] print.condition                                     
 [85] print.connection                                    
 [86] print.CRAN_package_reverse_dependencies_and_views*  
 [87] print.data.frame                                    
 [88] print.Date                                          
 [89] print.default                                       
 [90] print.dendrogram*                                   
 [91] print.density*                                      
 [92] print.difftime                                      
 [93] print.dist*                                         
 [94] print.Dlist                                         
 [95] print.DLLInfo                                       
 [96] print.DLLInfoList                                   
 [97] print.DLLRegisteredRoutines                         
 [98] print.dummy_coef*                                   
 [99] print.dummy_coef_list*                              
[100] print.ecdf*                                         
[101] print.eigen                                         
[102] print.evaluate_evaluation*                          
[103] print.factanal*                                     
[104] print.factor                                        
[105] print.family*                                       
[106] print.fileSnapshot*                                 
[107] print.findLineNumResult*                            
[108] print.formula*                                      
[109] print.ftable*                                       
[110] print.function                                      
[111] print.getAnywhere*                                  
[112] print.glm*                                          
[113] print.hashtab*                                      
[114] print.hclust*                                       
[115] print.help_files_with_topic*                        
[116] print.hexmode                                       
[117] print.HoltWinters*                                  
[118] print.hsearch*                                      
[119] print.hsearch_db*                                   
[120] print.htest*                                        
[121] print.html*                                         
[122] print.html_dependency*                              
[123] print.htmltools.selector*                           
[124] print.htmltools.selector.list*                      
[125] print.infl*                                         
[126] print.integrate*                                    
[127] print.isoreg*                                       
[128] print.json*                                         
[129] print.key_missing*                                  
[130] print.kmeans*                                       
[131] print.knitr_kable*                                  
[132] print.Latex*                                        
[133] print.LaTeX*                                        
[134] print.libraryIQR                                    
[135] print.listof                                        
[136] print.lm*                                           
[137] print.loadings*                                     
[138] print.loess*                                        
[139] print.logLik*                                       
[140] print.ls_str*                                       
[141] print.medpolish*                                    
[142] print.MethodsFunction*                              
[143] print.mtable*                                       
[144] print.NativeRoutineList                             
[145] print.news_db*                                      
[146] print.nls*                                          
[147] print.noquote                                       
[148] print.numeric_version                               
[149] print.object_size*                                  
[150] print.octmode                                       
[151] print.packageDescription*                           
[152] print.packageInfo                                   
[153] print.packageIQR*                                   
[154] print.packageStatus*                                
[155] print.paged_df*                                     
[156] print.pairwise.htest*                               
[157] print.person*                                       
[158] print.POSIXct                                       
[159] print.POSIXlt                                       
[160] print.power.htest*                                  
[161] print.ppr*                                          
[162] print.prcomp*                                       
[163] print.princomp*                                     
[164] print.proc_time                                     
[165] print.quosure*                                      
[166] print.quosures*                                     
[167] print.raster*                                       
[168] print.Rconcordance*                                 
[169] print.Rd*                                           
[170] print.recordedplot*                                 
[171] print.restart                                       
[172] print.RGBcolorConverter*                            
[173] print.RGlyphFont*                                   
[174] print.rlang_box_done*                               
[175] print.rlang_box_splice*                             
[176] print.rlang_data_pronoun*                           
[177] print.rlang_dict*                                   
[178] print.rlang_dyn_array*                              
[179] print.rlang_envs*                                   
[180] print.rlang_error*                                  
[181] print.rlang_fake_data_pronoun*                      
[182] print.rlang_lambda_function*                        
[183] print.rlang_message*                                
[184] print.rlang_trace*                                  
[185] print.rlang_warning*                                
[186] print.rlang_zap*                                    
[187] print.rlang:::list_of_conditions*                   
[188] print.rle                                           
[189] print.rlib_bytes*                                   
[190] print.rlib_error_3_0*                               
[191] print.rlib_trace_3_0*                               
[192] print.roman*                                        
[193] print.scalar*                                       
[194] print.sessionInfo*                                  
[195] print.shiny.tag*                                    
[196] print.shiny.tag.env*                                
[197] print.shiny.tag.list*                               
[198] print.shiny.tag.query*                              
[199] print.simple.list                                   
[200] print.smooth.spline*                                
[201] print.socket*                                       
[202] print.srcfile                                       
[203] print.srcref                                        
[204] print.stepfun*                                      
[205] print.stl*                                          
[206] print.StructTS*                                     
[207] print.subdir_tests*                                 
[208] print.summarize_CRAN_check_status*                  
[209] print.summary.aov*                                  
[210] print.summary.aovlist*                              
[211] print.summary.ecdf*                                 
[212] print.summary.glm*                                  
[213] print.summary.lm*                                   
[214] print.summary.loess*                                
[215] print.summary.manova*                               
[216] print.summary.nls*                                  
[217] print.summary.packageStatus*                        
[218] print.summary.ppr*                                  
[219] print.summary.prcomp*                               
[220] print.summary.princomp*                             
[221] print.summary.table                                 
[222] print.summary.warnings                              
[223] print.summaryDefault                                
[224] print.table                                         
[225] print.tables_aov*                                   
[226] print.terms*                                        
[227] print.ts*                                           
[228] print.tskernel*                                     
[229] print.TukeyHSD*                                     
[230] print.tukeyline*                                    
[231] print.tukeysmooth*                                  
[232] print.undoc*                                        
[233] print.vignette*                                     
[234] print.warnings                                      
[235] print.xfun_md_viewable*                             
[236] print.xfun_raw_string*                              
[237] print.xfun_record_results*                          
[238] print.xfun_rename_seq*                              
[239] print.xfun_strict_list*                             
[240] print.xgettext*                                     
[241] print.xngettext*                                    
[242] print.xtabs*                                        
see '?methods' for accessing help and source code
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", "()\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 <- max.levels %||% TRUE
    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: 0x107a744a8>
<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.

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

( x = structure(
    c(1,2,3), 
    class="class_A") )
[1] 1 2 3
attr(,"class")
[1] "class_A"
( y = structure(
    c(6,5,4), 
    class="class_B") )
[1] 6 5 4
attr(,"class")
[1] "class_B"
print.class_A = function(x) {
  cat("(Class A) ")
  print.default(unclass(x))
}
print(x)
(Class A) [1] 1 2 3
print.class_B = function(x) {
  cat("(Class B) ")
  print.default(unclass(x))
}
print(y)
(Class B) [1] 6 5 4
class(x) = "class_B"
print(x)
(Class B) [1] 1 2 3
class(y) = "class_A"
print(y)
(Class A) [1] 6 5 4

Defining a new S3 Generic

shuffle = function(x) {
  UseMethod("shuffle")
}
shuffle.default = function(x) {
  stop("Class ", class(x), " is not supported by shuffle.", call. = FALSE)
}
shuffle.factor = function(f) {
  factor( sample(as.character(f)), levels = sample(levels(f)) )
}
shuffle.integer = function(x) {
  sample(x)
}

Shuffle results

shuffle( 1:10 )
 [1]  8  5  4  7  6 10  2  9  3  1
shuffle( factor(c("A","B","C","A")) )
[1] B A C A
Levels: B C A
shuffle( c(1, 2, 3, 4, 5) )
Error: Class numeric is not supported by shuffle.
shuffle( letters[1:5] )
Error: Class character is not supported by shuffle.
shuffle( factor(letters[1:5]) )
[1] d b c e a
Levels: c a d b e

Demo - classes, modes, and types

report = function(x) {
  UseMethod("report")
}
report.default = function(x) {
  paste0("Class ", class(x)," does not have a method defined.")
}
report.integer = function(x) {
 "I'm an integer!"
}
report.double = function(x) {
  "I'm a double!"
}
report.numeric = function(x) {
  "I'm a numeric!"
}

#rm(report.integer)
#rm(report.double)
#rm(report.numeric)

report(1)
report(1L)
report("1")

On the left 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.

  • Try running the report function with different input types, what happens?

  • Now uncomment out the code on line 17 and try rerunning the code, what has changed?

  • What does this tell us about S3, types, modes, and classes?

  • What if we also uncomment the code on line 18?

03:00

Conclusions?

From UseMethods 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.


Why?