R:purrr包用于循环迭代

2020-07-16 10:50:43 浏览数 (1)

purrr中有多个迭代函数,可以用于快速解决循环迭代的问题,purrr中常用的迭代函数有map、map2、walk、reduce等等。

map

代码语言:javascript复制
map(.x, .f, ...)

map函数接受一个向量、列表,对其每一个元素执行函数。

数据框其实是一种格式化表示的列表,所以也可以使用map迭代。

map函数默认返回列表。

代码语言:javascript复制
iris %>% map(mean)
# $Sepal.Length
# [1] 5.843333
# 
# $Sepal.Width
# [1] 3.057333
# 
# $Petal.Length
# [1] 3.758
# 
# $Petal.Width
# [1] 1.199333
# 
# $Species
# [1] NA

如果返回值需要是一个数值向量,而不是列表,也可以使用map_dbl、map_chr、map_lgl等形式,代表分别返回数值、字符或者逻辑值。

代码语言:javascript复制
iris %>% map_dbl(mean)
#Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
#   5.843333     3.057333     3.758000     1.199333           NA

可以看到Species不是数值是无法求mean,那么此时可以使用map_if或者map_at跳过Species。

注意:此时的跳过是指的不对跳过的列执行函数mean,直接将其内容返回。

代码语言:javascript复制
iris %>% map_at(1:4, mean) # 只对前四列计算mean

对于map_if而言可以使用.else参数控制跳过的列的执行函数。

代码语言:javascript复制
iris %>% map_if(is.numeric, mean, .else = ~"不适用")
#$Sepal.Length
#[1] 5.843333
#$Sepal.Width
#[1] 3.057333
#$Petal.Length
#[1] 3.758
#$Petal.Width
#[1] 1.199333
#$Species
#[1] "不适用"

如果还有其他参数,放到函数后边即可:

代码语言:javascript复制
iris %>% map_dbl(mean, na.omit=T)

.f有三个快捷方式:公式、字符及数字。

代码语言:javascript复制
# 公式:用于简化R的匿名函数格式
# 例如如下两种方式是等价的
iris %>% map(function(x) mean(x, na.omit=T))
iris %>% map(~mean(., na.omit=T))

# 字符:用于快速提取内容
# 例如如下两种方式是等价的
iris %>% dplyr::select(-Species) %>% map(summary) %>%map_dbl(~.["Median"])
iris %>% dplyr::select(-Species) %>% map(summary) %>%map_dbl("Median")

# 数字:也是用于快速提取内容,按位置取值
iris %>% dplyr::select(-Species) %>% map(summary) %>% map_dbl(3)

map2

代码语言:javascript复制
map2(.x, .y, .f, ...)

map2可以对两个向量、列表同时进行迭代。

代码语言:javascript复制
x <- 1:3
y <- list(4,5,6)
map2(x,y,rnorm,n=5)
# [[1]]
# [1] -1.583531618 -4.480785670  0.008262269  0.452103825  4.257641158
# 
# [[2]]
# [1]  2.835951 -2.392248  2.874423 -2.714616  9.362894
# 
# [[3]]
# [1] -6.5547160 -4.3619104 -5.5202971 -0.1914993  2.2787720

可以同时对列表的名称和内容进行迭代,先将列表使用enframe转换为一个tibble:

代码语言:javascript复制
x <- 1:3
y <- list(4,5,6)
paras <- list(mean=x, sd=y)
paras <- tibble::enframe(paras)
## A tibble: 2 x 2
#  name  value     
#  <chr> <list>    
#1 mean  <int [3]> 
#2 sd    <list [3]>
map2(paras$name, paras$value, ~stringr::str_c(.x, .y[[1]], collapse=""))
#[[1]]
#[1] "mean1"
#
#[[2]]
#[1] "sd4"

map2在使用管道时,可以使用%$%:

代码语言:javascript复制
library(magrittr)
list(mean=1:3, sd=4:6) %>% 
  tibble::enframe() %$% 
  map2(name, value, ~stringr::str_c(.x, .y[[1]], collapse=""))
#[[1]]
#[1] "mean1"
#
#[[2]]
#[1] "sd4"

pmap

代码语言:javascript复制
pmap(.l, .f, ...)

pmap代表对多个列表进行迭代,将多个列表包装为一个列表传给.f可。

代码语言:javascript复制
x <- 1:3
y <- list(4,5,6)
z <- list(1,2,3)
pmap(list(mean=x, sd=y, n=z), rnorm)
#相当于rnorm(mean=1, sd=4, n=1)、rnorm(mean=2, sd=5, n=2)、rnorm(mean=3, sd=6, n=3)

#[[1]]
#[1] 6.224277
#
#[[2]]
#[1] 12.32054 10.57245
#
#[[3]]
#[1] 6.593741 1.389408 3.541097

invoke_map

代码语言:javascript复制
invoke_map(.f, .x = NULL, ..., .env = NULL)

invoke_map用于函数(.f参数)也不固定的情况。

代码语言:javascript复制
x <- list(n = 3)
y <- list(n = 5, lambda = 10)
z <- list(n = 3, mean = -3, sd = 10)
fun <- c("runif", "rpois", "rnorm")
invoke_map(fun, list(x,y,z))
#相当于runif(n=3)、rpois(n=5, lambda=10)、rnorm(n=3, mean=-3, sd=10)

#[[1]]
#[1] 0.09462862 0.54110640 0.87654702
#
#[[2]]
#[1] 10  6  5  8  9
#
#[[3]]
#[1] -20.92633 -14.80254 -16.16675

walk

除了上述的map系列函数,还有一批形式类似的walk函数,如walk、walk2、pwalk等等,他们用于一些不需要返回值的操作,如绘图和读写文件。

如果此时使用map系列函数,那么就会返回一个值为NULL的列表。

代码语言:javascript复制
df <- data.frame(plot1 = rnorm(5,1,1), 
                 plot2 = rnorm(5,2,3), 
                 plot3 = rnorm(5,4,5))
map(df, plot) #除了绘制三张散点图外,还会返回一个值为NULL的列表
#$plot1
#NULL
#$plot2
#NULL
#$plot3
#NULL
walk(df, plot) #只绘图,没有返回值

reduce

reduce是一个特殊的迭代函数,它执行一种“积累”的操作,如累加、累乘:

代码语言:javascript复制
reduce(1:100, ` `)
#[1] 5050
reduce(1:5, `*`)
#[1] 120

有些时候,reduce是很方便的,比如可以使用它计算数据框的最大值和最小值:

代码语言:javascript复制
reduce(iris, min) #0.1
reduce(iris, max) #7.9

和reduce类似的一个函数是accumulate,它会将中间每一个迭代过程记录下来:

代码语言:javascript复制
head(iris) %>% accumulate(min)

#$Sepal.Length
#[1] 5.1 4.9 4.7 4.6 5.0 5.4
#$Sepal.Width
#[1] 3
#$Petal.Length
#[1] 1.3
#$Petal.Width
#[1] 0.2
#$Species
#[1] 0.2

0 人点赞