如何在 R / tidyverse 中使用管道函数的进度条

Moo*_*han 6 r command-line-interface progress-bar magrittr tidyverse

我有一个主要函数,它对某些数据执行一些各种复杂(且长时间运行)的计算,它使用 tidyverse / magrittr 中的管道执行这些步骤。我想要一个进度条来报告处理的阶段,但是,我不知所措。我查看了cliprogressprogressr软件包,从中我只能开始cli工作(从某种意义上说。

\n

这是一个最小的例子:

\n
library(tidyverse)\nlibrary(cli)\n\nmain_fun <- function() {\n  cli_progress_step(msg = "Running main function")\n  tibble(a = 1:5) %>% \n    fun1() %>% \n    fun2() %>% \n    fun3()\n}\n\nfun1 <- function(data) {\n  cli_progress_step(msg = "Doing sub function 1")\n  Sys.sleep(2)\n\n  return(data)\n}\nfun2 <- function(data) {\n  cli_progress_step(msg = "Doing sub function 2")\n  Sys.sleep(1)\n\n  return(data)\n}\nfun3 <- function(data) {\n  cli_progress_step(msg = "Doing sub function 3")\n  Sys.sleep(3)\n\n  return(data)\n}\n\nmain_fun()\n#> \xe2\x84\xb9 Running main function\n#> \xe2\x84\xb9 Doing sub function 3\n#> \xe2\x84\xb9 Doing sub function 2\n#> \xe2\x84\xb9 Doing sub function 1\n#> \xe2\x9c\x94 Doing sub function 1 [2s]\n#> \n#> \xe2\x84\xb9 Doing sub function 2\xe2\x9c\x94 Doing sub function 2 [3s]\n#> \n#> \xe2\x84\xb9 Doing sub function 3\xe2\x9c\x94 Doing sub function 3 [6.1s]\n#> \n#> \xe2\x84\xb9 Running main function\xe2\x9c\x94 Running main function [6.1s]\n#> # A tibble: 10 \xc3\x97 1\n#>        a\n#>    <int>\n#>  1     1\n#>  2     2\n#>  3     3\n#>  4     4\n#>  5     5\n
Run Code Online (Sandbox Code Playgroud)\n

这将显示进度条,但按“相反”顺序,即 3 然后 2 然后 1。一旦全部完成,所有内容都会显示,这是我唯一满意的一点。

\n

GKi*_*GKi 5

这是因为,在管道中,函数不是从左到右计算的。适用于评估的常规 R 语义 - 延迟评估或按需调用。\n您与基础的调用管道的调用|>将如下所示:

\n
fun3(fun2(fun1(tibble(a = 1:5))))\n
Run Code Online (Sandbox Code Playgroud)\n

您可以强制评估,例如forceAndCall

\n
data.frame(a = 1:5) |> forceAndCall(n=1, Fun=fun1, data=_) |>\n  forceAndCall(n=1, Fun=fun2, data=_) |> forceAndCall(n=1, Fun=fun3, data=_)\n#\xe2\x9c\x94 Doing sub function 1 [2s]\n#\xe2\x9c\x94 Doing sub function 2 [1s]\n#\xe2\x9c\x94 Doing sub function 3 [3s]\n#...\n
Run Code Online (Sandbox Code Playgroud)\n

或者magrittr你可以使用急切的管道 %!>%从左到右评估表单(感谢 @Moohan 的评论!)。

\n
data.frame(a = 1:5) %!>% fun1() %!>%  fun2() %!>% fun3()\n#\xe2\x9c\x94 Doing sub function 1 [2s]\n#\xe2\x9c\x94 Doing sub function 2 [1s]\n#\xe2\x9c\x94 Doing sub function 3 [3s]\n#...\n
Run Code Online (Sandbox Code Playgroud)\n
\n

您可以force在函数的第一行中计算函数参数,这将得到您预期的结果。这适用于管道|>%>%

\n
library(magrittr)\nlibrary(cli)\n\nfun1 <- function(data) {\n  force(data) #or simple only data\n  cli_progress_step(msg = "Doing sub function 1")\n  Sys.sleep(2)\n  data\n}\nfun2 <- function(data) {\n  force(data)\n  cli_progress_step(msg = "Doing sub function 2")\n  Sys.sleep(1)\n  data\n}\nfun3 <- function(data) {\n  force(data)\n  cli_progress_step(msg = "Doing sub function 3")\n  Sys.sleep(3)\n  data\n}\n\ndata.frame(a = 1:5) %>% fun1() %>% fun2() %>% fun3()\n#\xe2\x9c\x94 Doing sub function 1 [2s]\n#\xe2\x9c\x94 Doing sub function 2 [1s]\n#\xe2\x9c\x94 Doing sub function 3 [3s]\n#\xe2\x9c\x94 Running main function [6.1s]\n#...\n\ndata.frame(a = 1:5) |> fun1() |> fun2() |> fun3()\n#\xe2\x9c\x94 Doing sub function 1 [2s]\n#\xe2\x9c\x94 Doing sub function 2 [1s]\n#\xe2\x9c\x94 Doing sub function 3 [3s]\n#\xe2\x9c\x94 Running main function [6.1s]\n#...\n
Run Code Online (Sandbox Code Playgroud)\n
\n

另一种方法可能是编写自定义管道函数。

\n
`:=` <- function(lhs, rhs) eval(substitute(rhs), list(. = lhs))\n\ndata.frame(a = 1:5) := fun1(.) := fun2(.) := fun3(.)\n#\xe2\x9c\x94 Doing sub function 1 [2s]\n#\xe2\x9c\x94 Doing sub function 2 [1s]\n#\xe2\x9c\x94 Doing sub function 3 [3s]\n#...\n
Run Code Online (Sandbox Code Playgroud)\n
\n

另一个示例显示何时进入和退出功能。

\n
library(magrittr)\nf1 <- \\(x) {message("IN 1"); x$b <- 1; message("OUT 1"); x}\nf2 <- \\(x) {message("IN 2"); x$c <- 2; message("OUT 2"); x}\n\ndata.frame(a=0) %>% f1 %>% f2\n#IN 2\n#IN 1\n#OUT 1\n#OUT 2\n#  a b c\n#1 0 1 2\n\ndata.frame(a=0) |> f1() |> f2()\n#IN 2\n#IN 1\n#OUT 1\n#OUT 2\n#  a b c\n#1 0 1 2\n\nf2(f1(data.frame(a=0)))\n#IN 2\n#IN 1\n#OUT 1\n#OUT 2\n#  a b c\n#1 0 1 2\n\ndata.frame(a=0) %!>% f1 %!>% f2\n#IN 1\n#OUT 1\n#IN 2\n#OUT 2\n#  a b c\n#1 0 1 2\n\ndata.frame(a=0) := f1(.) := f2(.)\n#IN 1\n#OUT 1\n#IN 2\n#OUT 2\n#  a b c\n#1 0 1 2\n\n. <- data.frame(a=0)\n. <- f1(.)\n#IN 1\n#OUT 1\n. <- f2(.)\n#IN 2\n#OUT 2\n.\n#  a b c\n#1 0 1 2\n
Run Code Online (Sandbox Code Playgroud)\n