前面介绍的进行向量化运算的apply
族函数来自基础包base
,而本文介绍的map
族函数则来自purrr
包,是tidyverse
家族的一员。与apply
相比,map
函数在逻辑上更易于理解,并且支持匿名函数和格式化输出结果。
1 map()函数
map()
函数相当于base
中的apply()
函数,适用于单参数向量化运算,语法结构如下:
map(.x, .f, ...)
map()
函数的语法结构比较简单,.x
是以某种结构储存的多组数据,.f
是应用于.x
中每组数据的的函数,...
为该函数的其他参数;map()
自带的参数名称带有前缀.
,可以和继承自.f
中的参数名相区别;.x
适用结构为向量或list,以及其他可强制转化为这两种结构的其他数据结构。
比如,使用正态随机数生成函数rnorm()
生成多组随机数,rnorm()
的语法结构:
rnorm(n, mean = 0, sd = 1)
均值
mean
和方差sd
固定,数目分别为3、5和7
library(purrr)
# 均值和方差取默认值
map(c(3,5,7), rnorm)
# 自定义均值和方差
map(c(3,5,7), rnorm, mean = 10, sd = 5)
# 部分输出结果
# 均值和方差取默认值
map(c(3,5,7), rnorm)
## [[1]]
## [1] -1.038812 1.027207 2.454847
##
## [[2]]
## [1] -0.6810901 -0.4481308 1.8273113 1.4177181 0.8114058
##
## [[3]]
## [1] -2.1307517 -0.8296705 1.7878942 -0.5691242 -0.7011114 0.8457154 0.6703464
# 自定义均值和方差
map(c(3,5,7), rnorm, mean = 10, sd = 5)
## [[1]]
## [1] 13.14507 11.74804 10.49418
##
## [[2]]
## [1] 12.640459 15.387362 10.467402 9.825429 15.142579
##
## [[3]]
## [1] 7.729443 16.371932 9.790515 7.922366 11.207658 15.414541 17.237151
随机数数目和方差固定,均值分别为3、5和7(即以第二个参数为向量化运算依据)
map(c(3,5,7), rnorm, n = 5, sd = 1)
# 输出结果
map(c(3,5,7), rnorm, n = 5, sd = 1)
## [[1]]
## [1] 3.259574 2.455316 2.995261 3.672239 2.100866
##
## [[2]]
## [1] 4.836467 4.988217 4.105124 5.106423 5.618277
##
## [[3]]
## [1] 7.483986 6.890507 7.041464 7.348307 6.810796
与apply()
不同的是,map()
中没有MARGIN
参数,即指定运算的维度,因为.x
的标准结构为向量或list,而二者本身就都只有一个维度。像矩阵、数据框这样的多维数据结构,放在.x
位置上函数会将其强制转换为list再进行计算。比较下面两句代码输出结果长度的区别:
map(mtcars, mean, na.rm = T) %>% length()
map(as.matrix(mtcars), mean, na.rm = T) %>% length()
# 输出结果
map(mtcars, mean, na.rm = T) %>% length()
## [1] 11
map(as.matrix(mtcars), mean, na.rm = T) %>% length()
## [1] 352
数据框转化为list,会将每列作为list的一个元素,因此是个长度为11的list;
矩阵转换为list,它的每个元素都会被当作list的一个元素,因此是长度为352的list。
2 map2()函数
map2()
函数适用于两个参数的向量化运算,语法结构如下:
map2(.x, .y, .f, ...)
比如分别以8、9、10为均值,1、2、3为方差,生成5个符合正态分布的随机数:
map2(.x = c(8:10), .y = c(1:3), rnorm, n = 5)
# 部分输出结果
map2(.x = c(8:10), .y = c(1:3), rnorm, n = 5)
## [[1]]
## [1] 8.110894 8.865796 8.695877 6.616142 10.102844
##
## [[2]]
## [1] 8.591896 9.759056 7.711670 8.613685 6.816343
##
## [[3]]
## [1] 9.270238 7.982870 5.706108 5.289253 8.178568
3 imap()函数
imap()
函数是map2()
函数的特殊形式。它不需要.y
参数,而是自动将.x
参数中的数据的行名(无行名时使用行序号)作为.y
参数。语法结构如下:
imap(.x, .y, .f, ...)
x <- y <- c(1:5)
names(x) <- LETTERS[1:5]
imap(x, paste, sep = "-")
imap(y, paste, sep = "-")
# 部分输出结果
imap(x, paste, sep = "-")
## $A
## [1] "1-A"
##
## $B
## [1] "2-B"
##
## $C
## [1] "3-C"
##
## $D
## [1] "4-D"
##
## $E
## [1] "5-E"
imap(y, paste, sep = "-")
## [[1]]
## [1] "1-1"
##
## [[2]]
## [1] "2-2"
##
## [[3]]
## [1] "3-3"
##
## [[4]]
## [1] "4-4"
##
## [[5]]
## [1] "5-5"
4 pmap()函数
pmap()
函数适用于多参数的向量化运算,语法结构如下:
pmap(.l, .f, ...)
.l
是list或数据框结构,它的每个元素或每列代表一个参数。如下:
pmap(list(n = c(5:7), mean = c(8:10), sd = c(1:3)), rnorm)
pmap(data.frame(n = c(5:7), mean = c(8:10), sd = c(1:3)), rnorm)
# 输出结果
pmap(list(n = c(5:7), mean = c(8:10), sd = c(1:3)), rnorm)
## [[1]]
## [1] 6.656474 8.539044 8.478001 7.899201 8.822755
##
## [[2]]
## [1] 10.559883 8.851182 11.147228 10.385334 8.003723 12.515218
##
## [[3]]
## [1] 12.455521 17.256238 8.903542 12.270363 10.457554 8.922254 9.615747
pmap(data.frame(n = c(5:7), mean = c(8:10), sd = c(1:3)), rnorm)
## [[1]]
## [1] 9.260113 10.188132 7.431212 8.308398 8.629498
##
## [[2]]
## [1] 7.987754 7.438008 10.994367 7.519215 8.044307 8.215979
##
## [[3]]
## [1] 14.061643 8.722118 9.080568 13.230390 6.569461 8.304886 10.003861
数据框必须有列名且列名和函数参数名必须相同,每列位置不需要和函数参数位置相同。如下:
pmap(data.frame(mean = c(8:10), n = c(5:7), sd = c(1:3)), rnorm)
# 输出结果
pmap(data.frame(mean = c(8:10), n = c(5:7), sd = c(1:3)), rnorm)
## [[1]]
## [1] 7.896589 7.705419 9.389938 6.654332 7.556056
##
## [[2]]
## [1] 11.154945 9.829625 13.870022 7.362613 9.630347 10.574812
##
## [[3]]
## [1] 11.002222 12.790695 13.448934 11.703728 8.790828 8.213918 8.907189
list的元素可以不命名,没名称的元素按函数的参数顺序依次赋值。如下:
pmap(list(c(8:10), c(5:7), c(1:3)), rnorm)
# 输出结果
pmap(list(c(8:10), c(5:7), c(1:3)), rnorm)
## [[1]]
## [1] 6.342120 6.530479 6.652506 4.739046 5.135108 2.740261 4.981687 7.295926
##
## [[2]]
## [1] 2.934093 7.153562 8.201544 4.530186 6.784143 6.520672 3.520575 6.232855
## [9] 9.400873
##
## [[3]]
## [1] 6.233360 8.855587 2.006880 7.149994 8.276719 9.014692 8.386454
## [8] 6.154487 3.218129 10.840443
5 匿名函数
map()
族函数调用自定义函数时,可以使用~
代替函数function
。具体地,
一元函数
map(1:10, ~.x + 2)
# 等价于
map(1:10, function(x) x + 2)
二元函数
map2(1:10, 11:2, ~ .x ^ (.y))
# 等价于
map2(1:10, 11:2, function(x, y) x^y)
多元函数
pmap(list(c(1:10), c(2:11), c(3:12)), ~ ..1^2 + ..2^3 + ..3^4)
# 等价于
pmap(list(c(1:10), c(2:11), c(3:12)), function(x, y, z) x^2 + y^3 + z^4)
更多元函数的参数依次使用
..4
、..5
...等代替;匿名函数的参数名必须带前缀
.
且严格按照x、y和1、2、3...的顺序使用。
6 格式化输出结果
map
族函数默认输出结果的数据结构是list。以map()
函数为例,它还有其他几种形式:
map_lgl()
、map_int()
、map_dbl()
和map_chr()
函数将输出结果的变量分别转化为逻辑型、整型、双精度性和字符型格式;map_df()
、map_dfr()
和map_dfc()
函数将输出结果转为数据框结构,后两者分别按行和列将结果合并。
map_lgl(.x, .f, ...)
map_chr(.x, .f, ...)
map_int(.x, .f, ...)
map_dbl(.x, .f, ...)
map_dfr(.x, .f, ..., .id = NULL)
map_dfc(.x, .f, ...)
map_dfc(1:10, rnorm, n = 10, sd = 2)
# 输出结果
map_dfc(1:10, rnorm, n = 10, sd = 2)
## New names:
## * NA -> ...1
## * NA -> ...2
## * NA -> ...3
## * NA -> ...4
## * NA -> ...5
## * ...
## # A tibble: 10 x 10
## ...1 ...2 ...3 ...4 ...5 ...6 ...7 ...8 ...9 ...10
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0.520 3.30 5.67 6.50 5.39 6.99 11.0 7.42 8.68 9.70
## 2 -1.08 3.89 3.75 4.67 4.65 7.37 4.58 6.62 6.59 8.98
## 3 2.50 1.46 -0.0660 3.16 5.43 6.08 8.28 12.1 9.06 12.0
## 4 2.71 2.16 2.95 5.74 5.37 1.11 4.59 2.59 9.74 12.8
## 5 0.892 3.75 3.53 2.06 6.25 10.9 8.51 6.63 11.8 6.73
## 6 -2.76 2.55 1.35 4.31 3.56 7.40 5.86 8.14 11.7 8.26
## 7 -0.955 2.13 1.52 3.85 9.01 3.45 10.2 14.2 12.0 9.84
## 8 1.18 2.59 3.51 2.62 5.08 3.23 7.14 8.15 7.88 9.59
## 9 1.69 2.07 2.40 3.31 6.00 6.96 4.41 4.68 12.4 11.2
## 10 -2.04 -0.371 7.41 4.58 4.56 4.57 4.42 8.12 8.86 10.8
最后附上一张函数功能示意图:
图片来源于网络:https://blog.csdn.net/weixin_38008864/article/details/107572648