purrr相对于apply系列的优势
purrr相对于apply系列的优势
有没有任何理由让我使用map(
而不是lapply(
,输出应该是一样的,我做的基准测试似乎表明lapply
稍微快一点(这是因为map
需要评估所有非标准评估的输入)。因此,对于这种简单情况,有没有任何理由让我考虑切换到purrr::map
呢?我在这里询问的不是关于语法喜好或不喜好,purrr提供的其他功能等等,而仅仅是关于purrr::map
和lapply
的比较,假设使用标准评估,即map(
。在性能、异常处理等方面,purrr::map
有什么优势吗?下面的评论表明没有,但也许有人可以详细解释一下?
在这段内容中,作者提出了一个关于使用`purrr`的`map`函数和`apply`系列函数(如`lapply`和`vapply`)的比较的问题。作者指出,如果不考虑个人口味、语法一致性和样式等方面,那么使用`map`而不是`lapply`或其他`apply`系列函数,如更严格的`vapply`,没有特殊的原因。
作者补充说,如果不考虑`purrr`的语法和其他功能,没有特殊的原因使用`map`。作者自己使用`purrr`并对Hadley的回答感到满意,但讽刺的是,他回答的内容恰好涉及到了作者最初明确表示不想问的内容。
接着,作者提到自己是因为一个同事的脚本中充满了`map`函数,而他想要使用`future.apply::future_lapply`或`furrr::future_map`。现在他知道可以安全地将一个替换为另一个了。
这段内容是关于使用`purrr`的`map`函数和`apply`系列函数的比较。作者问道是否有必要担心在`lapply`和`map`之间的一些差异,或者可以在它们之间更或多或少地互换使用。作者得到了答案,可以安全地替换它们。
purrr与apply家族的比较主要涉及便利性和速度两个方面。
1. purrr::map在语法上比lapply更方便
提取列表的第二个元素
map(list, 2)
正如Privé指出的那样,这与以下代码是相同的:
map(list, function(x) x[[2]])
使用lapply
lapply(list, 2) # 无法工作
我们需要传递一个匿名函数……
lapply(list, function(x) x[[2]]) # 现在可以工作了
……或者如Privé所指出的,我们将[[作为参数传递给lapply
lapply(list, `[[`, 2) # 语法上更简单一些
因此,如果发现自己使用lapply对许多列表应用函数,并且厌倦了定义自定义函数或编写匿名函数,便利性是偏好purrr的原因之一。
2. 类型特定的map函数简化了很多代码
map_chr()
map_lgl()
map_int()
map_dbl()
map_df()
这些类型特定的map函数中的每一个都返回一个向量,而不是map()和lapply()返回的列表。如果你处理嵌套的向量列表,你可以使用这些类型特定的map函数直接提取向量,并将向量直接转换为int、dbl、chr向量。基本的R版本可能看起来像as.numeric(sapply(...))
,as.character(sapply(...))
等等。
map_
3. 便利性之外,lapply比map稍微更快
使用purrr的便利函数,如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
这个优秀的purrr教程强调了在使用purrr时不必显式编写匿名函数的便利性,以及类型特定的map函数的好处。
请注意,如果使用function(x) x[[2]]
而不仅仅是2
,它会更慢。所有这些额外的时间都是由于lapply不进行的检查而导致的。
你不需要使用匿名函数。[[是一个函数。你可以使用lapply(list, "[[", 3)
。
那是有道理的。这简化了使用lapply比purrr的语法。
as.numeric(sapply(...))
是一种奇怪的做法。使用vapply:vapply(..., FUN.VALUE = numeric(1))
。这是从apply函数返回向量的基本R方式,也强制执行类型(如果你的函数不返回正确的类型,会抛出错误)。这也比sapply/lapply性能更好,因为整个向量可以一次性分配。map_type的唯一额外优点是它对初学者更友好一点。
Purrr是一个R语言的包,它提供了一组函数,用于进行迭代运算和函数式编程。与R的apply家族函数相比,Purrr具有以下优点:
1. 代码紧凑:Purrr的map函数提供了一些辅助函数,可以用更紧凑的方式编写常见的特殊情况代码。
- 例如,使用`~ . + 1`可以代替`function(x) x + 1`。
- 使用`list("x", 1)`可以代替`function(x) x[["x"]][[1]]`。
- 这些辅助函数比`[[`更通用。
2. 一致性:Purrr的函数在参数和用法上更加一致。
- 所有map函数的第一个参数始终是数据,而不像lapply和mapply函数的参数顺序不一致。
- 使用vapply、sapply和mapply函数时,可以选择使用USE.NAMES = FALSE来取消输出的命名,而lapply函数没有这个参数。
- 没有一种一致的方式将参数传递给映射函数,大多数函数使用...,但mapply使用MoreArgs,而Map、Filter和Reduce函数需要创建一个新的匿名函数。在map函数中,常量参数始终在函数名称之后。
- 几乎每个purrr函数都是类型稳定的,可以从函数名称中预测输出类型,而sapply和mapply函数则不是。
3. 提供了一些有用的map变种函数,这些函数在基本的R语言中是缺失的。
- modify()函数通过使用[[<-对数据进行原地修改,从而保留了数据的类型。与_if变体一起使用,可以编写出优雅的代码,如modify_if(df, is.factor, as.character)。
- map2()函数允许同时对x和y进行映射,使得表达式如map2(models, datasets, predict)更容易表达。
- imap()函数允许同时对x和其索引(名称或位置)进行映射,可以方便地(例如)加载目录中的所有csv文件,并为每个文件添加一个filename列。
- walk()函数以不可见方式返回其输入,当需要调用一个函数来产生副作用时(例如将文件写入磁盘)很有用。
- 还有其他一些辅助函数,如safely()和partial()。
Purrr可以帮助我们更轻松地编写函数式的R代码,减少了编程时的摩擦和难度。虽然每个人的使用情况可能会有所不同,但除非真正有所帮助,否则没有必要使用Purrr。
此外,有人可能关心Purrr与apply家族函数的性能差异。实际上,根据微基准测试的结果,使用map()函数可能比使用lapply()函数稍慢,但这个差距很小,对大多数R代码来说不会有实质性的影响。
最后,还有一些读者提到了一些关于具体用法的问题和建议,如使用transform()函数的替代方案、对类型的特殊性的讨论以及对R中函数包数量的疑问。这些问题并不直接与Purrr的优点和apply家族函数的比较相关,因此可能需要在其他上下文中进行深入讨论。