I'm trying to understand how to turn functions into object oriented programming in R. So for example, how could the data and 2 functions below be turned into one object using S3 (and then S4)? (Perhaps some other simple data and functions would serve better as examples?)
data <- c(1, 2, 3)
# Function 1
adding_1 <- function(x){
x <- x+1
}
# Function 2
subtracting_1 <- function(x){
x <- x-1
}
And how would below functions be executed using the OOP.
data1 <- adding_1(data)
data1
data2 <- subtracting_1(data)
data2
In R, object-oriented programming is implemented in several very different ways.
The S3 type of OO is the most used because it's very simple and yet does a good job at having what seems to be the same functions behave differently with different types of objects. A good reference is Advanced R by Hadley Wickham.
In R objects have attributes
. One of these attributes is the special class
attribute. You can see this with
x <- 1:3
y <- c(1, 2, 3)
class(x) # "integer"
class(y) # "numeric"
The S3 system is a function overloading system. A special function is defined, the generic. Then other functions, the methods, are defined to process objects depending or their classes. A method that must be defined is the default method.
Here I use your example to define first a generic and then the default method.
# Function 1
adding_1 <- function(x, ...) UseMethod("adding_1")
adding_1.default <- function(x, ...){
x <- x + 1
x
}
Now the methods for objects of class "list"
and "data.frame"
.
adding_1.list <- function(x, ...){
num <- sapply(x, is.numeric)
x[num] <- lapply(x[num], adding_1)
x
}
adding_1.data.frame <- function(x, ...){
num <- sapply(x, is.numeric)
x[num] <- lapply(x[num], adding_1)
x
}
And the same for subtracting_1
.
# Function 2
subtracting_1 <- function(x, ...) UseMethod("subtracting_1")
subtracting_1.default <- function(x){
x <- x - 1
x
}
subtracting_1.list <- function(x, ...){
num <- sapply(x, is.numeric)
x[num] <- lapply(x[num], subtracting_1)
x
}
subtracting_1.data.frame <- function(x, ...){
num <- sapply(x, is.numeric)
x[num] <- lapply(x[num], subtracting_1)
x
}
Test cases.
When called with x
as an argument (or y
above) it's the default method that is called since there is no adding_1.integer
nor adding_1.numeric
.
And the same goes for mat
.
But when called with a data frame, a special processing is needed in order not to have the function try to add 1
to character strings or other types of non-numeric column vectors that might be in the data frame.
mat <- matrix(1:6, 3)
df1 <- data.frame(x = letters[1:5], y = rnorm(5), z = 101:105)
adding_1(x)
adding_1(mat)
adding_1(df1)
subtracting_1(x)
subtracting_1(mat)
subtracting_1(df1)