博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
purrr鲜为人知的技巧
阅读量:6238 次
发布时间:2019-06-22

本文共 5328 字,大约阅读时间需要 17 分钟。

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

#创造一个辅助函数,如果为偶数则返回TRUEis_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## 
## ## ## [[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]]## 
## ## $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]]## 
## ## $`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)

转载地址:http://gwkia.baihongyu.com/

你可能感兴趣的文章
JavaScript数组与字符串常用方法总结
查看>>
经常使用socket函数具体解释
查看>>
Ubuntu 16.04安装ntopng流量监控软件
查看>>
Mongodb基本操作入门,增删改查和索引
查看>>
UVALive - 4255 - Guess (拓扑排序)
查看>>
UNIX网络编程卷1 时间获取程序client UDP 协议无关
查看>>
一个想法照进现实-《IT连》创业项目:万事开头难
查看>>
【zTree】zTree的3.5.26静态树与动态树(实用)
查看>>
《组合数学》
查看>>
文件操作方法大全以及文件打开的其他一些模式sys.stdout.write()就是标准输出到你当前的屏幕 sys.stdout.flush()把内存立即显示到您当前的屏幕...
查看>>
.NET Core 2.0 开源Office组件 NPOI
查看>>
SNF快速开发平台--规则引擎整体介绍及使用说明书
查看>>
vuex笔记
查看>>
【转】采用dlopen、dlsym、dlclose加载动态链接库
查看>>
【树3】满二叉树、完全二叉树、完美二叉树
查看>>
细说mysql replace into
查看>>
ThinkingInJava 学习 之 0000003 控制执行流程
查看>>
glValidateProgram只用于调试
查看>>
Asp.net MVC验证哪些事(2)-- 验证规则总结以及使用
查看>>
二叉排序树 -- 增删查改
查看>>