【问题标题】:Getting system.time or replicate work with piping %>%使用管道获取 system.time 或复制工作 %>%
【发布时间】:2017-01-19 08:17:30
【问题描述】:

我经常使用管道运算符(%>%,来自magrittr,或dplyr 库)。直到有一天,我尝试使用右侧的system.time 命令。

system.time(mean(rnorm(1E7))) # ok
#### user     system      elapsed 
#### 3.52        0.05        3.58 
rnorm(1E7) %>% mean %>% system.time # ?
#### user     system      elapsed 
#### 0        0        0

所以我去阅读文档并尝试了这个(它说你可以通过将它括在括号中来强制评估 RHS,但它给出了相同的行为:

rnorm(1E7) %>% mean %>% (function(x) system.time(x))
#### user     system      elapsed 
#### 0        0        0

我的问题如下:

1. 为什么将命令system.time 放置在管道末端时不能按预期工作?

2. 有没有办法测量由管道组成的一行代码的计算时间,而不必将整行放在括号内(这会抵消管道的实际好处.. .) 还是使用 proc.time?

注意replicate 命令也有同样的问题。

【问题讨论】:

  • 不是 [r] 大师,但在管道 (%>%) 文档的末尾有一些技术说明提到管道与函数调用并不完全相同。确切的措辞是 %>% 帮助上的技术说明可能有用: magrittr 管道操作员使用非标准评估。他们捕获他们的输入并检查它们以找出如何进行。首先从所有单独的右侧表达式生成一个函数,然后通过将此函数应用于左侧获得结果。。这可能对您有帮助。
  • @Bhargav Rao 谢谢,但对我来说这听起来有点像中文 ;-)
  • %>% 传递 lhs 表达式的 求值 结果,而 not 表达式本身。所以在第二种情况下,就像你给了a<-rnorm(1E7);d<-mean(a);system.time(d)
  • @nicola 好吧,我现在明白了:它总是会给出 0,但它工作正常......所以你说除了将 system.time 放在整行之前别无他法括号?
  • @agenis 猜猜看。

标签: r dplyr piping magrittr


【解决方案1】:

我能做的第二件事是在system.time 上创建一个包装器,它接受一个未计算的表达式并对其进行计算,然后你必须将定时表达式包装在大括号中,并在管道时引用它,这样它就不会评估直到我的包装函数得到它的爪子:

> psystime = function(e){system.time(eval(e))}
> quote({rnorm(1e7) %>%  mean}) %>% psystime
   user  system elapsed 
  0.764   0.004   0.767 
> 

我说第二好,因为最好的答案就是根本不这样做。有时管道是问题,而不是解决方案。

另一种可能性是将管道表达式包装在引号中并将其提供给system.time 包装器,该包装器将其参数的评估版本作为文本运行:

> esystime = function(e){system.time(eval(parse(text=e)))}
> "rnorm(1e7) %>%  mean" %>% esystime
   user  system elapsed 
  1.075   0.033   1.137 

我猜这个用例实际上是当你有一个很长的管道并且想要快速查看运行需要多长时间,所以你很自然地想要在最后敲击%>% system.time。假设您知道“行首”和“行尾”的键盘快捷键,将system.time( 放在开头,) 放在结尾,这可能同样简单。

【讨论】:

  • 对于所有除了 system.time,也总是可以选择使用管道。即system.time(rnorm(1E7) %>% mean)
  • fortunes::fortune(106)
  • 感谢@Spacedman 的广泛投入。我完全同意你的最后一段。
【解决方案2】:

我想出了一个使用管道和 proc.time 的解决方法。它不是绝对完美的,但它可能会有所帮助。它还使用 tee 运算符。

首先定义一个Timer函数,从全局环境中记录的start.time变量开始计时。

Timer = function(x){
  if (exists("start.time")){
    print(proc.time() - start.time)
    rm('start.time', envir=.GlobalEnv)
  } else {
    assign("start.time", proc.time(), envir=globalenv())
  }
}

然后在管道的第二个位置使用它,然后再次使用它:

diamonds %T>% Timer %>% group_by(clarity) %>% summarise(sd(price)) %>% Timer
#### user  system elapsed 
#### 0.02    0.00    0.02

除了第一个元素(但通常只是数据,所以..)之外,您可以获得所有内容的计算时间。我与 system.time 方式进行了比较,它似乎并没有给出太多不同的结果。

【讨论】: