Functional Programming in R (Using purrr Package)
Frank / 2020-06-15
I wrote a small article about purrr package before.
Now I think it’s time to write a better article introducing the purrr package.
You can find the official website through this link.
Map Family
The map family is used to apply function or functions over a list or vector.
The “primary” function is the map function.
library(purrr)
#Remember: map always return a list rather than a vector
test_list <- list(a=c(1,2,3),
b=c(2,3,4),
c=c(3,4,5))
map(test_list,mean)
#> $a
#> [1] 2
#>
#> $b
#> [1] 3
#>
#> $c
#> [1] 4
map takes 3 arguments which are .x,.f,….
.x is the list or vector you want to apply function on.
.f is the function you want to apply.
… is the arguments you want to specify in .f.
library(purrr)
test_list <- list(a=c(1,2,NULL),
b=c(2,3,4),
c=c(3,4,5))
map(test_list,mean,na.rm=TRUE)
#> $a
#> [1] 1.5
#>
#> $b
#> [1] 3
#>
#> $c
#> [1] 4
Another good thing is that you can use lambda function in .f part. All you need to is: 1. Add “~” before you lambda function 2. Use “.x” in .f to specify the location.
library(purrr)
test_list <- list(a=c(1,2,3),
b=c(2,3,4),
c=c(3,4,5))
map(test_list,~.x+5)
#> $a
#> [1] 6 7 8
#>
#> $b
#> [1] 7 8 9
#>
#> $c
#> [1] 8 9 10
Other map_ functions are similar to map. You just need to specify the output type.
library(purrr)
test_list <- list(a=c(1,2,3),
b=c(2,3,4),
c=c(3,4,5))
map_dbl(test_list,sum)
#> a b c
#> 6 9 12
Now it returns a vector rather than a list.
Manipulate Functions
Another good thing in purrr is that you can manipulate functions. Yes, you can manupulate functions just as other objects in R!It is really fantastic.
Create Function From Lambda
as_mapper enables you to create a function quickly and make your code cleaner and more readable.
library(purrr)
new_fc <- as_mapper(~(.x*100+1)/2)
new_fc(1)
#> [1] 50.5
Compose Function
compose enables you to connect a function from other existing functions.
library(purrr)
test_list <- list(a=c(1,2,3),
b=c(2,3,4),
c=c(3,4,5))
mutiply100 <- as_mapper(~.x*100)
mean_multiply100 <- compose(mutiply100,mean)
map_dbl(test_list,mean_multiply100)
#> a b c
#> 200 300 400
Please note the equivalent of compose(round,mean) is round(mean(x)). The order here is important.
Create Function with Default Arguments
library(purrr)
my_mean <- partial(mean,na.rm=TRUE)
my_mean(c(1,2,3,NULL))
#> [1] 2
Now my_mean is the mean function with default argument to ignore all NULL in calculating mean value of a vector.
Negate a Predicate Function
Predicate Function is the function that returns a logical output like TRUE or FALSE. Such as is.numeric(),is.character().
negate makes your life easy because you don’t need to write a second function!
library(purrr)
test_list <- list(a=c(1,2,0.5),
b=c(2,3,4),
c=c(3,4,5))
mean_over_2 <- as_mapper(~mean(.x)>=2)
mean_under_2 <- negate(mean_over_2)
map_lgl(test_list,mean_over_2)
#> a b c
#> FALSE TRUE TRUE
map_lgl(test_list,mean_under_2)
#> a b c
#> TRUE FALSE FALSE