R函数中的Magrittr管道

从(1)速度和(2)有效调试能力的角度来看,是否有利于在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)
}

比上一个函数更可读,R会在出现错误时为您提供更详细的信息.此外,它遵循传统的评估规则.同样,它给出了与其他两个函数相同的结果……

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

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

结论

如果这是您编写代码的最舒适方式,请随意在函数中使用管道.如果您开始遇到问题或者您需要尽可能快地使用代码,那么请切换到不同的范例.

相关文章

什么是设计模式一套被反复使用、多数人知晓的、经过分类编目...
单一职责原则定义(Single Responsibility Principle,SRP)...
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强...
适配器模式将一个类的接口转换成客户期望的另一个接口,使得...
策略模式定义了一系列算法族,并封装在类中,它们之间可以互...
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,...