purrr | 使用map族函数进行向量化运算

前面介绍的进行向量化运算的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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值