R功能中的Magrittr管

use*_*086 4 pipeline r magrittr

从(1)速度和(2)有效调试的能力的角度来看,在某些情况下在R函数内部使用magrittr管道是否不利?

And*_*ēza 6

在功能内部使用管道有优缺点。最大的优点是,在阅读代码时,更容易看到函数中正在发生的事情。最大的缺点是错误消息变得更难以解释,并且管道破坏了R的某些评估规则。

这是一个例子。假设我们要对mtcars数据集进行无意义的转换。这是我们可以使用管道进行的操作...

library(tidyverse)
tidy_function <- function() {
  mtcars %>%
    group_by(cyl) %>%
    summarise(disp = sum(disp)) %>%
    mutate(disp = (disp ^ 4) / 10000000000)
}
Run Code Online (Sandbox Code Playgroud)

您可以清楚地看到每个阶段发生的事情,即使它没有做任何有用的事情。现在,让我们来看一下使用达格伍德三明治法的时间代码...

base_function <- function() {
  mutate(summarise(group_by(mtcars, cyl), disp = sum(disp)), disp = (disp^5) / 10000000000)
}
Run Code Online (Sandbox Code Playgroud)

即使它给我们相同的结果,也很难阅读。

all.equal(tidy_function(), base_function())
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)

避免使用管道或Dagwood三明治的最常见方法是将每个步骤的结果保存到中间变量中...

intermediate_function <- function() {
  x <- mtcars
  x <- group_by(x, cyl)
  x <- summarise(x, disp = sum(disp))
  mutate(x, disp = (disp^5) / 10000000000)
}
Run Code Online (Sandbox Code Playgroud)

比最后一个函数更具可读性,R会在发生错误时为您提供更详细的信息。此外,它还遵循传统的评估规则。同样,它提供与其他两个功能相同的结果...

all.equal(tidy_function(), intermediate_function())
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)

您专门询问了速度,因此让我们通过将它们运行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
Run Code Online (Sandbox Code Playgroud)

即使在这个简单的示例中,管道也比其他两个选项慢一点。

结论

如果这是编写代码最方便的方式,请随时在函数中使用管道。如果您开始遇到问题,或者需要您的代码以最快的速度运行,请切换到其他范例。

  • 您的Dagwood三明治只需要一些换行符和缩进即可,并且可读性强。 (2认同)