0% found this document useful (0 votes)
66 views

Functions: FRE6871 & FRE7241, Fall 2020

The document discusses functions in R. It describes that R functions have formal arguments, a body, and an environment. Functions return the last statement evaluated by default unless a return statement is used. Functions can have default values assigned to arguments. An example function calc_skew() is provided to calculate skew of a time series of returns.

Uploaded by

Jerzy Pawlowski
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
66 views

Functions: FRE6871 & FRE7241, Fall 2020

The document discusses functions in R. It describes that R functions have formal arguments, a body, and an environment. Functions return the last statement evaluated by default unless a return statement is used. Functions can have default values assigned to arguments. An example function calc_skew() is provided to calculate skew of a time series of returns.

Uploaded by

Jerzy Pawlowski
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 57

Functions

FRE6871 & FRE7241, Fall 2020

Jerzy Pawlowski jp3900@nyu.edu

NYU Tandon School of Engineering

October 23, 2020

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 1 / 56


Functions Functions in R

Functions in R
R functions have three components: > # Define a function with two arguments
> test_func <- function(first_arg, second_arg) { # Body
a list of formal arguments, + first_arg + second_arg # Returns last evaluated statement
+ } # end test_func
a body containing R code, >
> test_func(1, 2) # Apply the function
> args(test_func) # Display argument
an environment, >
> # Define function that uses variable from enclosure environment
An R function plus its environment is > test_func <- function(first_arg, second_arg) {
referred to as a function closures. + first_arg + second_arg + glob_var
+ } # end test_func
>
The function body should be enclosed > test_func(3, 2) # error - glob_var doesn't exist yet!
in curly braces {}, unless it contains a > glob_var <- 10 # Create glob_var
> test_func(3, 2) # Now works
single command, then it doesn’t have
to enclosed.
The function body doesn’t require a
return statement, since by default R
functions return the last statement
evaluated in the body.
args() displays the formal arguments
of a function.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 2 / 56


Functions Return Values of Functions

Return Values of Functions


The function body doesn’t require a > # Define function that returns NULL for non-numeric argument
> test_func <- function(in_put) {
return statement, since by default R + if (!is.numeric(in_put)) {
functions return the last statement + warning(paste("argument", in_put, "isn't numeric"))
+ return(NULL)
evaluated in the body. + }
+ 2*in_put
return() statements are inserted in + } # end test_func
>
logical branches to terminate function > test_func(2)
execution and return its intended > test_func("hello")

value.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 3 / 56


Functions Functions That Return invisible

Functions That Return invisible


If a return value is wrapped in the > # Define a function that returns invisibly
> return_invisible <- function(in_put) {
function invisible() then the return + invisible(in_put)
value isn’t printed. + } # end return_invisible
>
But if the function is assigned to a >
>
return_invisible(2)

variable, then its return value is > glob_var <- return_invisible(2)


> glob_var
assigned to that variable. >
> rm(list=ls()) # Remove all objects
invisible() allows creating functions > # Load objects from file
whose return values can be assigned, > loaded <- load(file="C:/Develop/data/my_data.RData")
> loaded # Vector of loaded objects
but which do not print when they’re > ls() # List objects
not assigned.
The function load() reads data from
.RData files, and invisibly returns a
vector of names of objects created in
the workspace.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 4 / 56


Functions Binding Function Arguments

Binding Function Arguments


The formal arguments of a function > test_func <- function(first_arg, second_arg) {
+ # Last statement of function is return value
are defined in its argument list. + first_arg + 2*second_arg
+ } # end test_func
When a function is called, it’s passed > test_func(first_arg=3, second_arg=2) # Bind by name
a list of actual function arguments. > test_func(first=3, second=2) # Partial name binding
> test_func(3, 2) # Bind by position
> test_func(second_arg=2, 3) # mixed binding
Formal arguments can be bound to > test_func(3, 2, 1) # Too many arguments
actual arguments either by name or by > test_func(2) # Not enough arguments
position: All the actual arguments must be bound to formal
by name: formal arguments are arguments, and if not then an "unused argument" error
bound to actual arguments with is produced.
the same name, If there aren’t enough formal arguments, then an
by position: the first formal "argument is missing" error is produced,
argument is bound to the first
actual argument, etc.
Binding by name takes precedence
over binding by position: first all the
named arguments are bound, then the
remaining arguments are bound by
position.
Partial argument names are bound to
full names.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 5 / 56


Functions Default Values for Arguments

Default Values for Arguments


Formal arguments may be assigned > # Function "paste" has two arguments with default values
> str(paste)
default values, so that when the actual > # Default values of arguments can be specified in argument list
arguments are missing then their > test_func <- function(first_arg, fac_tor=1) {
+ fac_tor*first_arg
default values are used instead. + } # end test_func
> test_func(3) # Default value used for second argument
Default values are often assigned to > test_func(3, 2) # Default value over-ridden
> # Default values can be a vector of strings
function parameters, that determine > test_func <- function(in_put=c("first_val", "second_val")) {
the function’s behavior. + in_put <- match.arg(in_put) # Match to arg list
+ in_put
Default values can be specified as a + } # end test_func
> test_func("second_val")
vector of strings, representing the > test_func("se") # Partial name binding
> test_func("some_val") # Invalid string
possible values of a function’s
parameter.
The function match.arg() matches a
string to one of the possible values,
and returns the matched value, or
produces an error if it can’t match it.
The function str() displays the
structure of an R object, for example a
function name and its formal
arguments.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 6 / 56


Functions Function for Calculating Skew

Function for Calculating Skew


R provides an easy way for users to write > # DAX percentage returns
> re_turns <- rutils::diff_it(log(EuStockMarkets[, 1]))
functions. > # calc_skew() calculates skew of time series of returns
> # Default is normal time series
Formal function arguments can be bound to > calc_skew <- function(re_turns=rnorm(1000)) {
input variables by position or by name. +
+
# Number of observations
n_rows <- NROW(re_turns)
+ # Standardize re_turns
If the function arguments are missing then their + re_turns <- (re_turns - mean(re_turns))/sd(re_turns)
default value is used. + # Calculate skew - last statement automatically returned
+ n_rows*sum(re_turns^3)/((n_rows-1)*(n_rows-2))
Functions return the value of the last expression + } # end calc_skew
>
that is evaluated. > # Calculate skew of DAX returns
> # Bind arguments by name
datasets is a base package containing various > calc_skew(re_turns=re_turns)
> # Bind arguments by position
datasets, for example: EuStockMarkets. > calc_skew(re_turns)
> # Use default value of arguments
The EuStockMarkets dataset contains daily > calc_skew()
closing prices of european stock indices.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 7 / 56


Functions The dots "..." Function Argument

The dots "..." Function Argument


The dots "..." function argument is > str(plot) # Dots for additional plot parameters
> bind_dots <- function(in_put, ...) {
a formal argument without a name, as + paste0("in_put=", in_put,
opposed to the other formal + ", dots=", paste(..., sep=", "))
+ } # end bind_dots
arguments which all have names. > bind_dots(1, 2, 3) # "in_put" bound by position
> bind_dots(2, in_put=1, 3) # "in_put" bound by name
The dots "..." bind with any number > bind_dots(1, 2, 3, foo=10) # Named argument bound to dots
> bind_dots <- function(arg1, arg2, ...) {
of additional arguments, that aren’t + arg1 + 2*arg2 + sum(...)
already bound by name or position to + } # end bind_dots
> bind_dots(3, 2) # Bind arguments by position
the named arguments. > bind_dots(3, 2, 5, 8) # Extra arguments bound to dots

The dots "..." are used when the


number of arguments isn’t known in
advance, and allows functions to
accept an indefinite number of
arguments.
The dots "..." are sometimes placed
after the named arguments, to allow
passing of additional parameters into a
function.
Functionals often place the dots "..."
argument after the named arguments,
to allow passing the dots "..." to the
function being called by the functional.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 8 / 56


Functions Argument Binding With dots "..." Argument

Argument Binding With dots "..." Argument


The dots "..." argument is > str(sum) # Dots before other arguments
> sum(1, 2, 3) # Dots bind before other arguments
sometimes placed before the named > sum(1, 2, NA, 3, na.rm=TRUE)
arguments, so that a function can > bind_dots <- function(..., in_put) {
+ paste0("in_put=", in_put,
accept an indefinite number of + ", dots=", paste(..., sep=", "))
arguments, without binding them by + } # end bind_dots
> # Arguments after dots must be bound by full name
position with the named arguments. > bind_dots(1, 2, 3, in_put=10)
> bind_dots(1, 2, 3, in_put=10, foo=4) # Dots bound
When the dots "..." are placed > bind_dots(1, 2, 3) # "in_put" not bound
> bind_dots <- function(..., in_put=10) {
before the named arguments, the + paste0("in_put=", in_put,
named arguments are often assigned + ", dots=", paste(..., sep=", "))
+ } # end bind_dots
default values, so they don’t have to > bind_dots(1, 2, 3) # "in_put" not bound, but has default
be bound to a value in the call.
Arguments that appear after the dots
"..." must be bound by their full
name, and can’t be partially bound.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 9 / 56


Functions Wrapper Functions With dots "..." Argument

Wrapper Functions With dots "..." Argument


Wrapper functions provide a > # Wrapper for mean() with default na.rm=TRUE
> my_mean <- function(x, na.rm=TRUE, ...) {
convenient user interface to functions, + mean(x=x, na.rm=na.rm, ...)
by assigning default argument values, + } # end my_mean
> foo <- sample(c(1:10, NA, rep(0.1, t=5)))
validating data, and formatting the > mean(c(foo, NA))
output. > mean(c(foo, NA), na.rm=TRUE)
> my_mean(c(foo, NA))
> my_mean(c(foo, NA), trim=0.4) # Pass extra argument
Wrapper functions are designed to > # Wrapper for saving data into default directory
perform the actions of other functions, > save_data <- function(...,
+ file=stop("error: no file name"),
while reducing their complexity. + my_dir="C:/Develop/data") {
+ # Create file path
The dots "..." argument of the + file <- file.path(my_dir, file)
wrapper function allows passing +
+
save(..., file=file)
} # end save_data
additional arguments on to the > foo <- 1:10
> save_data(foo, file="scratch.RData")
wrapped function. > save_data(foo, file="scratch.RData", my_dir="C:/Develop")
> # Wrapper for testing negative arguments
Wrapper functions should be used > stop_if_neg <- function(in_put) {
with caution, since wrapping a + if (!is.numeric(in_put) || in_put<0)
+ stop("argument not numeric or negative")
function creates extra code + } # end stop_if_neg
> # Wrapper for sqrt()
(overhead), which slows down R. > my_sqrt <- function(in_put) {
+ stop_if_neg(in_put)
+ sqrt(in_put)
+ } # end my_sqrt
> my_sqrt(2)
> my_sqrt(-2)
> my_sqrt(NA)

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 10 / 56


Functions Recursive Functions with dots "..." Argument

Recursive Functions with dots "..." Argument


Recursive functions can also accept > # Recursive function sums its argument list
> sum_dots <- function(in_put, ...) {
the dots "..." argument. + if (missing(...)) { # Check if dots are empty
+ return(in_put) # just one argument left
The dots "..." argument can be + } else {
referenced inside a function by first +
+
in_put + sum_dots(...) # Sum remaining arguments
} # end if
converting it into a list using + } # end sum_dots
> sum_dots(1, 2, 3, 4)
"list(...)". > # Recursive function sums its argument list
> sum_dots <- function(in_put, ...) {
The function missing() returns TRUE + if (NROW(list(...)) == 0) { # Check if dots are empty
if an argument is missing, and FALSE + return(in_put) # just one argument left
+ } else {
otherwise. + in_put + sum_dots(...) # Sum remaining arguments
+ } # end if
+ } # end sum_dots
> sum_dots(1, 2, 3, 4)

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 11 / 56


Functions Recursive Function for Calculating Fibonacci Sequence

Recursive Function for Calculating Fibonacci Sequence


Recursive functions call themselves in > fibo_nacci <- function(len_gth) {
+ if (len_gth > 2) {
their own body. + fib_seq <- fibo_nacci(len_gth-1) # Recursion
+ c(fib_seq, sum(tail(fib_seq, 2))) # Return this
The Fibonacci sequence of integers is + } else {
defined by the recurrence relation: + c(0, 1) # Initialize and return
+ }
+ } # end fibo_nacci
Fn = Fn−1 + Fn−2 , > fibo_nacci(10)
> tail(fibo_nacci(9), 2)
F1 = 0, F2 = 1,
Fn = 0, 1, 1, 2, 3, 5, 8, 13, . . .
The Fibonacci sequence was invented
by Indian mathematicians, and later
described by the Italian mathematician
Fibonacci in his famous treatise Liber
Abaci.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 12 / 56


Functions Exploring Functions

Exploring Functions
If a function name is called alone > # Show the function code
> plot.default
without arguments, then R displays the > # Display function
function code (but it must be on the > getAnywhere(plot.default)

search path).
Non-visible objects can’t be viewed by
calling their name.
The function getAnywhere() displays
information about R objects, including
non-visible objects.
The function getAnywhere() also
displays R objects that aren’t on the
search path.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 13 / 56


Functions Internal and Primitive Functions

Internal and Primitive Functions


R is a high-level language written in lower-level > # Sum() is a compiled primitive function
> sum
languages, mostly C++ and some Fortran. > # mean() is a generic function
> mean
R functions are either written in R code > # Show all methods of mean()
(interpreted functions), or they directly call > methods(generic.function=mean)
> # Show code for mean.default()
compiled C++ or Fortran code (compiled > mean.default
functions, also called internal or primitive).
R parses the code of interpreted functions, and
eventually calls compiled C++ or Fortran code.
But this extra processing makes interpreted
functions much slower than compiled functions.
Users can distinguish between interpreted
functions and compiled functions by typing their
names, and analyzing their source code.
The source code of interpreted functions
contains multiple lines of R code, or a call to
function UseMethod() (which dispatches
methods associated with generic functions).
The source code of compiled functions contains
a single call to one of the functions that execute
compiled C++ or Fortran code: .Internal(),
.Primitive(), .C(), .Call(), .Fortran(), or
.External().
Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 14 / 56
Functions Exploring Internal and Primitive Functions

Exploring Internal and Primitive Functions


Several functions call compiled code: > # Get all methods for generic function "plot"
> methods("plot")
.C(), .Call(), .Fortran(), >
.External(), or .Internal() and > getAnywhere(plot) # Display function

.Primitive()
R .Internal() .Primitive()
The function getAnywhere() displays
R objects, including functions.
If a function name is called alone then
R displays the function code (but it
must be on the search path).
the user can access symbols from a
package that isn’t attached using the
double-colon operator
tools::file ext
The function getAnywhere() also
displays R objects that aren’t on the
search path.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 15 / 56


Functions Lazy Evaluation of Function Arguments

Lazy Evaluation of Function Arguments


R functions delay evaluation of their > lazy_func <- function(arg1, arg2) { # Define function lazy_func
+ 2*arg1 # just multiply first argument
arguments until they’re needed by + } # end lazy_func
their R code. > lazy_func(3, 2) # Bind arguments by position
> lazy_func(3) # Second argument was never evaluated!
This is called lazy evaluation. > lazy_func <- function(arg1, arg2) { # Define function lazy_func
+ cat(arg1, '\n') # Write to output
+ cat(arg2) # Write to output
If the function body doesn’t evaluate + } # end lazy_func
an argument, then the function won’t > lazy_func(3, 2) # Bind arguments by position
> lazy_func(3) # First argument written to output
produce an error, even if the argument
is missing.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 16 / 56


Functions Function Environments

Function Environments
When a function is called, a new > glob_var <- 1 # Define a global variable
> ls(environment()) # Get all variables in environment
evaluation environment is created. > func_env <- function() { # Explore function environments
+ loc_var <- 1 # Define a local variable
The evaluation environment contains + cat('objects in evaluation environment:\t',
the function arguments and locally +
+
ls(environment()), '\n')
cat('objects in enclosing environment:\t',
defined variables. + ls(parent.env(environment())), '\n')
+ cat('this is the enclosing environment:')
R evaluates variables inside functions + parent.env(environment()) # Return enclosing environment
+ } # end func_env
by searching first in the evaluation > func_env()
environment, then the enclosure >
> environment(func_env)
environment, then the R search path. > environment(print) # Package namespace is the enclosure

The enclosure of the evaluation


environment is the environment where
the function was defined.
The enclosure of functions defined in
the workspace is the global
environment.
The enclosure of functions defined in
packages is the package namespace.
Objects defined in the function
enclosure can be referenced inside the
function.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 17 / 56


Functions Lexical Function Scope

Lexical Function Scope


A free variable is a variable that’s not > glob_var <- 1 # Define a global variable
> probe_scope <- function() { # Explore function scope
included in the evaluation + loc_var <- 2*glob_var # Define a local variable
environment. + new_globvar <<- 11 # Define a global variable
+ cat('objects in evaluation environment:\t',
Scoping rules determine how free +
+
ls(environment()), '\n')
cat('this is a local loc_var:\t', loc_var, '\n')
variables are evaluated. + cat('objects in enclosing environment:\n',
+ ls(parent.env(environment())), '\n')
By default R uses lexical (static) + cat('this is glob_var:\t', glob_var, '\n')
+ glob_var <- 10 # Define local glob_var
scoping, which means that variables + cat('this is the local glob_var:\t', glob_var, '\n')
are first evaluated in the evaluation + } # end probe_scope
> probe_scope()
environment, then in the enclosing > glob_var # Global variable is unaffected
> new_globvar # new_globvar is preserved
environment in which the function was > loc_var # Local variable is gone!
defined, and so on.
Dynamic scoping means that variables
are evaluated in the environment from
which the function was called.
The standard assignment operator
"<-" modifies variables in the
evaluation environment.
The special assignment operator
"<<-" modifies variables in the
enclosing environment,

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 18 / 56


Functions Argument Passing in R

Argument Passing in R
In general, arguments can be passed into > a <- 1 # Define a variable
> # New variable "b" points to value of "a"
functions either by value or by reference. > b <- a # Define a new variable
> # When "b" is modified, R makes a copy of it
When an argument is passed by value, then a > b <- b+1
copy of that argument is passed to the function. >
>
# Function doubles its argument and returns it
double_it <- function(in_put) {
+ in_put <- 2*in_put
That way if the function modifies that + cat("input argument was doubled to:", in_put, "\n")
argument, then the original object isn’t + in_put
+ }
modified. > double_it(a)
> a # variable "a" is unchanged
When an argument is passed by reference, then
a pointer to the original object is passed to the
function. Copy-on-modify semantics has important
implications for performance and memory usage.
If the function modifies that argument, then the
original object is modified as well. http://stackoverflow.com/questions/15759117/
what-exactly-is-copy-on-modify-semantics-in-r-and-where
R uses a hybrid method of argument passing
called copy-on-modify semantics.
R passes arguments by reference, thus saving
memory space and time for copying.
But if the argument is modified within the
function, then R makes a copy of it, so that the
original object is unchanged.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 19 / 56


Functions Side effects Using the Super-assignment Operator "<<-"

Side effects Using the Super-assignment Operator "<<-"


Function side effects are operations on objects > rm(list=ls()) # Remove all objects
> ls() # List objects
outside a function’s evaluation environment. > # Load objects from file (side effect)
> load(file="my_data.RData")
The functions plot() and load() are examples > ls() # List objects
of functions that produce side effects. > glob_var <- 1 # Define a global variable
> # Explore function scope and side effects
> side_effect <- function() {
load() reads data from an .RData file, and + cat("global glob_var:\t", glob_var, "\n")
creates objects in the workspace that are + # Define local "glob_var" variable
+ glob_var <- 10
contained in the .RData file. + # Re-define the global "glob_var"
+ glob_var <<- 2
The super-assignment operator "<<-" allows + cat("local glob_var:\t", glob_var, "\n")
creating functions that produce side effects. +
>
} # end side_effect
side_effect()
> # Global variable was modified as side effect
The super-assignment operator "<<-" modifies > glob_var
or creates variables in the enclosing environment
in which a function was defined (lexical
scoping).
If a function was defined in the global
environment then that’s the function’s enclosing
environment, and the "<<-" operator operates
on variables in the global environment.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 20 / 56


Functions Operators as Functions

Operators as Functions
Most functions in R are prefix operators (where > # Standard infix operator call syntax
> 2 + 3
the function name is followed by a list of > # Infix operator applied using prefix syntax
arguments). > "+"(2, 3)
> # Standard bracket operator
Infix operators (where the the function name >
>
vec_tor <- c(4, 3, 5, 6)
vec_tor[2]
comes in between its arguments) can also be > # Bracket operator applied using prefix syntax
> "["(vec_tor, 2)
applied using prefix syntax. >

In prefix syntax, the Infix operator name must


be surrounded by single ’’ or double "" quotes.
The "[" bracket operator can also be written as
a prefix function.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 21 / 56


Functions Defining New Infix Operators

Defining New Infix Operators


New infix operators can be defined using the > # Define infix operator that returns string
> '%+%' <- function(a, b) paste(a, b, sep=" + ")
usual function definition syntax. > 2 %+% 3
> 2 %+% 3 %+% 4
All user defined infix operators names must be > "hello" %+% 2 %+% 3 %+% "bye"
nested between "%" characters.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 22 / 56


Functions Replacement Functions

Replacement Functions
R syntax allows assigning to the values > obj_string <- "hello"
> class(obj_string)
returned by functions, but they must > # Assign to value returned by "class" function
be defined as replacement functions. > class(obj_string) <- "string"
> class(obj_string)
replacement function names include >
>
# Define function last()
last <- function(vec_tor) {
the assignment arrow: "name<-". + vec_tor[NROW(vec_tor)]
+ } # end last
The first argument passed to the > last(1:10)
> # Define replacement function last()
replacement function is modified by > 'last<-' <- function(vec_tor, value) {
the second argument, and then it’s + vec_tor[NROW(vec_tor)] <- value
+ vec_tor
returned. + } # end last
> x <- 1:5
> last(x) <- 11
> x

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 23 / 56


Higher order Functions Functions as First Class Objects

Functions as First Class Objects


Functions in R are first class objects, > # Create functional that accepts a function as input argument
> func_tional <- function(func_name) {
which means they can be treated like + # Calculates statistic on random numbers
any other R object: + set.seed(1)
+ func_name(runif(1e4)) # Apply the function name
Functions can be passed as + } # end func_tional
> func_tional(mean)
arguments to other functions, > func_tional(sd)

Functions can be nested (defined


inside other functions),
Functions can return functions
as their return value,
Higher order functions are R functions
that either accept a function as their
argument (input) or return a function
as their value (output).

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 24 / 56


Higher order Functions Functions That Return Functions

Functions That Return Functions


R functions can also return a function > # Define a power function factory
> make_func <- function(arg_param) { # Wrapper function
as their value. + function(in_put) { # Anonymous closure
+ in_put^arg_param
Functions returned by a function are + }
called closures. +
>
} # end make_func

> square_func <- make_func(2) # Define square function


Functions that return closures can be > square_func(4)
used as function factories. > cube_func <- make_func(3) # Define cube function
> cube_func(2)
> cube_root_func <- make_func(1/3) # Define cube root function
> cube_root_func(8)

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 25 / 56


Higher order Functions Mutable States

Mutable States
A mutable state is an object that is > make_counter <- function() {
+ # Counter function with mutable state
preserved between function calls. + counter <- 0 # Initialize counter
+ cat('counter = ', counter)
Functions that return closures can also + function() { # Return anonymous advance function
be used for creating mutable states. + counter <<- counter + 1 # Advance counter
+ cat('counter = ', counter)
+ } # end advance function
A function evaluation environment is + } # end make_counter
only temporary and disappears after >
> advance_counter <- make_counter() # Create new counter
the function returns its value. > advance_counter() # Advance counter
> advance_counter() # Advance counter
But a closure assigned to a name > advance_counter_two <- make_counter() # Create another counter
maintains access to the environment >
>
advance_counter_two() # Advance counter two
advance_counter() # Advance counter one
in which it was created. > advance_counter_two() # Advance counter two
> advance_counter() # Advance counter one
Therefore the closure maintains access
to its parent function’s arguments and
locally defined variables.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 26 / 56


Higher order Functions Pseudo-Random Generating Function

Pseudo-Random Generating Function


Mutable states can be used to implement pseudo-random number generators,

> # Returns the pseudo-random generating function random_generator


> # the formal argument 'seed' persists in the evaluation environment of seed_random
> seed_random <- function(seed) { # Seed must be an integer
+ random_number <- as.numeric(paste0('0.', seed)) # Initialize
+ # Random_generator returns a vector of pseudo-random numbers of length length_rand
+ random_generator <- function(length_rand=1) { # Assign function name for recursion
+ # Returns a vector of pseudo-random numbers of length length_rand
+ random_number <<- 4*random_number*(1 - random_number) # Logistic map
+ if (length_rand == 1) {
+ return(random_number)
+ } else {
+ return(c(random_number, random_generator(length_rand - 1)))
+ } # end if
+ } # end random_generator
+ } # end seed_random
>
> # Create a random number generating function and set seed
> make_random <- seed_random(88)
> make_random(10) # calculate vector of 10 pseudo-random numbers
> ls(environment(make_random)) # List objects in scope of make_random

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 27 / 56


Higher order Functions Bank Account Using Mutable States

Bank Account Using Mutable States


> # Bank account example (from Venables) demonstrates mutable states> # Perform account operations
> # 'balance' is persistent between function calls > # open an account with 100 deposit
> open_account <- function(balance) { > my_account <- open_account(100)
+ # Returns function list for account operations > ls(my_account) # my_account is a list
+ list( > # Add my_account to search path
+ deposit = function(amount) { # Make deposit > attach(my_account)
+ if (amount > 0) { > withdraw(30) # Withdrawal to buy groceries
+ balance <<- balance + amount # '<<-' super-assignment operator > deposit(100) # Deposit paycheck to account
+ cat(amount, "deposited. Your balance is now:", > withdraw(200) # Withdrawal to buy Gucci bag
+ balance, "\n") > get_balance() # Get account balance
+ } else { >
+ cat("Deposits must be positive!\n") > # List objects in scope of get_balance
+ } > ls(environment(get_balance))
+ }, # end deposit >
+ withdraw = function(amount) { # Make withdrawal > detach(my_account) # Remove my_account from search path
+ if (amount <= balance) {
+ balance <<- balance - amount # '<<-' super-assignment operator
+ cat(amount, "withdrawn. Your balance is now:",
+ balance, "\n")
+ } else {
+ cat("You don't have that much money!\n")
+ }
+ }, # end withdraw
+ get_balance = function() { # Get balance
+ cat("Your current balance is:", balance, "\n")
+ } # end get_balance
+ ) # end list
+ } # end open_account

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 28 / 56


Functionals Functionals

Functionals
Functionals are functions that accept > # Func_tional accepts function name and additional argument
> func_tional <- function(func_name, in_put) {
a function or a function name (string) + # Produce function name from argument
as one of their input arguments. + func_name <- match.fun(func_name)
+ # Execute function call
Functionals are able to execute +
+
func_name(in_put)
} # end func_tional
function calls using the function > func_tional(sqrt, 4)
> # String also works because match.fun() converts it to a function
names. > func_tional("sqrt", 4)
> str(sum) # Sum() accepts multiple arguments
The function match.fun() returns a > # Func_tional can't accept indefinite number of arguments
function name that is specified by a > func_tional(sum, 1, 2, 3)

string.
Functionals that call match.fun() are
able to accept a string as a function
name, because match.fun() converts
it to a function.
match.fun() produces an error
condition if it fails to find a function
with the specified name.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 29 / 56


Functionals Functionals with dots "..." Argument

Functionals with dots "..." Argument


The dots "..." argument in > # Func_tional accepts function name and dots '...' argument
> func_tional <- function(func_name, ...) {
functionals can be used to pass + func_name <- match.fun(func_name)
additional arguments to the function + func_name(...) # Execute function call
+ } # end func_tional
being called by the functional. > func_tional(sum, 1, 2, 3)
> func_tional(sum, 1, 2, NA, 4, 5)
If named values are passed to the dots > func_tional(sum, 1, 2, NA, 4, 5, na.rm=TRUE)
> # Function with three arguments and dots '...' arguments
"..." argument, then the functional > my_func <- function(in_put, param1, param2, ...) {
can bind them to the correct formal + c(input=in_put, param1=param1, param2=param2,
+ dots=c(...))
arguments of the function being called + } # end my_func
by the functional. > my_func(1, 2, 3, param2=4, param1=5)
> func_tional(my_func, 1, 2, 3, param2=4, param1=5)
> func_tional(my_func, 1, 2, 3, 4, 5)

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 30 / 56


Functionals Anonymous Functions

Anonymous Functions
R allows defining functions without > # Simple anonymous function
> (function(x) (x + 3)) (10)
assigning a name to them.
Anonymous functions are functions
that are not assigned to a name.
Anonymous functions can be passed
as arguments to functionals.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 31 / 56


Functionals Functionals with Anonymous Functions

Functionals with Anonymous Functions


Anonymous functions can be passed > # Anonymous function passed to func_tional
> func_tional(func_name=(function(x) (x + 3)), 5)
as arguments to functionals. > # Anonymous function is default value
> func_tional <-
Anonymous functions can also be used + function(..., func_name=function(x, y, z) {x+y+z}) {
as default values for function +
+
func_name <- match.fun(func_name)
func_name(...) # Execute function call
arguments. + } # end func_tional
> func_tional(2, 3, 4) # Use default func_name
> func_tional(2, 3, 4, 5)
> # Func_name bound by name
> func_tional(func_name=sum, 2, 3, 4, 5)
> # Pass anonymous function to func_name
> func_tional(func_name=function(x, y, z) {x*y*z},
+ 2, 3, 4)

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 32 / 56


Functionals Executing Function Calls Using the do.call() Functional

Executing Function Calls Using the do.call() Functional


The functional do.call() executes a function > str(sum) # Sum() accepts multiple arguments
> # Sum() can't accept list of arguments
call using a function name and a list of > sum(list(1, 2, 3))
arguments. > str(do.call) # "what" argument is a function
> # Do.call passes list elements into "sum" individually
do.call() allows calling a function on > do.call(sum, list(1, 2, 3))
> do.call(sum, list(1, 2, NA, 3))
arguments that are elements of a list. > do.call(sum, list(1, 2, NA, 3, na.rm=TRUE))
> # Func_tional() accepts list with function name and arguments
do.call() passes the list elements individually, > func_tional <- function(list_arg) {
+ # Produce function name from argument
instead of passing the whole list as one + func_name <- match.fun(list_arg[[1]])
argument: + # Execute function call uing do.call()
+ do.call(func_name, list_arg[-1])
do.call(fun, list)= fun(list[[1]], + } # end func_tional
list[[2]], ...) > arg_list <- list("sum", 1, 2, 3)
> func_tional(arg_list)
> # Do_call() performs same operation as do.call()
do.call() can be called inside other functionals > all.equal(
to allow them to execute function calls. + do.call(sum, list(1, 2, NA, 3, na.rm=TRUE)),
+ rutils::do_call(sum, list(1, 2, NA, 3), na.rm=TRUE))
The function str() displays the structure of an
R object, for example a function name and its
formal arguments.
The function do call() from package rutils
performs the same operation as do.call(), but
using recursion, which is much faster and uses
less memory.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 33 / 56


Functionals Performing Loops Using the apply() Functionals

Performing Loops Using the apply() Functionals


An important example of functionals > str(apply) # Get list of arguments
> # Create a matrix
are the apply() functionals. > mat_rix <- matrix(6:1, nrow=2, ncol=3)
> mat_rix
The functional apply() returns the > # Sum the rows and columns
result of applying a function to the > row_sums <- apply(mat_rix, 1, sum)
> col_sums <- apply(mat_rix, 2, sum)
rows or columns of an array or matrix. > mat_rix <- cbind(c(sum(row_sums), row_sums),
+ rbind(col_sums, mat_rix))
If MARGIN=1 then the function will be > dimnames(mat_rix) <- list(c("col_sums", "row1", "row2"),
+ c("row_sums", "col1", "col2", "col3"))
applied over the matrix rows, > mat_rix

If MARGIN=2 then the function will be


applied over the matrix columns.
apply() performs a loop over the list
of objects, and can replace "for"
loops in R.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 34 / 56


Functionals The apply() Functional with dots "..." Argument

The apply() Functional with dots "..." Argument


The dots "..." argument in apply() is
designed to pass additional arguments to the > mat_rix[2, 2] <- NA # Introduce NA value
> mat_rix
function being called by apply(). > # Calculate median of columns
> apply(mat_rix, 2, median)
The additional arguments to apply() must be > # Calculate median of columns with na.rm=TRUE
bound by their full (complete) names. > apply(mat_rix, 2, median, na.rm=TRUE)

> str(apply) # Get list of arguments


> mat_rix <- matrix(sample(12), nrow=3, ncol=4) # Create a matrix
> mat_rix
> apply(mat_rix, 2, sort) # Sort matrix columns
> apply(mat_rix, 2, sort, decreasing=TRUE) # Sort decreasing order

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 35 / 56


Functionals The apply() Functional with Anonymous Functions

The apply() Functional with Anonymous Functions


The apply() functional combined > # DAX percentage returns
> re_turns <- rutils::diff_it(log(EuStockMarkets[, 1]))
with anonymous functions can be used > library(moments) # Load package moments
to loop over function parameters. > str(moment) # Get list of arguments
> # Apply moment function
The dots "..." argument in apply() >
>
moment(x=re_turns, order=3)
# 4x1 matrix of moment orders
is designed to pass additional > moment_orders <- as.matrix(1:4)
> # Anonymous function allows looping over function parameters
arguments to the function being called > apply(X=moment_orders, MARGIN=1,
by apply(). + FUN=function(moment_order) {
+ moment(x=re_turns, order=moment_order)
The additional arguments to apply() + } # end anonymous function
+ ) # end apply
must be bound by their full (complete) >
> # Another way of passing parameters into moment() function
names. > apply(X=moment_orders, MARGIN=1, FUN=moment,
+ x=re_turns)

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 36 / 56


Functionals apply() Calling Functions with Multiple Arguments

apply() Calling Functions with Multiple Arguments


When apply() calls a function with > # Function with three arguments
> my_func <- function(arg1, arg2, arg3) {
multiple arguments, then care must be + c(arg1=arg1, arg2=arg2, arg3=arg3)
taken for proper argument binding. + } # end my_func
> my_func(1, 2, 3)
The dots "..." argument in apply() >
>
da_ta <- as.matrix(1:4)
# Pass da_ta to arg1
allows passing additional arguments to > apply(X=da_ta, MAR=1, FUN=my_func, arg2=2, arg3=3)
> # Pass da_ta to arg2
the function being called by apply(). > apply(X=da_ta, MAR=1, FUN=my_func, arg1=1, arg3=3)
> # Pass da_ta to arg3
The additional arguments to apply() > apply(X=da_ta, MAR=1, FUN=my_func, arg1=1, arg2=2)
must be bound by their full (complete)
names.
The values of the "X" argument in
apply() are bound by position to the
first unused argument in the function
being called by apply().

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 37 / 56


Functionals The lapply() Functional

The lapply() Functional


The functional lapply() is a specialized version > # Vector of means of numeric columns
> sapply(iris[, -5], mean)
of the functional apply(). > # List of means of numeric columns
> lapply(iris[, -5], mean)
lapply() applies a function to a list of objects > # Lapply using anonymous function
and returns a list. >
+
unlist(lapply(iris,
function(col_umn) {
+ if (is.numeric(col_umn)) mean(col_umn)
The function unlist() collapses a list with + } # end anonymous function
atomic elements into a vector (which can cause + ) # end lapply
+ ) # end unlist
type coercion). > unlist(sapply(iris, function(col_umn) {
+ if (is.numeric(col_umn)) mean(col_umn)}))
Rule of Thumb
It’s often better to use lapply(), since apply()
and sapply() attempt to coerce their output
into a vector or matrix, which may cause them
to fail.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 38 / 56


Functionals The sapply() Functional

The sapply() Functional


The sapply() functional is a > sapply(6:10, sqrt) # Sapply on vector
> sapply(list(6, 7, 8, 9, 10), sqrt) # Sapply on list
specialized version of the apply() >
functional. > # Calculate means of iris data frame columns
> sapply(iris, mean) # Returns NA for Species
sapply() applies a function to a >
> # Create a matrix
vector or a list of objects and returns a > mat_rix <- matrix(sample(100), ncol=4)
> # Calculate column means using apply
vector or a list. > apply(mat_rix, 2, mean)
>
sapply() tries to return a vector, but > # Calculate column means using sapply, with anonymous function
if the elements can’t be combined into > sapply(1:NCOL(mat_rix),
+ function(col_index) { # Anonymous function
a vector, then it returns a list. + mean(mat_rix[, col_index])
+ } # end anonymous function
When sapply() is given a data frame, + ) # end sapply
it interprets it as a list, and applies the
function to each element (column) of
the data frame.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 39 / 56


Functionals sapply() Returning Matrices

sapply() Returning Matrices


If the function called by sapply() > # Vectors form columns of matrix returned by sapply
> sapply(2:4, function(num) c(el1=num, el2=2*num))
returns a vector, then sapply() > # Vectors of different lengths returned as list
returns a matrix, if possible. > sapply(2:4, function(num) 1:num)
> # vapply is similar to sapply
The vectors returned by the function >
+
vapply(2:4, function(num) c(el1=num, el2=2*num),
FUN.VALUE=c(row1=0, row2=0))
are arranged to form columns of the > # vapply produces an error if it can't simplify
> vapply(2:4, function(num) 1:num,
matrix returned by sapply(). + FUN.VALUE=c(row1=0, row2=0))

But if the function returns vectors of


different lengths, then sapply()
cannot return a matrix, and returns a
list instead.
This behavior of sapply() can cause
run-time errors.
The function vapply() is similar to
sapply(), but it always attempts to
simplify its output to a matrix, and if
it can’t then it produces an error.
vapply() requires the argument
FUN.VALUE that specifes the output
format of the function called by
vapply().

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 40 / 56


Object-Oriented Programming in R The S3 Object-Oriented Programming System in R

The S3 Object-Oriented Programming System in R


S3 is the standard object oriented (OO) > library(zoo) # Load package zoo
> # Show the generic function "merge"
programming system in R. > merge
> # Show the "merge" method dispatched to "zoo" objects
The S3 system is based on generic functions and > merge.zoo
the R class system.
Generic functions are functions that execute
different methods depending on the class of the
object on which the generic function is called.
Methods are functions that are specific to a
generic function and a class of objects.
Methods follow the naming convention
generic function.classname().
The actual function that is executed (called a
method) is determined by the class of the object
on which the generic function is called.
For example, when the function merge() is
called on a zoo object, then R executes the
method merge.zoo().

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 41 / 56


Object-Oriented Programming in R Generic Functions and Their Methods

Generic Functions and Their Methods


The generic function merge() has many > # Get all methods for generic function merge()
> methods(generic.function="merge")
methods with names merge.*(). > # Get generic function methods applied to "zoo" objects
> methods(class="zoo")
The function methods() lists all the methods of
a generic function, or all the methods for a class
of objects.
The merge() method dispatched to zoo objects
is called merge.zoo().

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 42 / 56


Object-Oriented Programming in R Method Dispatch Using UseMethod()

Method Dispatch Using UseMethod()


The function UseMethod() can be used to > # Define a generic function
> gen_sum <- function(a, b, ...) {
implement generic functions. + UseMethod("gen_sum")
+ } # end gen_sum
UseMethod() accepts at least two arguments: >
the name of a generic function, and the >
>
# Define method for "numeric" class
gen_sum.numeric <- function(a, b, ...) {
arguments passed to the generic function. + sum(a, b)
+ } # end gen_sum.character
UseMethod() calls (dispatches) a particular >
> # Define method for "character" class
method associated with the generic function, > gen_sum.character <- function(a, b, ...) {
depending on the class of the arguments passed + paste(a, "plus", b)
+ } # end gen_sum.character
to the generic function. >
> # Apply gen_sum to "numeric" objects
The arguments passed to the generic function > gen_sum(1, 2)
> # Apply gen_sum to "character" objects
are by default passed to UseMethod(), and then > gen_sum("a", "b")
along to the method itself.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 43 / 56


Object-Oriented Programming in R Method Dispatch by Internal Generic Functions

Method Dispatch by Internal Generic Functions


Method dispatch by internal generic functions is > # 'cbind' is an internal generic function
> cbind
performed inside compiled C code, instead of R
code using the function UseMethod().
Internal functions are implemented using the
function .Internal().

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 44 / 56


Object-Oriented Programming in R Operator Overloading

Operator Overloading
Operator overloading refers to defining new > # Define "+" method for "character" class
> "+.character" <- function(a, b, ...) {
methods for an existing generic function. + paste(a, "plus", b)
+ } # end +.character
The "+" operator may be overloaded by defining > methods("+") # view methods for "+" operator
a new method for "character" objects. >
>
# Define variables with "character" class
char1 <- "a"
> char2 <- "b"
But for the overloading of the "+" operator to > class(char1)
work, the objects must have an explicit > char1 + char2 # Add two "character" objects - doesn't work
> attributes(char1) # Doesn't have explicit "character" class - onl
"character" class attribute assigned to them. > char1 <- structure("a", class="character")
> char2 <- structure("b", class="character")
> attributes(char1) # Now has explicit "character" class
> # Add two "character" objects
> char1 + char2

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 45 / 56


Object-Oriented Programming in R Overloading the print() Function

Overloading the print() Function


The generic functions print(), plot() and > # Define object of class "string"
> obj_string <- "how are you today?"
summary() are very often overloaded for newly > class(obj_string) <- "string"
defined classes. > obj_string
> # overload "print" method for string objects
Since print() is a generic function, R >
+
print.string <- function(str_ing) {
print(
dispatches the method associated with the class + paste(strsplit(str_ing, split=" ")[[1]],
+ collapse=" + "))
of that variable. + } # end print.string
> # methods("print") # view new methods for "print" function
When a variable is called by its name, then R > print(obj_string)
invokes the print() function on that variable. > obj_string

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 46 / 56


Object-Oriented Programming in R Operator Overwriting

Operator Overwriting
Operator overwriting refers to redefining an > # overwrite "+" operator
> "+" = function(a, b) {
existing function. + if (is.character(a) && is.character(b)) {
+ paste(a, "plus", b)
The functions .Internal() and .Primitive() + } else {
call functions that are part of the internal code +
+ }
.Primitive("+") (a, b)

of R. + }
> methods("+") # view methods for "+" operator
Operator overwriting should be used with care, > # Add two "numeric" objects
> 1 + 2
since it may cause unintended consequences. > # Add two "character" objects
> "a" + "b"

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 47 / 56


Object-Oriented Programming in R Operator Overwriting Using UseMethod()

Operator Overwriting Using UseMethod()


Existing functions can be overwritten with > # overwrite "+" operator with a generic function
> "+" <- function(a, b, ...) {
generic functions using UseMethod(). + UseMethod("+")
+ } # end gen_sum
Operator overwriting should be used with care, > # Define method for "numeric" class
since it may cause unintended consequences. >
+
"+.numeric" <- function(a, b, ...) {
sum(a, b)
+ } # end gen_sum.character
> # Define method for "character" class
> "+.character" <- function(a, b, ...) {
+ paste(a, "plus", b)
+ } # end gen_sum.character
> methods("+") # view methods for "+" operator
> # Add two "numeric" objects
> 1 + 2
> # Add two "character" objects
> "a" + "b"

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 48 / 56


Object-Oriented Programming in R Exploring Generic Function Methods

Exploring Generic Function Methods


Most methods can be viewed by simply calling > cbind.ts # Can't view non-visible method
> stats::cbind.ts # Can't view non-visible method
their full name, unless they’re non-visible. > stats:::cbind.ts # Display non-visible method
> getAnywhere(cbind.ts) # Display non-visible method
Non-visible methods can be viewed using the
triple-colon operator ":::".
Non-visible methods can also be viewed by
calling the function getAnywhere().

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 49 / 56


Object-Oriented Programming in R Defining New Classes and Methods

Defining New Classes and Methods


A new R class can be created by simply assigning > new_zoo <- zoo(rnorm(10), order.by=(Sys.Date() + 0:9))
> # Coerce "zoo" object to new class "zoo_xtra"
to the class attribute of an existing object. > class(new_zoo) <- "zoo_xtra"
> class(new_zoo)
New methods can be defined for existing generic > methods(generic.function="length")
functions, and R will automatically dispatch > length # Primitive function
> # Define "length" method for class "zoo_xtra"
them for objects of the new class. > length.zoo_xtra <- function(in_ts) {
+ cat("length of zoo_xtra object:\n")
The function unclass() removes the explicit class + # Unclass object, then calculate length
+ NROW(unclass(in_ts))
attribute from an object. + } # end length.zoo_xtra
> NROW(new_zoo) # Apply "length" method to "zoo_xtra" object
Calling unclass() allows using the methods > methods(generic.function="length")
associated with the original object before a new
class attribute was assigned to it.
The functions .Internal() and .Primitive()
call internally implemented (primitive) functions.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 50 / 56


Object-Oriented Programming in R Defining New Generic Functions and Methods

Defining New Generic Functions and Methods


New methods have to be called by their full > # Define "last" method for class "zoo_xtra"
> last.zoo_xtra <- function(in_ts) {
name if a generic function isn’t defined for them. + in_ts[NROW(in_ts)]
+ } # end last.zoo_xtra
Once a generic function is defined, then new > last(new_zoo) # Doesn't work
methods can be called by their short name >
>
last.zoo_xtra(new_zoo) # Works
# Define a generic function
> last <- function(a, b, ...) {
+ UseMethod("last")
+ } # end last
> last(new_zoo) # Now works

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 51 / 56


Object-Oriented Programming in R Creating a ”string” Class

Creating a ”string” Class


A new "string" class can be created from a > # Define generic "string" class converter
> as.string <- function(str_ing, ...)
character object, by assigning to its class + UseMethod("as.string")
attribute. > # Default "string" class converter
> as.string.default <- function(str_ing, ...)
The generic function as.string() converts +
>
structure(str_ing, class="string", ...)
# Numeric "string" class converter
objects to class ”string”. > as.string.numeric <- function(str_ing, ...)
+ structure(as.character(str_ing), class="string", ...)
The function structure() adds attributes to an > # "string" class checker
> is.string <- function(str_ing)
object (specified as symbol=value pairs), and + inherits(x=str_ing, what="string")
returns it. > # Define "string" object
> obj_string <- as.string("how are you today?")
> obj_string
The function inherits() checks whether the > is.string(obj_string)
object class matches any of the names in the > is.string("hello")
> as.string(123)
"what" argument. > is.string(as.string(123))

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 52 / 56


Object-Oriented Programming in R Inheritance and Derived Classes and Methods

Inheritance and Derived Classes and Methods


Inheritance is a mechanism for defining a new > library(xts)
> new_xts <- xts(rnorm(10), order.by=(Sys.Date() + 0:9))
class that is derived from a base class. > class(new_xts) # Class attribute is a vector
> # "last" is a generic function from package "xts"
The derived class inherits all the methods from > last
the base class, but can also have new methods > methods(generic.function="last")
> last(new_xts) # Apply "last" method from "xts" class
of its own. > # Derive object "xts_xtra" from "xts" object
> class(new_xts) <- c("xts_xtra", class(new_xts))
In the S3 system inheritance is implemented by > class(new_xts) # Class attribute is a vector
> # "xts_xtra" object inherits "last" method from "xts" class
making the class attribute a vector. > last(new_xts)

When a generic function gen fun is called on an


object with class attribute c("class2",
"class1"), then R dispatches a method called
gen fun.class2.
If there’s no method with that name, then R first
dispatches a method called gen fun.class1.
Finally if there are no methods with those
names, then R dispatches a method called
gen fun.default.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 53 / 56


Object-Oriented Programming in R Defining New Methods for Derived Classes

Defining New Methods for Derived Classes


The S3 system automatically dispatches newly > # Define new "last" method for class "xts_xtra"
> last.xts_xtra <- function(in_ts) {
defined methods to objects of the new class. + cat("last element of xts_xtra object:\n")
+ drop(in_ts[NROW(in_ts), ])
If new methods aren’t found, then it dispatches + } # end last.xts_xtra
existing methods from the base class to objects >
>
last(new_xts) # Apply "last" from "xts_xtra" class
# Define "last" method for class "xts_xtra"
of the new class. > last.xts_xtra <- function(in_ts) {
+ cat("last element of xts_xtra object:\n")
The function NextMethod() dispatches the base + drop(NextMethod())
+ } # end last.xts_xtra
method of a generic function. > last(new_xts) # Apply "last" from "xts_xtra" class

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 54 / 56


Object-Oriented Programming in R Homework Assignment

Homework Assignment

Required
Create a function for calculating the kurtosis of a time series of returns,
Using this function calculate the kurtosis of DAX returns, and of t-distribution returns with
four degrees of freedom (use the same number of data points in both cases),
Plot the probability density of DAX returns together with t-distribution returns with four
degrees of freedom on a single plot,

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 55 / 56


Object-Oriented Programming in R Homework Assignment

Homework Assignment

Required
Create a function for calculating the kurtosis of a time series of returns,
Using this function calculate the kurtosis of DAX returns, and of t-distribution returns with
four degrees of freedom (use the same number of data points in both cases),
Plot the probability density of DAX returns together with t-distribution returns with four
degrees of freedom on a single plot,

Recommended
Read chapters 4, 5, 10 from: Introduction to R.

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 55 / 56


Object-Oriented Programming in R Additional Reading

Additional Reading
Download R Interpreter from CRAN (Comprehensive R Archive Network)
http://cran.r-project.org/

Download RStudio IDE (Integrated Development Environment)


http://www.rstudio.com/products/rstudio/

Jerzy Pawlowski (NYU Tandon) Functions October 23, 2020 56 / 56

You might also like