我有什么理由使用
map(<list-like-object>, function(x) <do stuff>)
而不是
lapply(<list-like-object>, function(x) <do stuff>)
输出应该是相同的,我做的基准似乎表明lapply
稍微快一些(应该是map
需要评估所有非标准评估输入)。 / p>
为什么有这么简单的情况我应该考虑切换到purrr::map
?我不是在这里询问关于语法,purrr等提供的其他功能的喜欢或不喜欢,而是严格关于purrr::map
与lapply
的比较,假设使用标准评估,即{ {1}}。 map(<list-like-object>, function(x) <do stuff>)
在性能,异常处理等方面是否有任何优势?下面的评论表明它没有,但也许有人可以详细说明一点?
答案 0 :(得分:197)
如果你在purrr中使用的唯一功能是map()
,那么不,
优势并不大。正如Rich Pauloo指出的那样,主要是
map()
的优点是帮助您编写紧凑的帮助程序
常见特殊情况的代码:
~ . + 1
相当于function(x) x + 1
list("x", 1)
相当于function(x) x[["x"]][[1]]
。这些
帮助者比[[
更为通用 - 有关详细信息,请参阅?pluck
。
对于data
rectangling,
.default
参数特别有帮助。
但大部分时间你都没有使用单*apply()
/ map()
功能,你正在使用它们,而purrr的优点是
功能之间的一致性更高。例如:
lapply()
的第一个参数是数据;第一个参数
mapply()
就是这个功能。所有地图函数的第一个参数
始终是数据。
您可以选择vapply()
,sapply()
和mapply()
使用USE.NAMES = FALSE
抑制输出上的名称;但
lapply()
没有那个论点。
没有一致的方法可以将一致的参数传递给
映射器功能。大多数函数使用...
但mapply()
使用
MoreArgs
(您希望将其称为MORE.ARGS
),以及
Map()
,Filter()
和Reduce()
希望您创建一个新的
匿名功能。在map函数中,常常有参数
在函数名称之后。
几乎每个purrr函数都是类型稳定的:你可以预测
输出类型仅来自函数名称。事实并非如此
sapply()
或mapply()
。是的,有vapply()
;但没有
相当于mapply()
。
您可能认为所有这些微小的区别并不重要 (就像有些人认为串起来没有优势一样 基本R正则表达式),但根据我的经验,他们造成不必要的 编程时的摩擦(不同的参数顺序总是习惯于 让我感到高兴,他们使函数式编程技术更难 学习因为以及伟大的想法,你还必须学习一堆 附带细节。
Purrr还填写了基础R中缺少的一些方便的地图变体:
modify()
使用[[<-
修改“in”来保留数据类型
放置“。与_if
变体一起允许(IMO
美丽的代码,如modify_if(df, is.factor, as.character)
map2()
可让您同时在x
和y
上进行映射。这个
这样可以更容易地表达想法
map2(models, datasets, predict)
imap()
允许您同时映射x
及其索引
(姓名或职位)。这使得(例如)加载所有容易
目录中的csv
个文件,为每个文件添加filename
列。
dir("\\.csv$") %>%
set_names() %>%
map(read.csv) %>%
imap(~ transform(.x, filename = .y))
walk()
无形地返回其输入;并且当你有用时很有用
为其副作用调用函数(即将文件写入
磁盘)。
更不用说其他帮助者,例如safely()
和partial()
。
就个人而言,我发现当我使用purrr时,我可以编写功能代码 摩擦力更小,更轻松;它缩小了之间的差距 思考一个想法并实施它。但你的里程可能会有所不同 没有必要使用purrr,除非它真的帮助你。
是的,map()
略慢于lapply()
。但使用成本
map()
或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
答案 1 :(得分:44)
This online purrr tutorial强调了方便性,在使用purrr时不必显式写出匿名函数,这与特定于类型的地图函数一起使其非常实用。
purrr::map
在语法上比lapply 提取列表的第二个元素
map(list, 2) # and it's done like magic
作为@F。 Privé指出,与:
相同map(list, function(x) x[[2]])
lapply
lapply(list, 2) # doesn't work
我们需要传递匿名函数
lapply(list, function(x) x[[2]]) # now it works
或@RichScriven指出,我们只需将[[
作为参数传递给lapply
lapply(list, `[[`, 2) # a bit more simple syntantically
在后台,purr
将数字或字符向量作为参数,并将其用于子集。如果发现自己将函数应用于许多,使用lapply
的列表,并且厌倦了定义自定义函数或编写匿名函数,那么便利是转向purrr的一个原因。
map_chr()
map_lgl()
map_int()
map_dbl()
map_df()
- 我最喜欢的,返回一个数据框。 这些特定于类型的映射函数中的每一个都返回一个原子列表,而不是map()
和lapply()
自动返回的列表。如果您正在处理具有原子向量的嵌套列表,您可以使用这些特定于类型的映射函数直接提取向量,或将向量强制转换为int,dbl,chr向量。方便和功能的另一点。
lapply
比map
快。使用purrr
的便利功能,如@F。 Privé指出减慢了处理速度。让我们对上面提到的4个案例中的每一个进行比赛。
# devtools::install_github("jennybc/repurrrsive")
library(repurrrsive)
library(purrr)
library(microbenchmark)
library(ggplot2)
mbm <- microbenchmark(
lapply = lapply(got_chars[1:4], function(x) x[[2]]),
lapply_2 = lapply(got_chars[1:4], `[[`, 2),
map_shortcut = map(got_chars[1:4], 2),
map = map(got_chars[1:4], function(x) x[[2]]),
times = 100
)
autoplot(mbm)
获胜者是......
lapply(list, `[[`, 2)
总而言之,如果你正在追求速度: base::lapply
如果您遇到简单的语法: purrr::map
答案 2 :(得分:25)
如果我们不考虑品味的方面(否则这个问题应该被关闭)或语法一致性,风格等,答案是否定的,没有特别的理由使用map
代替lapply
或申请系列的其他变体,例如更严格的vapply
。
PS:对于那些无偿贬低的人,只记得OP写道:
我不是在这里询问有关语法的喜欢或不喜欢, purrr等提供的其他功能,但严格来说 假设使用标准,purrr :: map与lapply的比较 评价
如果您不考虑purrr
的语法或其他功能,那么使用map
没有特别的理由。我自己使用purrr
而且我对Hadley的回答很好,但具有讽刺意味的是,OP先前提到的并没有提出要求的事情。