Loading [MathJax]/jax/output/CommonHTML/jax.js
+ - 0:00:00
Notes for current slide
Notes for next slide

Functional programming

450X

Stanford University

Department of Political Science


Toby Nowacki

1 / 28
2 / 28

Overview

  1. Why functions?
  2. Common pitfalls
  3. Functionals
  4. Function factories
  5. Recursion
3 / 28

Why functions?

  • A function is a mapping from some inputs X to some outputs Y.
  • Whenever we carry out the same process more than once, a function is strongly recommended
  • Much more convenient for both tractability and debugging
  • Allow for decomposition of complex problems into smaller pieces
4 / 28

Basic function architecture

foo <- function(x, y){
return(x + y)
}
5 / 28

Common pitfalls (1)

Can you spot the problem?

foo <- function(x, y){
return(x + y)
}
item1 <- 3
item2 <- "five"
foo(item1, item2)
6 / 28

Solution (1)

foo <- function(x, y){
stopifnot(is.numeric(x), is.numeric(y))
return(x + y)
}
foo(item1, item2)
# Error in foo(item1, item2) : is.numeric(y) is not TRUE
# Calls: <Anonymous> ... withCallingHandlers -> withVisible -> eval -> eval -> # foo -> stopifnot
  • An alternative is using tryCatch().
7 / 28

Common pitfall (2)

What's wrong here?

bar <- function(x, y, z){
out <- x + y
return(out)
out_two <- out + z
return(out_two)
}
8 / 28

Common pitfall (2)

What's wrong here?

bar <- function(x, y, z){
out <- x + y
return(out)
out_two <- out + z
return(out_two)
}
bar(2, 4, 6)
## [1] 6
9 / 28

Solution (2)

bar <- function(x, y, z){
out <- x + y
cat(paste0("Intermediate output: ", out))
out_two <- out + z
return(out_two)
}
bar(2, 4, 6)
## Intermediate output: 6
## [1] 12
10 / 28

Functionals

  • Functions can take other functions as arguments!
  • we've seen this before in the form of lapply or map:
vec <- 2:6
map_dbl(vec, sqrt)
## [1] 1.414214 1.732051 2.000000 2.236068 2.449490
  • Other functions that rely on functionals are, for example, apply, optimize, integrate
11 / 28

Functionals (cont'd)

  • You can write your own functions with functionals:
print_summary <- function(data, fn){
out <- fn(data)
return(paste0("Statistic: ", out))
}
print_summary(c(2, 4, 4), mean)
## [1] "Statistic: 3.33333333333333"
print_summary(c(2, 4, 4), max)
## [1] "Statistic: 4"
12 / 28

Functionals (cont'd)

  • But what about this?
blob <- c(2, 4, 4, NA)
print_summary(blob, mean)
## [1] "Statistic: NA"
  • Can't pass additional arguments to mean:
    print_summary(blob, mean(na.rm = FALSE))
13 / 28

Functionals (cont'd)

  • Fortunately, there is a shortcut!
  • ... lets us pass on whatever else is specified as an input argument.
print_summary <- function(x, f, ...){
return(f(x, ...))
}
print_summary(blob, mean, na.rm = TRUE)
## [1] 3.333333
14 / 28

Functionals (cont'd)

  • Selecting columns in dataframes is a little bit trickier.
df <- tibble(name = c("A", "B", "C"),
value = c(30, 16, 45))
col_summary <- function(dataframe, col_name, f, ...){
get_col <- dataframe %>% dplyr::select(col_name)
return(f(get_col, ...))
}
col_summary(df, value, mean)
# Error in .f(.x[[i]], ...) : object 'value' not found
15 / 28

Functionals (cont'd)

  • Have to rely on something called tidyeval
  • Look up quotations and quasi-quotations!
col_summary <- function(dataframe, col_name, f, ...){
col_name <- enquo(col_name)
get_col <- dataframe %>%
summarise(out = f(!!col_name, ...))
return(get_col)
}
col_summary(df, value, mean, na.rm = TRUE)
## # A tibble: 1 x 1
## out
## <dbl>
## 1 30.3
16 / 28

Function factories

  • Functions can also produce other functions as output!
  • These things are sometimes called function factories.
factory <- function(x, y){
fm <- paste0(y, " ~ ", x)
function(d){
lm(formula = fm, data = d)$coef
}
}
17 / 28

Function factories (cont'd)

car_reg <- factory("mpg", "hp")
car_reg(mtcars)
## (Intercept) mpg
## 324.082314 -8.829731
18 / 28

Function factories (cont'd)

car_reg2 <- factory("cyl", "wt")
car_reg2(mtcars)
## (Intercept) cyl
## 0.5646195 0.4287080
19 / 28

Function factories (cont'd)

  • Will be very useful when doing bootstrapping or MLE estimation
20 / 28

Recursion

  • Factorial example:

n!=n(n1)(n2)...1

  • Use the property of recursion to make the function to refer to itself.
21 / 28

Recursion (cont'd)

  • What's wrong with the definition as below?
factorial_fn <- function(n){
return(n * factorial_fn(n-1))
}
22 / 28

Recursion (cont'd)

  • Let's fix it.
factorial_fn <- function(n){
if(n <= 1){
return(1)
}
else{
return(n * factorial_fn(n-1))
}
}
23 / 28

Recursion (cont'd)

factorial_fn(5)
## [1] 120
factorial_fn(4)
## [1] 24
24 / 28

Problems with recursion

  • Not always the most efficient implementation...

25 / 28

Further applications

  • Fibonacci sequence xn=xn1+xn+2
  • Collatz conjecture (Syracuse Problem)
  • Sorting, searching, merging algorithms...
26 / 28

Conclusion

  • More hands-on programming: what are the most efficient ways to solve a problem?
  • Functions are the bread-and-butter of intermediate and advanced programming
  • Highly recommended for replicability, tractability, and time saving.
  • Still, much more out there... (e.g., basic search algorithms)
27 / 28

Next week

  • Parallel programming
  • Server-side scripts and working on the cluster
28 / 28
2 / 28
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow