Chapter 10 功能化函数编程purrr
purrr
是R进行函数化编程(functional programing)的包.
10.1 安装
purrr
包在tidyverse
包中,所以可以直接通过安装tidyverse
包进行安装.当然,也可以单独安装.
10.2 map
函数系列
purrr
包提供了很多map
系列的函数,跟R的base apply家族函数有点像,都是对向量化的对象的每一个元素进行处理.map(.x, .f)
,其中.x
是向量化的对象,比如向量,list等,然后对每一个元素使用.f
函数进行处理,得到结果之后会返回一个跟原来.x
长度相同的对象.返回对象的格式跟map
函数的后缀有关,比如_lgl
,_chr()
等.
在R中,向量包括两类,atomic和list,二者区别在于前者的元素类型必须相同,而后者可以不同,前者的代表为向量和矩阵,而后者为list和数据框.
比如map()
函数,用法如下:
.x
是第一个参数,是一个list或者一个atomic vector
..f
是一个函数,一个公式或者是向量....
则是函数的其他参数.
我们举个例子:
## [[1]]
## [1] 3.762651
##
## [[2]]
## [1] 1.482415 3.753958
##
## [[3]]
## [1] 2.819546 3.798441 2.240516
这时候.x
是一个向量.函数是rnorm
.如果不对函数进行特别说明,则向量的每个元素默认都是函数的第一个参数,然后后面的参数是其他参数.如果在后面对第一参数进行设置,这时候前面的向量则按顺序向后移动.比如:
## [[1]]
## [1] 0.173504 1.844842 1.884314
##
## [[2]]
## [1] 1.160271 2.520271 2.214823
##
## [[3]]
## [1] 4.374233 2.225759 3.481143
这时候,因为在后面设置了n = 3
,因此这时候向量就是rnorm
的第二个参数mean了.
对于比较复杂的参数,不太推荐这样的写法,更推荐使用匿名函数,然后将参数位置进行固定:
## [[1]]
## [1] 0.08069196 1.62799499 1.93824957
##
## [[2]]
## [1] 1.736541 2.797254 2.606457
##
## [[3]]
## [1] 2.052848 3.339062 1.852975
当然,也可以是一个公式:
## [[1]]
## [1] 1.4893993 2.8728652 0.5825109
##
## [[2]]
## [1] 1.8248339 0.5420468 1.4399422
##
## [[3]]
## [1] 2.896965 3.190717 5.415523
这时候参数需要使用.x
作为占位符.
对于数据框来说,其实也可以看做是一个list,每一列就是一个list中的一个元素.
## V1 V2 V3 V4
## 1 1 5 9 13
## 2 2 6 10 14
## 3 3 7 11 15
## 4 4 8 12 16
## $V1
## [1] 2.5
##
## $V2
## [1] 6.5
##
## $V3
## [1] 10.5
##
## $V4
## [1] 14.5
也可以做一些复杂的运算,比如进行scale.
## V1 V2 V3 V4
## 1 1 5 9 13
## 2 2 6 10 14
## 3 3 7 11 15
## 4 4 8 12 16
## $V1
## [1] -1.1618950 -0.3872983 0.3872983 1.1618950
##
## $V2
## [1] -1.1618950 -0.3872983 0.3872983 1.1618950
##
## $V3
## [1] -1.1618950 -0.3872983 0.3872983 1.1618950
##
## $V4
## [1] -1.1618950 -0.3872983 0.3872983 1.1618950
## $foo
## [1] "foo:suffix"
##
## $bar
## [1] "bar:suffix"
从上面的可以看到,使用map
函数,最后返回的结果毕竟是一个list.如果想要返回其他的类型呢?这时候需要使用map_
加上后缀名的函数,在purrr
中一共有以下几种:
map_lgl()
,map_int()
,map_dbl()
和map_chr()
返回的分别是logical,integer,dbl和character类型的数据.
l1 <- list(list(a = 1L), list(a = NULL, b = 2L), list(b = 3L))
map(.x = l1, .f = "a", .default = "???")
## [[1]]
## [1] 1
##
## [[2]]
## [1] "???"
##
## [[3]]
## [1] "???"
另外,函数还可以是一个列名或者向量某个元素的名字,也就是使用位置参数对其进行提取.比如上面的例子,就是使用函数“a”,也就是提取对象中名字为“a”的元素的名字,其实也就是函数[[
,如果没有该元素,则使用函数.default
的值进行填充.当然,还可以使用位置来进行提取.
## [1] NA 2 3
## [1] NA 2 NA
data <-
mtcars %>%
split(.$cyl)
data <- map(.x = data, .f = ~ lm(mpg ~ wt, data = .x))
data <- map(.x = data, .f = summary)
map(.x = data, .f = "r.squared")
## $`4`
## [1] 0.5086326
##
## $`6`
## [1] 0.4645102
##
## $`8`
## [1] 0.4229655
## 4 6 8
## 0.5086326 0.4645102 0.4229655
所以,可以看出来,其实这几个函数都是在map
的基础上对返回的数值进行一定的修饰得到不同类型结果而已.
map_dfr()
和map_dfc()
函数.
这两个函数其实就是将map()
返回的list对象整理为data frame对象.分别按照行和列进行整合.
举个例子:
## V1 V2 V3 V4 V5
## 1 3 4 19 1 14
## 2 13 10 2 6 20
## 3 11 5 7 9 15
## 4 12 8 16 17 18
## $V1
## [1] -1.4759020 0.7106195 0.2733152 0.4919673
##
## $V2
## [1] -0.9986254 1.1801937 -0.6354889 0.4539206
##
## $V3
## [1] 1.0160010 -1.1430011 -0.5080005 0.6350006
##
## $V4
## [1] -1.0817683 -0.3357212 0.1119071 1.3055824
##
## $V5
## [1] -0.9986254 1.1801937 -0.6354889 0.4539206
## # A tibble: 4 x 5
## V1 V2 V3 V4 V5
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 -1.48 -0.999 1.02 -1.08 -0.999
## 2 0.711 1.18 -1.14 -0.336 1.18
## 3 0.273 -0.635 -0.508 0.112 -0.635
## 4 0.492 0.454 0.635 1.31 0.454
## # A tibble: 4 x 5
## V1 V2 V3 V4 V5
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 -1.48 -0.999 1.02 -1.08 -0.999
## 2 0.711 1.18 -1.14 -0.336 1.18
## 3 0.273 -0.635 -0.508 0.112 -0.635
## 4 0.492 0.454 0.635 1.31 0.454
walk()
函数,称之为游走函数
当使用函数的目的是向屏幕提供输出或将文件保存到磁盘——重要的是操作过程而不是返回值,我们应该使用游走函数,而不是映射函数。
比如最简单的例子:
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
另外一种是保存文件到本地.这时候就可以使用walk
函数.
10.3 map
变种函数
和map()
函数类似,purrr
提供了一系列map
函数的变种函数.
10.3.1 map2
系列函数
map2()
可以多提供一个输入变量.然后对其同时进行处理.
用法如下:
map2(.x, .y, .f, ...)
map2_lgl(.x, .y, .f, ...)
map2_int(.x, .y, .f, ...)
map2_dbl(.x, .y, .f, ...)
map2_chr(.x, .y, .f, ...)
map2_raw(.x, .y, .f, ...)
map2_dfr(.x, .y, .f, ..., .id = NULL)
map2_dfc(.x, .y, .f, ...)
.x
和.y
是两个长度相等的向量.如果其中一个长度为1,那么会对其进行循环..f
还是对向量进行处理的函数.
举个例子:
## [[1]]
## [1] 2
##
## [[2]]
## [1] 12
##
## [[3]]
## [1] 103
## [[1]]
## [1] 2
##
## [[2]]
## [1] 12
##
## [[3]]
## [1] 103
10.3.2 pmap
和pwalk
系列函数
pmap()
和pwalk()
比较特殊,他们用法如下:
pmap(.l, .f, ...)
pmap_lgl(.l, .f, ...)
pmap_int(.l, .f, ...)
pmap_dbl(.l, .f, ...)
pmap_chr(.l, .f, ...)
pmap_raw(.l, .f, ...)
pmap_dfr(.l, .f, ..., .id = NULL)
pmap_dfc(.l, .f, ...)
pwalk(.l, .f, ...)
其中.l
是指一个列表(list).长度可以为任意值.如果输入的是一个data frame,那么他们是按照行进行处理的.这时候需要和map
系列函数区分,以为他们是按照列进行处理的.
比如下面的例子:
## [[1]]
## [1] 7
##
## [[2]]
## [1] 62
##
## [[3]]
## [1] 603
分别将x,y和z的第一个元素,第二个元素和第三个元素相加得到最终的结果.
## [[1]]
## [1] 0.1666667
##
## [[2]]
## [1] 0.1923077
##
## [[3]]
## [1] 0.1988072
allow you to provide any number of arguments in a list.
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10