A function factory is a function that makes (returns) functions
Factory made function are manufactured functions.
Function factory | Credits: epsis.com
How does it work? | Credits: kakaakigas.com/how-it-works/
power1()
is the function factory and square()
and cube()
are manufactured functions.
function()
and <-
)R functions are objects in their own right, a language property often called “first-class functions”
– Section 6.2.3
#> <environment: R_GlobalEnv>
#> <environment: 0x0000029fbf09a6d0>
env_print(fun)
and fn_env()
to explore#> <environment: 0x0000029fc0169900>
#> Parent: <environment: global>
#> Bindings:
#> • exp: <lazy>
#> [1] 2
Blue indicates environment, arrows bindings
2
evaluated when function calledSo use force()
! (Unless you want it to change with the x
in the parent environment)
Only required if the argument is not evaluated before the new function is created:
Because
<<-
(super assignment)We can change that enclosing environment and keep track of that state across iterations (!)
<-
Assignment in current environment<<-
Assignment in parent environmentnew_counter <- function() {
i <- 0
function() {
i <<- i + 1 # second assignment (super assignment)
i
}
}
counter_one <- new_counter()
counter_two <- new_counter()
c(counter_one(), counter_one(), counter_one())
#> [1] 1 2 3
#> [1] 1 2 3
“As soon as your function starts managing the state of multiple variables, it’s better to switch to R6”
Cleaning up using rm()
inside a function:
Useful when…
For example, these bins are not appropriate
sd <- c(1, 5, 15)
n <- 100
df <- data.frame(x = rnorm(3 * n, sd = sd), sd = rep(sd, n))
ggplot(df, aes(x)) +
geom_histogram(binwidth = 2) +
facet_wrap(~ sd, scales = "free_x") +
labs(x = NULL)
We could just make a function…
binwidth_bins <- function(x) (max(x) - min(x)) / 20
ggplot(df, aes(x = x)) +
geom_histogram(binwidth = binwidth_bins) +
facet_wrap(~ sd, scales = "free_x") +
labs(x = NULL)
But if we want to change the number of bins (20) we’d have to re-write the function each time.
If we use a factory, we don’t have to do that.
binwidth_bins <- function(n) {
force(n)
function(x) (max(x) - min(x)) / n
}
ggplot(df, aes(x = x)) +
geom_histogram(binwidth = binwidth_bins(20)) +
facet_wrap(~ sd, scales = "free_x") +
labs(x = NULL, title = "20 bins")
ggplot(df, aes(x = x)) +
geom_histogram(binwidth = binwidth_bins(5)) +
facet_wrap(~ sd, scales = "free_x") +
labs(x = NULL, title = "5 bins")
Similar benefit in Box-cox example
Useful when…
For example, ggsave()
wraps a bunch of different graphics device functions:
# (Even more simplified)
plot_dev <- function(ext, dpi = 96) {
force(dpi)
switch(
ext,
svg = function(filename, ...) svglite::svglite(file = filename, ...),
png = function(...) grDevices::png(..., res = dpi, units = "in"),
jpg = ,
jpeg = function(...) grDevices::jpeg(..., res = dpi, units = "in"),
stop("Unknown graphics extension: ", ext, call. = FALSE)
)
}
Then ggsave()
uses
ggsave <- function(...) {
dev <- plot_dev(device, filename, dpi = dpi)
...
dev(filename = filename, width = dim[1], height = dim[2], bg = bg, ...)
...
}
Otherwise, would have to do something like like a bunch of if/else statements.
Useful when…
optimise()
/optimize()
(Skipping to final results from section)
Here, using MLE want to to find the most likely value of lambda for a Poisson distribution and this data.
We’ll create a function that creates a lambda assessment function for a given data set.
We can use this on different data sets, but here use ours x1
Use optimise()
rather than trial and error
#> $maximum
#> [1] 32.09999
#>
#> $objective
#> [1] -30.26755
Result: Highest log-probability is -30.3, best lambda is 32.1
Combine functionals and function factories to turn data into many functions.
names <- list(
square = 2,
cube = 3,
root = 1/2,
cuberoot = 1/3,
reciprocal = -1
)
funs <- purrr::map(names, power1)
names(funs)
#> [1] "square" "cube" "root" "cuberoot" "reciprocal"
#> [1] 8
#> [1] 9
Avoid the prefix with
with()
- with(funs, root(100))
attach()
- attach(funs)
/ detach(funs)
rlang::env_bind
- env_bind(globalenv(), !!!funs)
/ env_unbind(gloablenv(), names(funs))