knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
library(Rdlazer)

lapply(c(lipstick, deodorant), drop)




Functions Recap

Here is what we have covered so far:

  • functions are the active components of the language, whereas objects are passive
  • a function is a block of code that can be run repeatedly
  • as instance of a function running is a function call
  • a function usually takes arguments
  • arguments can modify the procedure within the function
  • objects passed as arguments remain unchanged
  • a function usually returns an output value

\newline

  • R is a language where functions are first class citizens
  • everything is done by a function
  • everything can be done to a function




Function Environments

If you find yourself confused by this part, consider reading the Environments vignette.




Defining New Functions

f <- function(x, y = 9L) {
  cat('x and y will be added', '\n')
  z <- x + y
  cat('the sum will be pasted to a string constant', '\n')
  ans <- paste('text', z)
  cat('the result wil be returned', '\n')
  return(ans)
  cat('I am being ignored', '\n')
  ans <- paste(ans, 'redoubled')
  return(ans)
}

# the defined function is assigned as the variable f
# f has two formal arguments, x and y; y has a default value of 9L
# during the call a variable called ans is created and returned
# returning ends the call

f(3)
f2 <- function(x, y = 9) {
    cat('x and y will be added', '\n')
  z <- x + y
  cat('the sum will be pasted to a string constant', '\n')
  cat('the pasted text will be implicitly returned', '\n')
  paste('text', z)
}

f2(3)

\newline

function(x, y) {z <- x + y; as.character(z)}(3, 5)

function(x, y) {x + y}(3, 5)

(function(x, y) x + y)(3, 5)

\newline

SIDENOTE

  • during a function call arguments are evaluated as they are needed, not before
fs <- function(x, y = NULL) {
  y
  toupper(x)
}

# y is not specified, defaults to NULL
fs('text')

# y is specified as a call to stop, which signals an error and terminates the call
fs('text', stop('not so fast!', call. = FALSE))



fns <- function(x, y = NULL) {
  toupper(x)
}

fns('text')

fns('text', stop('not so fast!'))

# the error is ignored as no expression in the body refers to it
  • this behavior is called lazy evaluation, as opposed to eager evaluation, and is a standard feature of R

END OF SIDENOTE




Applying Functions Over Vectors

v1 <- 1:5

# this is a vectorized operation:
v2 <- v1 < 3

# these are not
any(v2)
isTRUE(v2)

\newline

v <- c('one', 'two')
vn <- setNames(v, c('item1', 'item2'))

lapply(v, identity)

lapply(vn, identity)

lapply(as.list(vn), identity)

\newline

# get the vectors in list form
l <- as.list(iris[1:4])

# run lapply
lapply(l, mean)
l2 <- vector(mode = 'list', length = length(l))
for (i in seq_along(l)) {
  l2[[i]] <- mean(l[[i]])
}
names(l2) <- names(l)
l2
ff <- function(x, text = 'text') {
  y <- mean(x)
  y <- round(y, 2)
  ans <- paste0(text, ': ', y)
  return(ans)
}

lapply(l, ff)

\newline

lapply(l, ff, text = 'the mean value is')

lapply(list(iris, mtcars), head, n = 2)

\newline

# get the mean of each list element
lapply(l, mean)

# alternative with anonymous function
lapply(l, function(x) mean(x))

# get the first element of each list item
lapply(l, function(x) x[1])

# get the first and third element of each list item
lapply(l, function(x) c(x[1], x[3]))

# get the first and last elements
lapply(l, function(x) c(x[1], x[length(x)]))

\newline

seps <- c(',', ', ', ' - ', '_')

l[[1]]

lapply(seps, function(x) paste(l[[1]], collapse = x))




lapply Family Members

sapply

lapply(v, identity)

sapply(v, identity)
# simplifying can involve coercion
ll <- list(one = '1', two = 1)
sapply(ll, identity)

# it can also be impossible
lll <- list(one = 1:2, two = 1)
sapply(lll, identity)

vapply

vapply(ll, identity, character(1))

vapply(v, identity, character(1))

mapply

mapply(paste, l, collapse = seps)

replicate

replicate(5, 1:4)
# create 5 random samples (10 items each) of a normal distribution
replicate(5, rnorm(10))

# compute mean of a random sample (10k items)
replicate(100, {
  s <- rnorm(1e+4)
  mean(s)
})

# capture the means and compute their mean
means <- replicate(100, {
  s <- rnorm(1e+4)
  sm <- mean(s)
})

head(means)

mean(means)
# random pertumation
sample(6)

# another random permutation
sample(6)

# assigned random permutation ("captured")
(p <- sample(6))

# the permutation remains "captured"
p

# p stores the value returned by the call to sample, not the function call itself

\newline

Check out the Vectorize function too.

`lapply` is a very powerful and flexible tool
`sapply` can return vectors rather than lists
`sapply` results can be surprising
`mapply` can iterate over any number of arguments
`replicate` can be useful for simulations




olobiolo/Rdlazer documentation built on Aug. 6, 2022, 11:37 a.m.