逐步通过具有中间结果的管道

Posted

技术标签:

【中文标题】逐步通过具有中间结果的管道【英文标题】:Stepping through a pipeline with intermediate results 【发布时间】:2015-07-19 02:55:11 【问题描述】:

有没有一种方法可以在每个步骤中输出管道的结果而无需手动执行? (例如,不选择并仅运行选定的块)

我经常发现自己逐行运行管道以记住它在做什么或何时进行一些分析。

例如:

library(dplyr)

mtcars %>% 
  group_by(cyl) %>% 
  sample_frac(0.1) %>% 
  summarise(res = mean(mpg))
# Source: local data frame [3 x 2]
# 
# cyl  res
# 1   4 33.9
# 2   6 18.1
# 3   8 18.7

我要选择并运行:

mtcars %>% group_by(cyl)

然后……

mtcars %>% group_by(cyl) %>% sample_frac(0.1)

等等……

但是在RStudio 中选择和CMD/CTRL+ENTER 需要一种更有效的方法。

这可以在代码中完成吗?

是否有一个函数可以接受管道并逐行运行/消化它,在控制台中的每个步骤中显示输出,然后您可以通过按包指南中的demos(...)examples(...) 中的回车键继续强>

【问题讨论】:

查看 R 的 debug() 函数。它接近你想要的。您可以将它与print() 语句一起使用。 Cross Validated 上的这篇文章对此进行了更多讨论。 您可以简单地使用%>% print() %>% - 请参阅此答案:***.com/a/54075410/5535152 【参考方案1】:

您可以使用 tee 运算符 (%T>%) 和 print() 选择要打印的结果。 tee-operator 专门用于打印等副作用。

# i.e.
mtcars %>%
  group_by(cyl) %T>% print() %>%
  sample_frac(0.1) %T>% print() %>%
  summarise(res = mean(mpg))

【讨论】:

当输出是数据框时,我发现使用%T>% View() %>% 查看中间结果很有用【参考方案2】:

使用 magrittr 函数链很容易。例如定义一个函数my_chain

foo <- function(x) x + 1
bar <- function(x) x + 1
baz <- function(x) x + 1
my_chain <- . %>% foo %>% bar %>% baz

并得到一个链的最终结果为:

     > my_chain(0)
    [1] 3

您可以使用functions(my_chain) 获取函数列表 并像这样定义一个“步进器”函数:

stepper <- function(fun_chain, x, FUN = print) 
  f_list <- functions(fun_chain)
  for(i in seq_along(f_list)) 
    x <- f_list[[i]](x)
    FUN(x)
  
  invisible(x)

并使用插入的print 函数运行链:

stepper(my_chain, 0, print)

# [1] 1
# [1] 2
# [1] 3

或者等待用户输入:

stepper(my_chain, 0, function(x) print(x); readline())

【讨论】:

【参考方案3】:

添加打印:

mtcars %>% 
  group_by(cyl) %>% 
  print %>% 
  sample_frac(0.1) %>% 
  print %>% 
  summarise(res = mean(mpg))

【讨论】:

我知道 print 返回了它的参数,所以这是可行的,但它并不比手动选择和运行块更短/更快/更方便。 @andrewwong 告诉我们更多,为什么要逐行运行,更重要的是为什么要逐个查看打印输出? 更新问题。我想要一个控制台中的交互式步进器或一个自动魔术降价文档,其中所有中间体都生成。谢谢你的想法!【参考方案4】:

恕我直言,magrittr 主要用于交互方式,即当我探索数据或构建新公式/模型时。

在这种情况下,将中间结果存储在不同的变量中非常耗时且分散注意力,而管道让我专注于数据,而不是打字:

x %>% foo
## reason on results and 
x %>% foo %>% bar
## reason on results and 
x %>% foo %>% bar %>% baz
## etc.

这里的问题是我事先不知道最终的管道会是什么,就像在@bergant 中一样。

打字,如@zx8754,

x %>% print %>% foo %>% print %>% bar %>% print %>% baz

增加了很多开销,对我来说,破坏了 magrittr 的全部目的。

本质上 magrittr 缺少 printspipes 结果的简单运算符。 好消息是制作它似乎很容易:

`%P>%`=function(lhs, rhs) print(lhs); lhs %>% rhs 

现在您可以打印管道了:

1:4 %P>% sqrt %P>% sum 
## [1] 1 2 3 4
## [1] 1.000000 1.414214 1.732051 2.000000
## [1] 6.146264

我发现,如果为%P&gt;%%&gt;% 定义/使用键绑定,那么原型设计工作流程将非常简化(请参阅Emacs ESS 或RStudio)。

【讨论】:

【参考方案5】:

我编写了 pipes 包,它可以做一些可能有帮助的事情:

使用%P&gt;%print 输出。 使用%ae&gt;% 在输入和输出上使用all.equal。 使用%V&gt;% 在输出中使用View,它将为每个相关步骤打开一个查看器。

如果您想查看一些汇总信息,您可以尝试使用%summary&gt;%%glimpse&gt;%%skim&gt;%,它们将使用summarytibble::glimpseskimr::skim,或者您可以定义自己的管道来显示特定的更改,使用new_pipe

# devtools::install_github("moodymudskipper/pipes")
library(dplyr)
library(pipes)
res <- mtcars %P>% 
  group_by(cyl) %P>% 
  sample_frac(0.1) %P>% 
  summarise(res = mean(mpg))
#> group_by(., cyl)
#> # A tibble: 32 x 11
#> # Groups:   cyl [3]
#>      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>  * <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#>  1  21       6  160    110  3.9   2.62  16.5     0     1     4     4
#>  2  21       6  160    110  3.9   2.88  17.0     0     1     4     4
#>  3  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
#>  4  21.4     6  258    110  3.08  3.22  19.4     1     0     3     1
#>  5  18.7     8  360    175  3.15  3.44  17.0     0     0     3     2
#>  6  18.1     6  225    105  2.76  3.46  20.2     1     0     3     1
#>  7  14.3     8  360    245  3.21  3.57  15.8     0     0     3     4
#>  8  24.4     4  147.    62  3.69  3.19  20       1     0     4     2
#>  9  22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
#> 10  19.2     6  168.   123  3.92  3.44  18.3     1     0     4     4
#> # ... with 22 more rows
#> sample_frac(., 0.1)
#> # A tibble: 3 x 11
#> # Groups:   cyl [3]
#>     mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1  26       4  120.    91  4.43  2.14  16.7     0     1     5     2
#> 2  17.8     6  168.   123  3.92  3.44  18.9     1     0     4     4
#> 3  18.7     8  360    175  3.15  3.44  17.0     0     0     3     2
#> summarise(., res = mean(mpg))
#> # A tibble: 3 x 2
#>     cyl   res
#>   <dbl> <dbl>
#> 1     4  26  
#> 2     6  17.8
#> 3     8  18.7
res <- mtcars %ae>% 
  group_by(cyl) %ae>% 
  sample_frac(0.1) %ae>% 
  summarise(res = mean(mpg))
#> group_by(., cyl)
#> [1] "Attributes: < Names: 1 string mismatch >"                                              
#> [2] "Attributes: < Length mismatch: comparison on first 2 components >"                     
#> [3] "Attributes: < Component \"class\": Lengths (1, 4) differ (string compare on first 1) >"
#> [4] "Attributes: < Component \"class\": 1 string mismatch >"                                
#> [5] "Attributes: < Component 2: Modes: character, list >"                                   
#> [6] "Attributes: < Component 2: Lengths: 32, 2 >"                                           
#> [7] "Attributes: < Component 2: names for current but not for target >"                     
#> [8] "Attributes: < Component 2: Attributes: < target is NULL, current is list > >"          
#> [9] "Attributes: < Component 2: target is character, current is tbl_df >"
#> sample_frac(., 0.1)
#> [1] "Different number of rows"
#> summarise(., res = mean(mpg))
#> [1] "Cols in y but not x: `res`. "                                                                
#> [2] "Cols in x but not y: `qsec`, `wt`, `drat`, `hp`, `disp`, `mpg`, `carb`, `gear`, `am`, `vs`. "
res <- mtcars %V>% 
  group_by(cyl) %V>% 
  sample_frac(0.1) %V>% 
  summarise(res = mean(mpg))
# you'll have to test this one by yourself

【讨论】:

以上是关于逐步通过具有中间结果的管道的主要内容,如果未能解决你的问题,请参考以下文章

命名管道如何识别客户端

php 算法?

Linux进程间通信的方式都有哪些

如何通过命名管道传输文件描述符

.NET6之MiniAPI:中间件

Mongodb 高级