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

vwkv1x7d  于 2023-05-20  发布在  其他
关注(0)|答案(2)|浏览(142)

我有一个main函数,它对一些数据执行一些不同的复杂(和长时间运行)计算,它使用tidyverse / magrittr的管道执行这些步骤。我想一个进度条报告的处理阶段,因为它通过它的工作,但是,我在亏损。我已经看过cliprogressprogressr包,我只能让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。一旦全部完成,所有的都显示出来,这是我唯一感到高兴的。

jogvjijk

jogvjijk1#

这是因为,在管道中,函数不是从左到右计算的。用于求值的常规R语义适用-惰性求值或按需调用。使用 base 管道|>的调用将如下所示:

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]
#...

或者使用magrittr,您可以使用 eager pipe%!>%从左到右计算表单(感谢@Moohan的评论!).

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
daolsyd0

daolsyd02#

这可以使用{magrittr}中的' eager pipe '(%!>%)来实现

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

相关问题