使用管道 %>% 获取 system.time 或复制工作

Getting system.time or replicate work with piping %>%

我经常使用管道运算符(%>%,来自 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 命令相同的问题。

我能做的第二好是在 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最后。假设您知道 "start of line" 和 "end of line" 的键盘快捷键,将 system.time( 放在开头,将 ) 放在结尾可能同样简单。

我想出了一个使用管道和 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 方式进行了比较,它似乎并没有给出太大不同的结果。