Functional Programming with purrr


Functional programming is a programming methodology in which we construct programs by constructing and applying functions. More specifically in programs, we apply sequential pure functions rather than statements. A pure function is a function that accepts an input and produces a consistent value as an output. Also, during this process no augment or input stream is modified. Such functions are capable of doing a single operation but for carrying out complex operations we can combine them into sequences.

In this tutorial, we will discuss functional programming using purr. Nowadays, Functional programming is important to master due to its capability to solve complex problems.

Functional programming in R

We can achieve functional programming in R using the purrr package. You can install the purrr package by using the below command in CRAN −

install.packages("purrr")

As you can see, we have installed purrr package successfully in our system.

Map_() family functions

The purrr package provides us map_() family functions using which we can achieve functional programming to get the same outcome of for and while loops.

The map_() function has several forms. Let’s see them one by one −

The map() function

Syntax

This is the most fundamental function. It accepts a vector and a function as a parameter and then calls the function for each element in the vector. The syntax of this function is given below,

map(vector, function)

It accepts a vector and a function as a parameter. It returns a list as an output.

Example

Consider a program given below,

# Import library
library("purrr")

# Create a vector
myVector <- c(8, 3, 7, 2, 11, 20)

# Define a function
subtractByThree <- function(x) x - 3

# Print the output list 
print(map(myVector, subtractByThree))

Output

[[1]]
[1] 5

[[2]]
[1] 0

[[3]]
[1] 4

[[4]]
[1] -1

[[5]]
[1] 8

[[6]]
[1] 17

As you can see in the above, a list is produced after subtracting three from all the elements of the given vector.

Example

Now let us try to do the same thing again as we have done above but this time with loops. As you can see in the source code, this time it takes us to write more complex code in the case of loops.

# Create a vector
myVector <- c(8, 3, 7, 2, 11, 20)

# Create a list of size six
subtractByThree <- vector("list", 6)

# Iterate using a for loop
# and calculate the result
# for each value in the vector
for(currentNumber in seq_along(myVector)){
   subtractByThree[[currentNumber]] <- myVector[currentNumber] - 3
}

# Print the list
print(subtractByThree)

Output

[[1]]
[1] 5

[[2]]
[1] 0

[[3]]
[1] 4

[[4]]
[1] -1

[[5]]
[1] 8

[[6]]
[1] 17

As you can see above, a list is produced after subtracting three from all the elements of the given vector.

Therefore, we can say that using map() in code reduces the code size (as compared to loops), and also we don’t require to create an empty list to hold the results.

The map() function also provides us with simpler ways to extract elements from a vector on the basis of position and name. For example,

Example

In the following program, we have created a list of lists and we are accessing elements using the position first and then using the header name.

# Import the library
library("purrr")

# Create a list of lists
myList <- list(list(data1 = 0.1, data2 = 1, data3 = "p"), list(data1 = 0.2, data2 = 4, data3 = "q"), list(data1 = 0.3, data2 = 8, data3 = "r")
)

# Access using position
map(myList, 2)

Output

[[1]]
[1] 1

[[2]]
[1] 4

[[3]]
[1] 8

Access using header name

map(myList, "data3")

Output

[[1]]
[1] "p"

[[2]]
[1] "q"

[[3]]
[1] "r"

As you can see above, second-position elements (using position) are printed first, and then the third-position (using header name) elements.

The map_chr() function

Syntax

As the name suggests, the map_char() function returns the atomic vector of strings. This function has the following syntax −

map_chr(vector, function)

It accepts a vector and a function as a parameter. It returns an atomic vector of strings.

Example

Let us consider a program illustrating the working of this function −

# Import library
library("purrr")

# Create a vector
myVector <- c(8, 3, 7, 2, 11, 20)

# Define a function
subtractByThree <- function(x) x - 3

# Print the output list 
print(map_chr(myVector, subtractByThree))

Output

[1] "5.000000"  "0.000000"  "4.000000"  "-1.000000" "8.000000"  "17.000000"

As you can see in the output, the result of the operation is returned as a vector of strings.

The map_dbl() function

Syntax

As the name suggests, the map_dbl() function returns the atomic vector of double type. This function has the following syntax −

map_dbl(vector, function)

It accepts a vector and a function as a parameter. It returns an atomic vector of double type.

Example

Let us consider a program illustrating the working of this function −

# Import library
library("purrr")

# Create a vector
myVector <- c(8, 3, 7, 2, 11, 20)

# Define a function
subtractByThree <- function(x) x - 3

# Print the output list 
print(map_dbl(myVector, subtractByThree))

Output

[1]  5  0  4 -1  8 17

As you can see in the output, the result of the operation is returned as a vector of double data type.

The reduce() Function

Syntax

Another motive of functional programming is to reduce. The purrr package provides us reduce() function using which we can reduce the number of values in a list to a single value. This function has the following syntax −

reduce(list, operation)

It accepts a list and the operation to be performed for the values of the list. It returns a single value.

Example

Let us consider the following program demonstrating the working of reduce() function −

# Import library
library("purrr")

# Apply reduce() function
reduce(seq(1:10), `*`)

Output

[1] 3628800

As you can see above, we have applied multiply operation for all the elements of the list and a single value is produced as an output.

Troubleshooting Errors

The skill of troubleshooting issues in a particular program or software is quite important in the field of development. If there are lists involved in a program then it becomes more important because finding issues becomes tricky in the case of strings.

Map over safely() function

When we map over a list then sometimes the results of some operations don’t come as per the correct data type. More specifically the operation results in an invalid value. In such cases, we can use the safely() function to purrr safely.

In the following program, we are calculating the logarithmic value of all the elements of the list. But for -1 the log cannot be calculated. Therefore, it might raise some errors. But we are mapping using the safely() function here to get both results and errors during these operations. Since we want to pipe out results before getting any errors therefore we have applied the transpose() function.

Example

# Import library
library("purrr")


# Map using safely
myList <- list(12, 11, 10, -1) %>% 
   map(safely(log, otherwise = NA_real_)) %>%
   # Transpose the outcome
   transpose()

# Print the result
print(myList)

# Print the result component
print(myList[["Result"]])

# Print the error component
print(myList[["Error"]])

Output

$result
$result[[1]]
[1] 2.484907

$result[[2]]
[1] 2.397895

$result[[3]]
[1] 2.302585

$result[[4]]
[1] NaN


$error
$error[[1]]
NULL

$error[[2]]
NULL

$error[[3]]
NULL

$error[[4]]
NULL

Print the result component

print(myList[["Result"]])

Output

NULL

Print the error component

print(myList[["Error"]])

Output

NULL

As you can see in the output, the log value of individual elements of the given list has been displayed. Since, the log of -1 in the list cannot be calculated therefore the corresponding output value has been replaced by NAN value. You can also see the result component and error components separately in the output.

Map over possibly() function

The possibly() function does the almost same task as the safely() function. The only difference is possibly() doesn’t print any error messages.

Example

Let us consider the program below −

# Import library
library("purrr")

# Map over possibly()
myList <- list(12, 11, 10, -1) %>% 
map(possibly(function(data){
   log(data)
},NA_real_))

# Print the list
print(myList)

Output

[[1]]
[1] 2.484907

[[2]]
[1] 2.397895

[[3]]
[1] 2.302585

[[4]]
[1] NaN

As you can see in the output, the log value of individual elements of the given list has been displayed. Since the log of -1 in the list cannot be calculated therefore the corresponding output value has been replaced by NAN.

Conclusion

In this tutorial, we discussed function programming using purrr package. We started with what is purrr package in R and how we can install it using CRAN, then we saw different versions of map functions and their working. In the end, we discussed troubleshooting methods using safely() and possibly() functions. I hope this tutorial will help you to enhance your knowledge in the field of data science.

Updated on: 17-Jan-2023

197 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements