使用 purrr 进行函数式编程


函数式编程是一种编程方法,我们通过构造和应用函数来构建程序。更具体地说,在程序中,我们应用顺序的纯函数而不是语句。纯函数是指接受输入并产生一致输出值的函数。此外,在此过程中不会修改任何增强或输入流。此类函数能够执行单个操作,但为了执行复杂的操作,我们可以将它们组合成序列。

在本教程中,我们将讨论使用 purrr 进行函数式编程。如今,函数式编程由于其解决复杂问题的能力而变得很重要。

R 中的函数式编程

我们可以使用 purrr 包在 R 中实现函数式编程。您可以使用以下 CRAN 命令安装 purrr 包:

install.packages("purrr")

如您所见,我们已成功在系统中安装了 purrr 包。

Map_() 系列函数

purrr 包为我们提供了 map_() 系列函数,使用这些函数我们可以实现函数式编程以获得与 for 和 while 循环相同的输出。

map_() 函数有多种形式。让我们一一来看:

map() 函数

语法

这是最基本的函数。它接受一个向量和一个函数作为参数,然后为向量中的每个元素调用该函数。该函数的语法如下所示:

map(vector, 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(myVector, subtractByThree))

输出

[[1]]
[1] 5

[[2]]
[1] 0

[[3]]
[1] 4

[[4]]
[1] -1

[[5]]
[1] 8

[[6]]
[1] 17

如上所示,在从给定向量的所有元素中减去 3 后,会生成一个列表。

示例

现在让我们尝试再次执行与上面相同的操作,但这次使用循环。如您在源代码中看到的,这次在循环的情况下,它需要我们编写更复杂的代码。

# 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)

输出

[[1]]
[1] 5

[[2]]
[1] 0

[[3]]
[1] 4

[[4]]
[1] -1

[[5]]
[1] 8

[[6]]
[1] 17

如上所示,在从给定向量的所有元素中减去 3 后,会生成一个列表。

因此,我们可以说在代码中使用 map() 可以减少代码量(与循环相比),并且我们也不需要创建空列表来保存结果。

map() 函数还为我们提供了更简单的方法来根据位置和名称从向量中提取元素。例如:

示例

在下面的程序中,我们创建了一个列表的列表,并且我们首先使用位置然后使用标题名称访问元素。

# 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)

输出

[[1]]
[1] 1

[[2]]
[1] 4

[[3]]
[1] 8

使用标题名称访问

map(myList, "data3")

输出

[[1]]
[1] "p"

[[2]]
[1] "q"

[[3]]
[1] "r"

如上所示,首先打印第二位置的元素(使用位置),然后打印第三位置(使用标题名称)的元素。

map_chr() 函数

语法

顾名思义,map_char() 函数返回字符串的原子向量。此函数具有以下语法:

map_chr(vector, 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))

输出

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

如输出所示,操作的结果作为字符串向量返回。

map_dbl() 函数

语法

顾名思义,map_dbl() 函数返回双精度类型的原子向量。此函数具有以下语法:

map_dbl(vector, 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))

输出

[1]  5  0  4 -1  8 17

如输出所示,操作的结果作为双精度数据类型的向量返回。

reduce() 函数

语法

函数式编程的另一个动机是减少。purrr 包为我们提供了 reduce() 函数,使用该函数我们可以将列表中的值的数量减少到单个值。此函数具有以下语法:

reduce(list, operation)

它接受一个列表和要对列表的值执行的操作。它返回一个单一的值。

示例

让我们考虑以下演示 reduce() 函数工作原理的程序:

# Import library
library("purrr")

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

输出

[1] 3628800

如上所示,我们对列表的所有元素应用了乘法运算,并生成了一个单一的值作为输出。

故障排除错误

在开发领域,故障排除特定程序或软件中的问题的能力非常重要。如果程序中涉及列表,那么它变得更加重要,因为在字符串的情况下查找问题会变得很棘手。

安全地遍历 Map over safely() 函数

当我们遍历列表时,有时某些操作的结果不会按照正确的数据类型出现。更具体地说,操作会导致无效值。在这种情况下,我们可以使用 safely() 函数来安全地使用 purrr。

在下面的程序中,我们正在计算列表所有元素的对数值。但是对于 -1,无法计算对数。因此,它可能会引发一些错误。但是我们在这里使用 safely() 函数进行映射以获取这些操作期间的结果和错误。由于我们希望在出现任何错误之前传递结果,因此我们应用了 transpose() 函数。

示例

# 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"]])

输出

$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(myList[["Result"]])

输出

NULL

打印错误组件

print(myList[["Error"]])

输出

NULL

如输出所示,已显示给定列表的各个元素的对数值。由于无法计算列表中 -1 的对数,因此相应的输出值已替换为 NAN 值。您还可以分别在输出中看到结果组件和错误组件。

安全地遍历 Map over possibly() 函数

possibly() 函数执行与 safely() 函数几乎相同的任务。唯一的区别是 possibly() 不会打印任何错误消息。

示例

让我们考虑以下程序:

# 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)

输出

[[1]]
[1] 2.484907

[[2]]
[1] 2.397895

[[3]]
[1] 2.302585

[[4]]
[1] NaN

如输出所示,已显示给定列表的各个元素的对数值。由于无法计算列表中 -1 的对数,因此相应的输出值已替换为 NAN。

结论

在本教程中,我们讨论了使用 purrr 包进行函数式编程。我们从什么是 R 中的 purrr 包以及如何使用 CRAN 安装它开始,然后我们看到了 map 函数的不同版本及其工作原理。最后,我们讨论了使用 safely() 和 possibly() 函数进行故障排除的方法。我希望本教程将帮助您提高数据科学领域的相关知识。

更新于: 2023年1月17日

252 次浏览

开启您的 职业生涯

通过完成课程获得认证

开始
广告