Chapter 10 功能化函数编程purrr

purrr是R进行函数化编程(functional programing)的包.

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类型的数据.
## [[1]]
## [1] 1
## 
## [[2]]
## [1] "???"
## 
## [[3]]
## [1] "???"

另外,函数还可以是一个列名或者向量某个元素的名字,也就是使用位置参数对其进行提取.比如上面的例子,就是使用函数“a”,也就是提取对象中名字为“a”的元素的名字,其实也就是函数[[,如果没有该元素,则使用函数.default的值进行填充.当然,还可以使用位置来进行提取.

## [1] NA  2  3
## [1] NA  2 NA
## $`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()可以多提供一个输入变量.然后对其同时进行处理.

用法如下:

.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 pmappwalk系列函数

pmap()pwalk()比较特殊,他们用法如下:

其中.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