purrr鲜为人知的技巧

purrr 是一个拓展R函数式编程能力的包。它会涉及到很多东西,在这篇文章中,我会展示在purrr中最重要的(至少对我来说)几个函数。

map函数来摆脱循环

library(purrr)

numbers <- list(11, 12, 13, 14)

map_dbl(numbers, sqrt)
## [1] 3.316625 3.464102 3.605551 3.741657

你可能想知道为什么这可能比for循环更受欢迎?因为它更简洁,你不需要初始化任何类型的结构来保存结果。如果用google “create empty list in R”,你会发现它很普遍。然而,有了map函数族,将不需要初始化结构。map_dbl函数会返回一个实数原子列表(atomic list),map函数会返回一个列表,去试一下吧。

在某些list上使用map

map_if()
#创造一个辅助函数,如果为偶数则返回TRUE
is_even <- function(x){
  !as.logical(x %% 2)
}
map_if(numbers, is_even, sqrt)
## [[1]]
## [1] 11
## 
## [[2]]
## [1] 3.464102
## 
## [[3]]
## [1] 13
## 
## [[4]]
## [1] 3.741657
map_at()
map_at(numbers, c(1,3), sqrt)
## [[1]]
## [1] 3.316625
## 
## [[2]]
## [1] 12
## 
## [[3]]
## [1] 3.605551
## 
## [[4]]
## [1] 14

map_if()map_at()map拥有更多的参数;如果是map_if(),则是用一个判断函数(一个返回TRUE 或者FALSE的函数),map_at则是用一个位置向量。这样只有在满足某一条件时才会映射你的函数,这也是很多人google寻找的东西。

在多个参数中映射一个函数

numbers2 <- list(1, 2, 3, 4)

map2(numbers, numbers2, `+`)
## [[1]]
## [1] 12
## 
## [[2]]
## [1] 14
## 
## [[3]]
## [1] 16
## 
## [[4]]
## [1] 18

map_2函数可以输入2个参数来

你可以使用map_2函数将两个列表映射到一个函数,你甚至可以使用pmap()函数将任意数量的列表映射到任何函数。

如果出现问题,不要停止执行你的函数

possible_sqrt <- possibly(sqrt, otherwise = NA_real_)

numbers_with_error <- list(1, 2, 3, "spam", 4)

map(numbers_with_error, possible_sqrt)
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 1.414214
## 
## [[3]]
## [1] 1.732051
## 
## [[4]]
## [1] NA
## 
## [[5]]
## [1] 2

另一个很常见的问题:即使报错,也要继续执行你的循环。在大多数情况下,循环会在错误处停止,但是你想让他继续跑下去,看看哪里出错了。去google “skip error in a loop” 你会发现有很多人也想这样做。其实只要结合map()函数与possibly()函数就可以了。大多数解决方案可以会说使用tryCatch函数,但我个人感觉它不太好用。

如果出现错误,不要停止执行函数并捕获错误

safe_sqrt <- safely(sqrt, otherwise = NA_real_)

map(numbers_with_error, safe_sqrt)
## [[1]]
## [[1]]$result
## [1] 1
## 
## [[1]]$error
## NULL
## 
## 
## [[2]]
## [[2]]$result
## [1] 1.414214
## 
## [[2]]$error
## NULL
## 
## 
## [[3]]
## [[3]]$result
## [1] 1.732051
## 
## [[3]]$error
## NULL
## 
## 
## [[4]]
## [[4]]$result
## [1] NA
## 
## [[4]]$error
## <simpleError in sqrt(x = x): non-numeric argument to mathematical function>
## 
## 
## [[5]]
## [[5]]$result
## [1] 2
## 
## [[5]]$error
## NULL

safely函数与possibly函数很相似,但是它会在列表中返回列表。因此元素是结果和伴随错误消息的列表。如果没有错误,则返回NULL。如果有错误,则返回错误信息。

转置一个列表

safe_result_list <- map(numbers_with_error, safe_sqrt)

transpose(safe_result_list)
## $result
## $result[[1]]
## [1] 1
## 
## $result[[2]]
## [1] 1.414214
## 
## $result[[3]]
## [1] 1.732051
## 
## $result[[4]]
## [1] NA
## 
## $result[[5]]
## [1] 2
## 
## 
## $error
## $error[[1]]
## NULL
## 
## $error[[2]]
## NULL
## 
## $error[[3]]
## NULL
## 
## $error[[4]]
## <simpleError in sqrt(x = x): non-numeric argument to mathematical function>
## 
## $error[[5]]
## NULL

这里我们转置了一个列表。这意味着我们仍然返回列表中的列表,但是第一个列表里面全是results,可通过safe_result_list$result得到;第二个列表中全是errors,可以通过 safe_result_list$error得到,这是很有用的。

将函数应用到列表的更底层

transposed_list <- transpose(safe_result_list)

transposed_list %>%
    at_depth(2, is_null)
## Warning: at_depth() is deprecated, please use `modify_depth()` instead
## $result
## $result[[1]]
## [1] FALSE
## 
## $result[[2]]
## [1] FALSE
## 
## $result[[3]]
## [1] FALSE
## 
## $result[[4]]
## [1] FALSE
## 
## $result[[5]]
## [1] FALSE
## 
## 
## $error
## $error[[1]]
## [1] TRUE
## 
## $error[[2]]
## [1] TRUE
## 
## $error[[3]]
## [1] TRUE
## 
## $error[[4]]
## [1] FALSE
## 
## $error[[5]]
## [1] TRUE

有时候处理列表嵌套列表的数据会很棘手,特别是当我们想在子列表中应用一个函数时。但是使用at_depth()函数将会变得很简单。

对列表元素进行命名

name_element <- c("sqrt()", "ok?")

set_names(transposed_list, name_element)
## $`sqrt()`
## $`sqrt()`[[1]]
## [1] 1
## 
## $`sqrt()`[[2]]
## [1] 1.414214
## 
## $`sqrt()`[[3]]
## [1] 1.732051
## 
## $`sqrt()`[[4]]
## [1] NA
## 
## $`sqrt()`[[5]]
## [1] 2
## 
## 
## $`ok?`
## $`ok?`[[1]]
## NULL
## 
## $`ok?`[[2]]
## NULL
## 
## $`ok?`[[3]]
## NULL
## 
## $`ok?`[[4]]
## <simpleError in sqrt(x = x): non-numeric argument to mathematical function>
## 
## $`ok?`[[5]]
## NULL

对列表进行reduce操作,使之变成一个值

reduce(numbers, `*`)
## [1] 24024

下面是 accumulate()函数:

accumulate(numbers, `*`)
## [1]    11   132  1716 24024

它会保存中间结果。
若用accumulate_right() 则为 从右向左

这个函数非常常用,你可以reduce任何东西:

矩阵:

mat1 <- matrix(rnorm(10), nrow = 2)
mat2 <- matrix(rnorm(10), nrow = 2)
mat3 <- matrix(rnorm(10), nrow = 2)
list_mat <- list(mat1, mat2, mat3)

reduce(list_mat, `+`)#结果等同于mat1+mat2+mat3
##             [,1]       [,2]       [,3]     [,4]      [,5]
## [1,] -2.48530177  1.0110049  0.4450388 1.280802 1.3413979
## [2,]  0.07596679 -0.6872268 -0.6579242 1.615237 0.8231933

甚至数据框:

df1 <- as.data.frame(mat1)
df2 <- as.data.frame(mat2)
df3 <- as.data.frame(mat3)

list_df <- list(df1, df2, df3)

reduce(list_df, dplyr::full_join)
## Joining, by = c("V1", "V2", "V3", "V4", "V5")
## Joining, by = c("V1", "V2", "V3", "V4", "V5")
##           V1         V2          V3          V4         V5
## 1 -0.6264538 -0.8356286  0.32950777  0.48742905  0.5757814
## 2  0.1836433  1.5952808 -0.82046838  0.73832471 -0.3053884
## 3 -0.8969145  1.5878453 -0.08025176  0.70795473  1.9844739
## 4  0.1848492 -1.1303757  0.13242028 -0.23969802 -0.1387870
## 5 -0.9619334  0.2587882  0.19578283  0.08541773 -1.2188574
## 6 -0.2925257 -1.1521319  0.03012394  1.11661021  1.2673687

希望你能喜欢这些有用的列变函数。

原文地址

解释下map,reduce

举例说明,比如我们有一个函数$f(x)=x^2$,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下:

举例说明,比如我们有一个函数f(x)=x^2,要把这个函数作用在一个list(1, 2, 3, 4, 5, 6, 7, 8, 9)上,就可以用map()实现如下:  map(list(1:9),function(x)x^2)
            f(x) = x * x

                  │
                  │
  ┌───┬───┬───┬───┼───┬───┬───┬───┐
  │   │   │   │   │   │   │   │   │
  ▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼

[ 1   2   3   4   5   6   7   8   9 ]

  │   │   │   │   │   │   │   │   │
  │   │   │   │   │   │   │   │   │
  ▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼

[ 1   4   9  16  25  36  49  64  81 ]
reduce(list(x1, x2, x3, x4),f) = f(f(f(x1, x2), x3), x4)
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《Python从入门到实践》(第二版)是一本关于Python编程语言的教材,适合初学者学习和实践。这本书介绍了Python的基本语法、数据类型、函数、类、文件操作等内容,并通过一些实际项目的示例帮助读者加深理解和应用。 这本书的PDF版本可以方便读者在线或离线阅读。通过下载PDF版本,读者可以随时随地学习Python,不受时间和地点的限制。读者可以在计算机、平板电脑或手机上打开PDF文件,查看书的内容。 Python是一种流行的编程语言,广泛应用于各个领域,包括科学计算、数据分析、网络开发和人工智能等。《Python从入门到实践》(第二版)这本书的目的是让读者掌握Python的基础知识,并通过练习项目来实践所学知识。 这本书的第二版相对于第一版进行了更新和改进,更加全面和详细地介绍了Python的各个方面。它不仅适合没有编程经验的初学者,也适合有其他编程经验的读者想学习Python。书的项目示例也有助于读者从实际问题学习编程的应用。 总之,《Python从入门到实践》(第二版)的PDF版本是一本对于想学习Python编程的读者来说非常有用的资源。它提供了便捷的在线或离线学习方式,包含了Python的基础知识和实践项目示例,以帮助读者更好地理解和掌握Python编程语言。 ### 回答2: 《Python Crash Course》,第二版,是一本关于Python编程基础的教材,也是入门级的学习指南。该书由Eric Matthes撰写,旨在帮助读者快速入门Python编程,并提供实践项目来巩固所学内容。 书的内容从基础知识开始,逐渐引导读者了解Python的核心概念和语法。每个章节都有清晰的示例代码和练习题,读者可以直接动手实践。此外,书还包含一些鲜为人知技巧和窍门,帮助读者更高效地编写Python代码。 第二版相较于第一版进行了更新和改进。新增了一些主题,例如数据可视化、测试代码和Web应用程序的开发等。这些内容使得读者能够更深入地了解Python的各个方面,并能够应用于实际项目。 书还提供了一些有趣的项目,例如创建一个数据可视化程序、设计一个Web应用程序等。这些项目有助于读者在实践应用所学的知识,提高编程能力和解决问题的能力。 《Python Crash Course》,第二版,适合那些零基础或有少量编程经验的读者入门学习Python。它以简洁易懂的语言和充实的实践示例,帮助读者轻松入门Python编程,并为日后深入学习打下坚实基础。无论是想要学习编程的新手,还是希望巩固Python基础的学生和开发人员,都可以从本书获得丰富的知识和实践经验。 ### 回答3: 《Python Crash Course,第2版》是一本面向初学者的Python编程教程,它提供了全面且易于理解的学习资源。该书作者Eric Matthes详细介绍了Python编程语言的基本概念和应用,并提供了大量的实例和练习,帮助读者掌握Python编程的核心技能。 这本书的第2版与第1版相比,进行了全面的更新和改进。它添加了更多的实例和练习,涵盖了最新的Python 3.7版本,并包括了新的主题,如数据可视化和Web开发。此外,书还介绍了常见的编程概念和实践,如控制流程、函数、类和文件处理等,这些都是编程入门的关键内容。 《Python Crash Course,第2版》的主要特点包括: 1. 结构清晰:该书按照逻辑顺序组织,逐渐引导读者从Python的基础知识开始,并逐步深入探讨更高级的主题。 2. 大量的实例和练习:该书提供了许多实际应用的示例代码和练习题,读者可以通过实践来巩固所学知识。 3. 实用的项目:在书的后半部分,作者引导读者完成两个大型的实际项目,通过实际案例展示Python编程的应用和技术。 4. 学习资源:书提供了在线资源,包括源代码、额外的练习和更多的学习资源,读者可以通过这些资源进一步拓展他们的知识和能力。 总之,《Python Crash Course,第2版》是一本非常适合初学者的Python编程教程,它通过易于理解的语言和丰富的实例,帮助读者建立起扎实的Python编程基础,并为进一步深入学习和应用Python打下坚实的基础。无论是想要学习编程的新手还是已有一定编程经验的人士,都可以从这本书受益。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值