在使用R函数时,从速度和有效调试的角度考虑,是否存在不使用magrittr管道的情况?
在使用R函数时,从速度和有效调试的角度考虑,是否存在不使用magrittr管道的情况?
在函数中使用管道有优点和缺点。最大的优点是,在阅读代码时,更容易看出函数内部发生了什么。最大的缺点是,错误信息变得更难以解释,而且管道打破了一些 R 评估规则。
以下是一个示例。假设我们想对 mtcars
数据集进行无意义的转换。以下是如何使用管道完成这个任务...
library(tidyverse)
tidy_function <- function() {
mtcars %>%
group_by(cyl) %>%
summarise(disp = sum(disp)) %>%
mutate(disp = (disp ^ 4) / 10000000000)
}
即使它没有执行任何有用的操作,您仍然可以清楚地看到每个阶段发生了什么。现在让我们使用 Dagwood Sandwich 方法查看时间代码...
base_function <- function() {
mutate(summarise(group_by(mtcars, cyl), disp = sum(disp)), disp = (disp^5) / 10000000000)
}
虽然得出相同的结果,但更难阅读...
all.equal(tidy_function(), base_function())
# [1] TRUE
避免使用管道或Dagwood三明治的最常见方法是将每个步骤的结果保存到一个中间变量中...
intermediate_function <- function() {
x <- mtcars
x <- group_by(x, cyl)
x <- summarise(x, disp = sum(disp))
mutate(x, disp = (disp^5) / 10000000000)
}
all.equal(tidy_function(), intermediate_function())
# [1] TRUE
您特别询问了速度问题,因此让我们通过运行这三个函数各1000次来进行比较...
library(microbenchmark)
timing <-
microbenchmark(tidy_function(),
intermediate_function(),
base_function(),
times = 1000L)
timing
#Unit: milliseconds
#expr min lq mean median uq max neval cld
#tidy_function() 3.809009 4.403243 5.531429 4.800918 5.860111 23.37589 1000 a
#intermediate_function() 3.560666 4.106216 5.154006 4.519938 5.538834 21.43292 1000 a
#base_function() 3.610992 4.136850 5.519869 4.583573 5.696737 203.66175 1000 a
即使在这个微不足道的例子中,管道比其他两个选项慢一点。
如果使用管道是您编写代码最舒适的方式,请随意在您的函数中使用它。 如果您开始遇到问题或需要尽可能快地运行代码,则切换到不同的范例。