Formal class 'ext_gene' [package ".GlobalEnv"] with 5 slots
..@ gene :Formal class 'gene' [package ".GlobalEnv"] with 2 slots
.. .. ..@ name : chr(0)
.. .. ..@ coords: num(0)
..@ feature_name : chr "num_introns"
..@ feature_value: chr "5"
..@ name : chr "ANK3"
..@ coords : num [1:2] 1400000 1412000
Sealed Class
Preventing double class definition:
setClass('Not_Sealed')setClass('Not_Sealed')
But to prevent this:
setClass('Sealed', sealed = T)setClass('Sealed')
Error in setClass("Sealed"): "Sealed" has a sealed class definition and cannot be redefined
S4 class – slots
The variables within an S4 class are stored in the so-called slots. In the above example, we have 2 such slots: name and coords. Here is how to access them:
my.gene@name # access using @ operatormy.gene@coords[2] # access the 2nd element in slot coords
[1] "ANK3"
[1] 1412000
S4 class – methods
The power of classes lies in the fact that they define both the data types in particular slots and operations (functions) we can perform on them. Let us define a generic print function for an S4 class:
setMethod('print', 'gene', function(x) {cat('GENE: ', x@name, ' --> ')cat('[', x@coords, ']') })print(my.gene) # and we use the newly defined print
GENE: ANK3 --> [ 1400000 1412000 ]
S3 Classes
An S3 class object is one of R base types (e.g. integer) with class attribute set:
obj <-factor(c("a", "b", "c"))typeof(obj)
[1] "integer"
class(obj)
[1] "factor"
attributes(obj)
$levels
[1] "a" "b" "c"
$class
[1] "factor"
str(obj)
Factor w/ 3 levels "a","b","c": 1 2 3
print(obj)
[1] a b c
Levels: a b c
print(unclass(obj))
[1] 1 2 3
attr(,"levels")
[1] "a" "b" "c"
Custom str Methods
Some S3 classes provide a custom str, e.g.:
time <-strptime("2018-06-07", "%Y-%m-%d")
str(time)
POSIXlt[1:1], format: "2018-06-07"
str(unclass(time))
List of 11
$ sec : num 0
$ min : int 0
$ hour : int 0
$ mday : int 7
$ mon : int 5
$ year : int 118
$ wday : int 4
$ yday : int 157
$ isdst : int 0
$ zone : chr "UTC"
$ gmtoff: int NA
- attr(*, "tzone")= chr [1:3] "Etc/UTC" "UTC" "UTC"
Generic Methods and Method Dispatch
Have you ever wondered why print() or summary() work on many types (classes) of data?
They are so-called generics, i.e. functions and methods that operate on classes. They know which method to apply to which class thanks to the process of method dispatch.
The naming scheme for generics is: generic.class() i.e. a generic that applies to the class class.
Examples:
print.factor(),
print.default(),
print.data.frame().
To see the code of a method:
getS3method('summary', 'lm') %>%head(n =5)
1 function (object, correlation = FALSE, symbolic.cor = FALSE,
2 ...)
3 {
4 z <- object
5 p <- z$rank
Creating S3 Classes
To create an S3 class, simply give a name to a data structure:
gf <-structure(list(), class ='genomic_features')class(gf)
Fluent function interface — tidyverse functions take data x as the very first argument and return object similar to x so that they can be chained by %>%
validate_Animal <-function(x) { species <- x[1] age <-attr(x, 'age')if (is.na(species) || species =="") {stop('Species name is missing!', call. =FALSE) }if (!is.numeric(age) || age <1|| age >=100) {stop("Invalid age!", call. =FALSE) }return(x)}
Safe S3 – Helper
Animal <-function(x) { species <- x[[1]] age <- x[[2]]validate_Animal(new_Animal(species, age))}
dog <-Animal(list('Canis familiaris', 7))class(dog)
[1] "Animal"
cat <-Animal(list('Felis felis', '9'))
Error in new_Animal(species, age): is.numeric(age) is not TRUE
cat <-Animal(list('Felis felis', 9))class(cat)
[1] "Animal"
Building S3 Classes – Styles
One can build an S3 class on top of any existing base type, e.g. a named list:
point_in_space_class <-function(x, y, z) {structure(list(x = x, y = y, z = z ),class ="point_in_space_class" )}
Introduction to R6 classes
require(R6),
Do not rely on S4 like RC, but on S3,
Are faster than RC,
Do not do copy-on-modify,
Thus provide OO model similar to C++ or Java.
Methods belong to objects, not to generics.
R6 Class Example
library(R6)Person <-R6Class("Person",public =list(name =NULL,hair =NULL,initialize =function(name =NA, hair =NA) {stopifnot(is.character(name), is.character(hair)) self$name <- name self$hair <- hair self$greet() },set_hair =function(val) { self$hair <- val },greet =function() {cat(paste0("Hello, my name is ", self$name, ".\n")) } ))
R6 in Action
kate <- Person$new(name ='Kate', hair ='blond')str(kate)
Hello, my name is Kate.
Classes 'Person', 'R6' <Person>
Public:
clone: function (deep = FALSE)
greet: function ()
hair: blond
initialize: function (name = NA, hair = NA)
name: Kate
set_hair: function (val)
R6 – methods
kate$greet()kate$set_hair('red')kate
Hello, my name is Kate.
<Person>
Public:
clone: function (deep = FALSE)
greet: function ()
hair: red
initialize: function (name = NA, hair = NA)
name: Kate
set_hair: function (val)
R6 copy-on-modify
kate$hair
[1] "red"
ann <- kateann$set_hair('blue')ann$hair
[1] "blue"
kate$hair
[1] "blue"
R6 – clone()
kate$set_hair('brown')kate$hair
[1] "brown"
ann <- kate$clone()kate$hairann$hair
[1] "brown"
[1] "brown"
ann$set_hair('blond')kate$hairann$hair
[1] "brown"
[1] "blond"
Thank you! Questions?
_
platform x86_64-pc-linux-gnu
os linux-gnu
major 4
minor 2.3