functional programming - biostatistics 140 · simpleexample...
TRANSCRIPT
![Page 1: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/1.jpg)
Functional ProgrammingBiostatistics 140.776
![Page 2: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/2.jpg)
What is Functional Programming?
Functional programming concentrates on four constructs:
1. Data (numbers, strings, etc)2. Variables (function arguments)3. Functions4. Function Applications (evaluating functions given arguments
and/or data)
![Page 3: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/3.jpg)
What is Functional Programming?
By now you’re used to treating variables inside of functions as data:
I numbers and stringsI data structures like lists and vectors.
With functional programming you can also
I provide a function as an argument to another functionI return a function as the result of a function
The sapply() function is a an example of supplying a function asan argument to another function
![Page 4: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/4.jpg)
Simple Example
The following function returns a function as its “result”.
adder_maker <- function(n) {function(x){
n + x}
}
This function “constructs” functions based on its argument n.
![Page 5: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/5.jpg)
Simple Example
## Make a function that adds 3 to its argumentadd3 <- adder_maker(3)add3(5)
[1] 8
## Make a function that adds 2 to its argumentadd2 <- adder_maker(2)add2(5)
[1] 7
![Page 6: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/6.jpg)
Simple Example
A few notes on this example:
I This example works because each of the add functions “knows”its value of n
I Lexical scoping rules mean that both add2() and add3() lookup the value of n in the environment where they were defined
I Each function was defined inside the adder_maker() functionwhich supplied the value of n via the user
![Page 7: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/7.jpg)
Functional Programming
The functional programming approach essentially allows you towrite code based on data supplied by the user!
1. Usual model: Data –> Code –> “Results”2. Functional model: Data –> Code –> Code (!)
It’s a very powerful technique that can allow you to developso-called “intelligent” systems
![Page 8: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/8.jpg)
The purrr Package
I There are groups of functions that are essential for functionalprogramming: map, reduce, filter, compose, search
I In most cases they take a function and a data structure asarguments, and that function is applied to that data structurein some way.
I The purrr package contains many of these functionsI Functional programming is concerned mostly with lists and
vectors (for data structures)
![Page 9: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/9.jpg)
Map
A key function in the purrr package is the map() function, whichhas two arguments:
I .x A data structure like a list or vectorI .f A function to be applied, a formula (converted into a
function), or an atomic vector
map() always returns a list, regardless of the input. It works muchlike lapply().
![Page 10: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/10.jpg)
Map
library(purrr)num2word <- function(x) {
words <- c("one", "two", "three", "four", "five")words[x]
}map(3:1, num2word)
[[1]][1] "three"
[[2]][1] "two"
[[3]][1] "one"
![Page 11: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/11.jpg)
Map with Simplification
map_chr(5:1, num2word)
[1] "five" "four" "three" "two" "one"
![Page 12: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/12.jpg)
Map
Often we want to map a function to an object (list/vector) and thenextract an element of each of the return values.
This code splits the MIE data into separate rooms, fits a linearmodel to each subset, and then extracts the coefficients.
results <- split(airquality, airquality$Month) %>%map(~ lm(Ozone ~ Temp, data = .x)) %>%map("coefficients")
![Page 13: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/13.jpg)
Map
results[1:3]
$`5`(Intercept) Temp-102.159308 1.884808
$`6`(Intercept) Temp-91.990958 1.552441
$`7`(Intercept) Temp-372.920837 5.150363
![Page 14: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/14.jpg)
MapUsing map() with a formula argument “auto-generates” ananonymous function, which is more compact than using somethinglike lapply().
split(airquality, airquality$Month) %>%map(~ lm(Ozone ~ Temp, data = .x))
is equivalent to
lapply(split(airquality, airquality$Month),function(.x) {
lm(Ozone ~ Temp, data = .x)})
The version using map() is more readable, modular, and highlightswhat is happening in the right sequence.
![Page 15: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/15.jpg)
Conditional MapThe map_if() function takes a predicate and then a function forapplication. A predicate is a function that returns TRUE or FALSEgiven some input.
## A predicate functionis_even <- function(x) {
x %% 2 == 0}square <- function(y){
y^2}map_if(1:5, is_even, square) %>% unlist
[1] 1 4 3 16 5
If the predicate evalutes to FALSE, the argument just passesthrough.
![Page 16: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/16.jpg)
Reduce
List or vector reduction is done with the reduce() function. It is akind of looping that
1. Combines the first element of a vector with the second elementof a vector;
2. then that combined result is combined with the third elementof the vector;
3. and so on until the end of the vector is reached.
The function to be applied should take at least two arguments.
![Page 17: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/17.jpg)
Reduce
Often, after mapping a function to a list/vector, you will want tocombine the results at the end.
map_if(1:5, is_even, square) %>%reduce(function(x, y) {
x + y})
[1] 29
This is overkill since the result is just a simple list; we could havejust used sum().
But what if the map() function returns a more complicated object?
![Page 18: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/18.jpg)
Reduce
split(airquality, airquality$Month) %>%map(~ lm(Ozone ~ Temp, data = .x)) %>%map("coefficients") %>%reduce(function(x, y) {
## 'x' and 'y' are numeric vectorscbind(x, y)
})
x y y y y(Intercept) -102.159308 -91.990958 -372.920837 -238.861312 -149.346890Temp 1.884808 1.552441 5.150363 3.559044 2.351148
![Page 19: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/19.jpg)
Search
You can search for specific elements of a vector using the
I has_element(): will return TRUE if a specified element ispresent in a vector, otherwise it returns FALSE
I detect(): takes a vector and a predicate function asarguments and it returns the first element of the vector forwhich the predicate function returns ‘TRUE
![Page 20: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/20.jpg)
Search
has_element(letters, "a")
[1] TRUE
has_element(letters, "A")
[1] FALSE
![Page 21: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/21.jpg)
Search
detect(20:40, function(x){x > 22 && x %% 2 == 0
})
[1] 24
![Page 22: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/22.jpg)
Filter
The various filter functions are
I keep(): Keep elements that satisfy a predicateI discard(): Remove elements that satisfy a predicateI every(): returns TRUE only if every element in the vector
satisfies the predicate functionI some(): returns TRUE if at least one element in the vector
satisfies the predicate function
![Page 23: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/23.jpg)
Filtering
keep(1:20, function(x){x %% 2 == 0
})
[1] 2 4 6 8 10 12 14 16 18 20
discard(1:20, function(x){x %% 2 == 0
})
[1] 1 3 5 7 9 11 13 15 17 19
![Page 24: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/24.jpg)
Filtering
every(1:20, function(x){x %% 2 == 0
})
[1] FALSE
some(1:20, function(x){x %% 2 == 0
})
[1] TRUE
![Page 25: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/25.jpg)
ComposeThe compose() function take any number of functions andcombines them into a single function.
nlevels <- compose(length, unique)nlevels(c("a","a", "a", "b", "b"))
[1] 2
This is equivalent to
nlevels <- function(x) {length(unique(x))
}
The evaluation of functions is done from right to left.
![Page 26: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/26.jpg)
Compose
You can also do more unusual things like
not_null <- compose("!", is.null)not_null(NULL)
[1] FALSE
not_null(4)
[1] TRUE
![Page 27: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/27.jpg)
Compose
You can also build upon little building blocks
add1 <- function(x) {x + 1
}add3 <- compose(add1, add1, add1)add3(8)
[1] 11
![Page 28: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/28.jpg)
ComposeWhen fitting linear models, I do this a lot:
summary_lm <- compose(summary, lm)summary_lm(Ozone ~ Temp, data = airquality)
Call:last(formula = ..1, data = ..2)
Residuals:Min 1Q Median 3Q Max
-40.729 -17.409 -0.587 11.306 118.271
Coefficients:Estimate Std. Error t value Pr(>|t|)
(Intercept) -146.9955 18.2872 -8.038 9.37e-13Temp 2.4287 0.2331 10.418 < 2e-16
Residual standard error: 23.71 on 114 degrees of freedom(37 observations deleted due to missingness)
Multiple R-squared: 0.4877, Adjusted R-squared: 0.4832F-statistic: 108.5 on 1 and 114 DF, p-value: < 2.2e-16
![Page 29: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/29.jpg)
Compose
Or even
coef_lm <- compose(coef, lm)coef_lm(Ozone ~ Temp, data = airquality)
(Intercept) Temp-146.995491 2.428703
![Page 30: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/30.jpg)
From purrr to furrr via future
The furrr package is a “future-ized” version of the purrr package.
I future provides parallel processing capabilities that workacross all R platforms
I The furrr package uses future to parallelize the purrrfunctions
I map() 7→ future_map(), etc.I Need to invoke a future “plan” via the plan() functionI GitHub site: https://github.com/DavisVaughan/furrr
![Page 31: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/31.jpg)
From purrr to furrr
The purrr way:
split(airquality, airquality$Month) %>%map(~ lm(Ozone~Temp, data = .x))
The furrr way:
library(furrr)split(airquality, airquality$Month) %>%
future_map(~ lm(Ozone~Temp, data = .x))
![Page 32: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/32.jpg)
From purrr to furrrBy default uses a “sequential” future plan, so everything is donesequentially (and not in parallel).
To get parallel computation, you need to change the plan().
library(furrr)## This works on Mac/Windows/Unixplan(multiprocess)
split(airquality, airquality$Month) %>%future_map(~ lm(Ozone~Temp, data = .x)) %>%future_map("coefficients") %>%reduce(cbind)
out(Intercept) -102.159308 -91.990958 -372.920837 -238.861312 -149.346890Temp 1.884808 1.552441 5.150363 3.559044 2.351148
![Page 33: Functional Programming - Biostatistics 140 · SimpleExample Thefollowingfunctionreturnsafunctionasits“result”. adder_maker](https://reader036.vdocuments.net/reader036/viewer/2022081404/5f053fb97e708231d41205b7/html5/thumbnails/33.jpg)
Summary
I Functional programming treats functions as “first class”citizens, along with data, and parameters
I Building functions that adapt to data is a key elementI Map, reduce, filter, search, and compose are key conceptsI Often can be a clearer way to express code (if not more
succinct or faster)I Functional programming often extends easily to parallel
programming, such as via the furrr package