purrr 0.2.0

Hadley Wickham

2016-01-06

Categories: Packages tidyverse

原文地址

我很高兴的发布了purrr 0.2.0。Purrr填补了R的函数式编程工具中的缺失部分,让你的函数更加纯粹、类型稳定。

我仍然在研究purrr应该做什么,以及它如何与基础R、dplyr, tidyr的现有功能进行比较。一个主要的观点影响了当前的版本那就是:为编程设计的函数应该是类型稳定的。类型稳定性是一个来自Julia的概念,并引起了我的注意。尽管R和Julia中的函数可以返回不同类型的输出,但总的来说,您应该努力让函数总是返回相同类型数据结构。这使得函数对变化的输入更加稳健,并且使得它们合理。(但是并不是每个函数都能是类型稳定的)

Purrr 0.2.0 为map,flattens和try()添加了类型稳定的替代方法,如下所述。还有很多其他的小改进,错误修复和一些弃用。请参阅发布说明以获取完整的更改列表。

类型稳定的映射

map是一个让函数作用在每个向量上的函数。在基础R中的map函数有*apply族:lapply(),sapply(),vapply等。lapply()是一个类型稳定的函数:无论输入什么,都将返回一个列表。sapply不是一个类型稳定函数,它会根据输入返回不同的类型的输出。下面的代码会向你展示一个简单的sapply的例子,它会根据他的输入返回一个向量,矩阵或者列表。

df <- data.frame(a = 1L,b = 1.5,y = Sys.time(),z = ordered(1)
)df[1:4] %>% sapply(class) %>% str()
#> List of 4
#>  $ a: chr "integer"
#>  $ b: chr "numeric"
#>  $ y: chr [1:2] "POSIXct" "POSIXt"
#>  $ z: chr [1:2] "ordered" "factor"
df[1:2] %>% sapply(class) %>% str()
#>  Named chr [1:2] "integer" "numeric"
#>  - attr(*, "names")= chr [1:2] "a" "b"
df[3:4] %>% sapply(class) %>% str()
#>  chr [1:2, 1:2] "POSIXct" "POSIXt" "ordered" "factor"
#>  - attr(*, "dimnames")=List of 2
#>   ..$ : NULL
#>   ..$ : chr [1:2] "y" "z"

这种行为使得sapply()适合于交互式使用,因为它通常会正确地猜测并给出一个有用的数据结构。
但在包或生产代码中使用它是不合适的,因为如果输入不是你所期望的,它将不会失败,而是会返回一个意外的数据结构。这通常会在整个过程中导致进一步的错误,所以你会收到令人困惑的错误消息,并且很难找出根本原因。

基础R有一个sapply()的类型稳定版本叫做vapply()。它需要一个额外的参数来决定输出结果。purrr则是采用不同的方法,purrr有多个函数而不是一个函数做所有的事情,每个函数对应一种常见的输出类型: map_lgl(), map_int(), map_dbl(), map_chr(), and map_df()

  • int,代表integer
  • dbl,代表double
  • chr,代表character向量或字符串。
  • dttm,代表日期+时间(a date + a time)
  • lgl,代表逻辑判断TRUE或者FALSE
  • fctr,代表因子类型factor
  • date,代表日期dates.

这些要么产生指定类型的输出要么抛出一个错误。 这迫使你立即处理这个问题:

df[1:4] %>% map_chr(class)
#> Error: Result 3 is not a length 1 atomic vector
df[1:4] %>% map_chr(~ paste(class(.), collapse = "/"))
#>                a                b                y                z
#>        "integer"        "numeric" "POSIXct/POSIXt" "ordered/factor"

map()的其他变体具有相似的后缀。 例如,map2()允许你并行地迭代两个向量:

x <- list(1, 3, 5)
y <- list(2, 4, 6)
map2(x, y, c)
#> [[1]]
#> [1] 1 2
#>
#> [[2]]
#> [1] 3 4
#>
#> [[3]]
#> [1] 5 6

map2()总是返回一个列表。如果要将相应的值相加,并将结果存储为double类型的向量,你可以使用 map2_dbl()

map2_dbl(x, y, `+`)
#> [1]  3  7 11

另一个map变体是invoke_map(),它接受函数列表和参数列表。 它也有类型稳定的后缀:

#IQR为四分位距,mad为中位数绝对偏差
spread <- list(sd = sd, iqr = IQR, mad = mad)
x <- rnorm(100)invoke_map_dbl(spread, x = x)
#>        sd       iqr       mad
#> 0.9121309 1.2515807 0.9774154

Type-stable flatten

当类型稳定性很重要时,另一种情况是将嵌套列表展平为更简单的数据结构。基础R有unlist函数,但它是危险的,因为它总是成功的。作为替代,purrr提供了flatten_lgl(), flatten_int(), flatten_dbl(), 和 flatten_chr():

x <- list(1L, 2:3, 4L)
x %>% str()
#> List of 3
#>  $ : int 1
#>  $ : int [1:2] 2 3
#>  $ : int 4
x %>% flatten() %>% str()
#> List of 4
#>  $ : int 1
#>  $ : int 2
#>  $ : int 3
#>  $ : int 4
x %>% flatten_int() %>% str()
#>  int [1:4] 1 2 3 4

Type-stable try()

另一个在基础R中的非类型稳定函数是 trytry()确保表达式总是成功,返回原始值或错误消息:

str(try(log(10)))
#>  num 2.3
str(try(log("a"), silent = TRUE))
#> Class 'try-error'  atomic [1:1] Error in log("a") : non-numeric argument to mathematical function
#>
#>   ..- attr(*, "condition")=List of 2
#>   .. ..$ message: chr "non-numeric argument to mathematical function"
#>   .. ..$ call   : language log("a")
#>   .. ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

safely()是一个类型稳定版本的try()。它总是返回2个元素:结果与错误,有一个总为NULL(有结果则不报错,报错则没结果)

safely(log)(10)
#> $result
#> [1] 2.302585
#>
#> $error
#> NULL
safely(log)("a")
#> $result
#> NULL
#>
#> $error
#> <simpleError in .f(...): non-numeric argument to mathematical function>

注意到safely()将一个函数作为输入,并返回一个“安全”函数,这个函数永远不会抛出错误。一个强大的技术是地使用safely()map()一起尝试对列表中的每个元素进行操作:

#注意safely()是将函数作为输入,故若对多个值进行操作,可用map
safe_sqrt <- safely(sqrt, otherwise = NA_real_)
numbers_with_error <- list(1, 2, 3, "spam", 4)
map(numbers_with_error, safe_sqrt)%>%transpose()
safe_log <- safely(log)
x <- list(10, "a", 5)
log_x <- x %>% map(safe_log)str(log_x)
#> List of 3
#>  $ :List of 2
#>   ..$ result: num 2.3
#>   ..$ error : NULL
#>  $ :List of 2
#>   ..$ result: NULL
#>   ..$ error :List of 2
#>   .. ..$ message: chr "non-numeric argument to mathematical function"
#>   .. ..$ call   : language .f(...)
#>   .. ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
#>  $ :List of 2
#>   ..$ result: num 1.61
#>   ..$ error : NULL

这是输出稍微不方便,因为你更想有一个三个结果的列表,另一个三个错误的列表。 您可以使用新的transpose()函数来切换层次结构中第一个和第二个层次的顺序:

log_x %>% transpose() %>% str()
#> List of 2
#>  $ result:List of 3
#>   ..$ : num 2.3
#>   ..$ : NULL
#>   ..$ : num 1.61
#>  $ error :List of 3
#>   ..$ : NULL
#>   ..$ :List of 2
#>   .. ..$ message: chr "non-numeric argument to mathematical function"
#>   .. ..$ call   : language .f(...)
#>   .. ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
#>   ..$ : NULL

这样可以很容易地提取原始函数失败的输入,或者保存成功结果:

keep()保留TRUE

discard()保留FALSE

results <- x %>% map(safe_log) %>% transpose()(ok <- results$error %>% map_lgl(is_null))
#> [1]  TRUE FALSE  TRUE
(bad_inputs <- x %>% discard(ok))
#> [[1]]
#> [1] "a"
(successes <- results$result %>% keep(ok) %>% flatten_dbl())
#> [1] 2.302585 1.609438

一般分3步:

  1. 保留转化好的含有result 与 error 的列表
  2. 使用map_lgl(is_null)获取逻辑向量
  3. 根据逻辑向量与列表得到想要的数

purrr 0.2.0相关推荐

  1. libgstreamer-1.0.so.0: cannot open shared object file: No such file or directory

    1. 问题现象 error while loading shared libraries: libgstreamer-1.0.so.0: cannot open shared object file: ...

  2. c+语言+null,C/C++语言中NULL、'\0’和0的区别

    NULL.'\0'和0的值是一样的,都是0,不过它们的表现形式不一样: 1. NULL: 即空指针,不过在C和C++中并不一样.在VS 2013的库文件string.h中可以看到如果定义. 1 /* ...

  3. Ubuntu14.04 64位机上安装cuda8.0+cudnn5.0操作步骤

    查看Ubuntu14.04 64位上显卡信息,执行: lspci | grep -i vga lspci -v -s 01:00.0 nvidia-smi 第一条此命令可以显示一些显卡的相关信息:如果 ...

  4. Spring Cloud Alibaba 基础教程:Nacos 生产级版本 0.8.0

    Spring Cloud Alibaba 基础教程:Nacos 生产级版本 0.8.0 昨晚Nacos社区发布了第一个生产级版本:0.8.0.由于该版本除了Bug修复之外,还提供了几个生产管理非常重要 ...

  5. Silverlight 3发布新版3.0.50106.0

    微软1月19日发布Silverlight 3新版本3.0.50106.0. 该版本主要修复以下几个问题: 问题一: 当使用图形硬件加速功能(GPU)的时候,如果GPU驱动报错,Silverlight ...

  6. AS1.0(2.0)中的XML示例

    虽然Flash早就升级为AS3.0,但是FMS的服务端编程依然仅支持AS1.0(2.0),服务端与.net通讯的最简单方式莫过于请求一个RESTful的webService或wcf,通过它们返回的xm ...

  7. 多数编程语言里的0.1+0.2≠0.3?

    作者 | Parul Malhotra 译者 | Raku 出品 | AI科技大本营(ID:rgznai100) 我们从小就被教导说0.1+0.2=0.3,但是在奇妙的计算机编程世界里面,事情变得不一 ...

  8. flannel原理初探针对0.1.0版本

    flannel flannel是针对k8s设计的三层的网络解决方案.在k8s中为了使pod之间能够使用一种偏平的网络架构,从而完成跨Pod的网络通信. 官网给的原理图如下: flannel 使用TUN ...

  9. GTX 1080Ti + cuda8.0 + cuDNN6.0 安装及测试

    GPU 显卡厂商已经安装好了,直接安装 cuda8.0 + cuDNN6.0 我这里的显卡是 GTX 1080 Ti cuda安装 我下载的是cuda8.0的是deb格式的1.9个G地址:https: ...

最新文章

  1. 自定义Kubernetes调度程序来编排高可用性应用程序
  2. 基于python的人工智能Jiagu深度学习自然语言处理开源工具
  3. centos7 docker 安装 otter 注意事项
  4. ASP.NET MVC5+EF6+EasyUI 后台管理系统(47)-工作流设计-补充
  5. python装饰器函数-python 装饰器 函数被装饰+函数执行
  6. Android Configuration change引发的问题及解决方法
  7. 微信平台开发1--开发者模式基本配置
  8. Maven 使用 Tomcat7
  9. TensorFlow学习笔记(十二)TensorFLow tensorBoard 总结
  10. 打钱!我的数据库被黑客勒索了!
  11. 记一次微信数据库解密过程
  12. VMware 修复 Workstation、Fusion 和 ESXi中的多个漏洞
  13. Jquery的jqzoom插件的使用(图片放大镜)
  14. 一些实用但不为人知的Unix命令
  15. Elaine的python初学习
  16. 艺多不压身—摩尔斯电码
  17. 因为改 UOM conversion 导致库存数量和財务上的数据错误
  18. GD32F330+DS18B20
  19. 突破生命法则极限!它会是外星生命的遗传密码?
  20. DeFi热潮下的安全隐患:流动性危机恐将造成连锁反应 | 非正式会谈

热门文章

  1. 树莓派B+使用入门RPI库安装wringPi库安装
  2. Java学习笔记-1.简介
  3. SQL基础实例(学生课程系统)
  4. 【Java】Springboot项目中jar包加密
  5. linux运维高频命令汇总
  6. 03-19 分布式测试-Selenium Grid
  7. html中的数字选框,带有复选框和数字类型的HTML表单提交与PHP?
  8. c#用canny算子做边缘提取_干货 | 边缘检测
  9. knn的python代码_详细的的KNN代码——python实现
  10. compose部署redis和mysql_浅析docker-compose部署mysql无法访问的问题