有什么理由我应该使用
map(<list-like-object>, function(x) <do stuff>)
代替
lapply(<list-like-object>, function(x) <do stuff>)
输出应该是相同的,并且我所做的基准测试似乎表明它lapply稍微快一些(它应该是map评估所有非标准评估输入的需要)。
lapply
map
那么,对于这种简单的情况,我是否应该考虑切换到purrr::map. 我不是在这里询问一个人对语法、purrr 提供的其他功能等的好恶,而是严格地与使用标准评估的假设进行比较purrr::map,lapply即map(<list-like-object>, function(x) <do stuff>). purrr::map在性能、异常处理等方面有什么优势吗?下面的评论表明它没有,但也许有人可以详细说明一下?
purrr::map
如果您在 purrr 中使用的唯一功能是map(),那么不,优势并不显着。正如 Rich Pauloo 所指出的,它的主要优点map()是帮助程序,它允许您为常见的特殊情况编写紧凑的代码:
map()
~ . + 1相当于function(x) x + 1(\(x) x + 1在 R-4.1 和更新版本中)
~ . + 1
function(x) x + 1
\(x) x + 1
list("x", 1)相当于function(x) x[["x"]][[1]]。这些帮助器比[[- 更多信息请参阅?pluck。对于数据矩形,该 .default参数特别有用。
list("x", 1)
function(x) x[["x"]][[1]]
[[
?pluck
.default
但是大多数时候你不是在使用单个*apply()/map() 函数,而是在使用一堆,而 purrr 的优点是函数之间的一致性更高。例如:
*apply()
第一个参数lapply()是数据;第一个参数 mapply()是函数。所有地图函数的第一个参数始终是数据。
lapply()
mapply()
使用vapply(), sapply(),mapply()您可以选择使用USE.NAMES = FALSE;隐藏输出中的名称。但 lapply()没有那个论点。
vapply()
sapply()
USE.NAMES = FALSE
没有一致的方法可以将一致的参数传递给映射器函数。大多数函数使用...but mapply()uses MoreArgs(您希望被称为MORE.ARGS)和 Map(),Filter()并Reduce()希望您创建一个新的匿名函数。在 map 函数中,常量参数总是在函数名之后。
...
MoreArgs
MORE.ARGS
Map()
Filter()
Reduce()
几乎每个 purrr 函数都是类型稳定的:您可以仅根据函数名称预测输出类型。sapply()对于or来说,这不是真的 mapply()。是的,有vapply();但mapply().
您可能认为所有这些细微的区别并不重要(就像有些人认为 stringr 与基本 R 正则表达式相比没有优势),但根据我的经验,它们在编程时会引起不必要的摩擦(不同的参数顺序总是会绊倒我起来了),而且它们使函数式编程技术更难学习,因为除了伟大的想法之外,你还必须学习一些附带的细节。
Purrr 还填充了一些基本 R 中没有的方便的地图变体:
modify()``[[<-保留用于“就地”修改的数据类型。结合_if变体,这允许(IMO 美丽的)代码,如modify_if(df, is.factor, as.character)
modify()``[[<-
_if
modify_if(df, is.factor, as.character)
map2()允许您同时映射到x和y。这使得表达想法变得更容易,比如 map2(models, datasets, predict)
map2()
x
y
map2(models, datasets, predict)
imap()允许您同时映射x及其索引(名称或位置)。这使得(例如)加载 csv目录中的所有文件变得容易,为每个文件添加一filename列。
imap()
csv
filename
dir("\\.csv$") %>%
set_names() %>% map(read.csv) %>% imap(~ transform(.x, filename = .y))
walk()不可见地返回其输入;并且在您调用函数以获取其副作用(即,将文件写入磁盘)时很有用。
walk()
更不用说像safely()and之类的其他助手了partial()。
safely()
partial()
就个人而言,我发现当我使用 purrr 时,我可以更轻松地编写函数式代码;它缩小了构思和实施之间的差距。但是您的里程可能会有所不同;除非确实对您有所帮助,否则无需使用 purrr。
是的,map()比lapply(). 但是使用 map()or的成本lapply()取决于您所映射的内容,而不是执行循环的开销。下面的微基准表明,与每个元素map()相比的成本lapply()约为 40 ns,这似乎不太可能对大多数 R 代码产生重大影响。
library(purrr) n <- 1e4 x <- 1:n f <- function(x) NULL mb <- microbenchmark::microbenchmark( lapply = lapply(x, f), map = map(x, f) ) summary(mb, unit = "ns")$median / n #> [1] 490.343 546.880