提问人:Moohan 提问时间:5/4/2023 更新时间:5/17/2023 访问量:255
如何在 R / tidyverse 中为管道函数使用进度条
How can I use a progress bar for piped functions in R / tidyverse
问:
我有一个 main 函数,它可以对某些数据执行一些各种复杂(且长时间运行)的计算,它使用 tidyverse / magrittr 的管道执行这些步骤。我想要一个进度条来报告处理阶段,但是,我不知所措。我看过 和 包,从中我只能开始工作(在某种程度上。cli
progress
progressr
cli
下面是一个最小的示例:
library(tidyverse)
library(cli)
main_fun <- function() {
cli_progress_step(msg = "Running main function")
tibble(a = 1:5) %>%
fun1() %>%
fun2() %>%
fun3()
}
fun1 <- function(data) {
cli_progress_step(msg = "Doing sub function 1")
Sys.sleep(2)
return(data)
}
fun2 <- function(data) {
cli_progress_step(msg = "Doing sub function 2")
Sys.sleep(1)
return(data)
}
fun3 <- function(data) {
cli_progress_step(msg = "Doing sub function 3")
Sys.sleep(3)
return(data)
}
main_fun()
#> ℹ Running main function
#> ℹ Doing sub function 3
#> ℹ Doing sub function 2
#> ℹ Doing sub function 1
#> ✔ Doing sub function 1 [2s]
#>
#> ℹ Doing sub function 2✔ Doing sub function 2 [3s]
#>
#> ℹ Doing sub function 3✔ Doing sub function 3 [6.1s]
#>
#> ℹ Running main function✔ Running main function [6.1s]
#> # A tibble: 10 × 1
#> a
#> <int>
#> 1 1
#> 2 2
#> 3 3
#> 4 4
#> 5 5
这将显示进度条,但以“相反”的顺序显示,即 3、2、1。一旦全部完成,所有内容都会显示出来,这是我唯一满意的地方。
答:
5赞
GKi
5/15/2023
#1
这是因为,在管道中,函数不是从左到右计算的。用于评估的常规 R 语义应用 - 延迟评估或按需调用。
您对基础管道的调用将如下所示:|>
fun3(fun2(fun1(tibble(a = 1:5))))
您可以强制进行评估,例如使用 .forceAndCall
data.frame(a = 1:5) |> forceAndCall(n=1, Fun=fun1, data=_) |>
forceAndCall(n=1, Fun=fun2, data=_) |> forceAndCall(n=1, Fun=fun3, data=_)
#✔ Doing sub function 1 [2s]
#✔ Doing sub function 2 [1s]
#✔ Doing sub function 3 [3s]
#...
或者,您可以使用急切的管道从左到右评估表单(感谢@Moohan的评论!magrittr
%!>%
data.frame(a = 1:5) %!>% fun1() %!>% fun2() %!>% fun3()
#✔ Doing sub function 1 [2s]
#✔ Doing sub function 2 [1s]
#✔ Doing sub function 3 [3s]
#...
您可以在函数的第一行中对函数参数进行计算,这将如您预期的那样产生结果。这适用于管道和 .force
|>
%>%
library(magrittr)
library(cli)
fun1 <- function(data) {
force(data) #or simple only data
cli_progress_step(msg = "Doing sub function 1")
Sys.sleep(2)
data
}
fun2 <- function(data) {
force(data)
cli_progress_step(msg = "Doing sub function 2")
Sys.sleep(1)
data
}
fun3 <- function(data) {
force(data)
cli_progress_step(msg = "Doing sub function 3")
Sys.sleep(3)
data
}
data.frame(a = 1:5) %>% fun1() %>% fun2() %>% fun3()
#✔ Doing sub function 1 [2s]
#✔ Doing sub function 2 [1s]
#✔ Doing sub function 3 [3s]
#✔ Running main function [6.1s]
#...
data.frame(a = 1:5) |> fun1() |> fun2() |> fun3()
#✔ Doing sub function 1 [2s]
#✔ Doing sub function 2 [1s]
#✔ Doing sub function 3 [3s]
#✔ Running main function [6.1s]
#...
另一种方法是编写自定义管道函数。
`:=` <- function(lhs, rhs) eval(substitute(rhs), list(. = lhs))
data.frame(a = 1:5) := fun1(.) := fun2(.) := fun3(.)
#✔ Doing sub function 1 [2s]
#✔ Doing sub function 2 [1s]
#✔ Doing sub function 3 [3s]
#...
另一个示例显示了进入和退出函数时的情况。
library(magrittr)
f1 <- \(x) {message("IN 1"); x$b <- 1; message("OUT 1"); x}
f2 <- \(x) {message("IN 2"); x$c <- 2; message("OUT 2"); x}
data.frame(a=0) %>% f1 %>% f2
#IN 2
#IN 1
#OUT 1
#OUT 2
# a b c
#1 0 1 2
data.frame(a=0) |> f1() |> f2()
#IN 2
#IN 1
#OUT 1
#OUT 2
# a b c
#1 0 1 2
f2(f1(data.frame(a=0)))
#IN 2
#IN 1
#OUT 1
#OUT 2
# a b c
#1 0 1 2
data.frame(a=0) %!>% f1 %!>% f2
#IN 1
#OUT 1
#IN 2
#OUT 2
# a b c
#1 0 1 2
data.frame(a=0) := f1(.) := f2(.)
#IN 1
#OUT 1
#IN 2
#OUT 2
# a b c
#1 0 1 2
. <- data.frame(a=0)
. <- f1(.)
#IN 1
#OUT 1
. <- f2(.)
#IN 2
#OUT 2
.
# a b c
#1 0 1 2
评论
0赞
Moohan
5/15/2023
我想我没有考虑过你布置的评估顺序,但这是有道理的......也就是说,是否有一种技巧/解决方法可以使消息按我们的预期显示,尽管 R 在“相反”方向上评估?
1赞
GKi
5/15/2023
也许你的解决方案是编写一个自己的管道函数?
0赞
Moohan
5/15/2023
谢谢!我想知道使用可能有什么缺点,我遇到了 magrittr 的“急切管道”。这基本上是您的解决方案!magrittr.tidyverse.org/reference/pipe-eager.htmlforce()
4赞
Moohan
5/15/2023
#2
这可以使用 'eager pipe' () 来实现%!>%
{magrittr}
library(tidyverse)
library(cli)
library(magrittr)
main_fun <- function() {
cli_progress_step(msg = "Running main function")
tibble(a = 1:5) %!>%
fun1() %!>%
fun2() %!>%
fun3()
}
main_fun()
#> ℹ Running main function
#> ℹ Doing sub function 1
#> ✔ Doing sub function 1 [2s]
#>
#> ℹ Running main functionℹ Doing sub function 2
#> ✔ Doing sub function 2 [1s]
#>
#> ℹ Running main functionℹ Doing sub function 3
#> ✔ Doing sub function 3 [3s]
#>
#> ℹ Running main function✔ Running main function [6.1s]
#> # A tibble: 10 × 1
#> a
#> <int>
#> 1 1
#> 2 2
#> 3 3
#> 4 4
#> 5 5
评论
%>%
<-
|>