【问题标题】:R Pipelining functionsR流水线功能
【发布时间】:2012-11-13 00:46:11
【问题描述】:

有没有办法在 R 中编写流水线函数,其中一个函数的结果立即传递到下一个函数?我来自 F#,非常欣赏这种能力,但还没有找到如何在 R 中做到这一点。它应该很简单,但我找不到方法。在 F# 中,它看起来像这样:

let complexFunction x =
     x |> square 
     |> add 5 
     |> toString

在这种情况下,输入将被平方,然后添加 5,然后转换为字符串。我希望能够在 R 中做类似的事情,但不知道如何做。我已经搜索过如何做这样的事情,但没有遇到任何事情。我想要这个来导入数据,因为我通常必须导入它然后过滤。现在,我分多个步骤执行此操作,并且非常希望能够像在 F# 中使用管道那样做一些事情。

【问题讨论】:

标签: r pipeline


【解决方案1】:

这是一种使用Reduce 的函数式编程方法。其实就是来自?Reduce的例子

square <- function(x) x^2
add_5 <- function(x)  x+5
x <- 1:5
## Iterative function application:
Funcall <- function(f, ...) f(...)

Reduce(Funcall, list(as.character, add_5, square,x), right = TRUE)
## [1] "6"  "9"  "14" "21" "30"

或者更简单地使用functional 包和Compose

这很好,因为它会为你创建函数

library(functional)
do_stuff <-   Compose(square,add_5,as.character )
do_stuff(1:5)
##  [1] "6"  "9"  "14" "21" "30"

我注意到我不会习惯性地考虑这两种方法中的任何一种R ish(如果这甚至是一个短语)

【讨论】:

    【解决方案2】:

    我们可以使用函数包中的Compose 来创建我们自己的二元运算符,它可以执行类似于您想要的操作

    # Define our helper functions
    square <- function(x){x^2}
    add5 <- function(x){x + 5}
    
    # functional contains Compose
    library(functional)
    
    # Define our binary operator
    "%|>%" <- Compose
    
    # Create our complexFunction by 'piping' our functions
    complexFunction <- square %|>% add5 %|>% as.character
    complexFunction(1:5)
    #[1] "6"  "9"  "14" "21" "30"
    
    
    # previously had this until flodel pointed out
    # that the above was sufficient
    #"%|>%" <- function(fun1, fun2){ Compose(fun1, fun2) }
    

    我想我们在技术上不需要功能包就可以做到这一点 - 但使用 Compose 来完成这项任务感觉非常合适。

    "%|>%" <- function(fun1, fun2){
        function(x){fun2(fun1(x))}
    }
    complexFunction <- square %|>% add5 %|>% as.character
    complexFunction(1:5)
    #[1] "6"  "9"  "14" "21" "30"
    

    【讨论】:

    • 我们可以去掉%|&gt;% 中的两个%% 符号吗?它们看起来又丑又笨重。或者 R 中的 %% 有什么特殊性吗?我在文档中找不到解释。
    • 它们是必需的。我不知道如何在文档中提到如何定义这些东西,但它就在某个地方。用户定义的二进制中缀运算符将始终具有 %something% 形式
    【解决方案3】:

    我认为您可能只想编写一个函数来执行您想要的步骤。

    complexFunction <- function(x) {
        as.character(x^2 + 5)
    }
    

    那就直接拨打complexFunction(x)吧。


    编辑以显示 R 在内部做什么 (@mnel) -- R 解析和评估 as.character(x^2 + 5) 的方式可以满足您的需求

    您可以使用codetools 调查R 以查看值是如何传递给彼此的

    flattenAssignment(quote(as.character(x^2+5)))
    [[1]]
    [[1]][[1]]
    x
    
    [[1]][[2]]
    `*tmp*`^2
    
    [[1]][[3]]
    `*tmp*` + 5
    
    
    [[2]]
    [[2]][[1]]
    `as.character<-`(`*tmp*`, value = `*tmpv*`)
    
    [[2]][[2]]
    `+<-`(`*tmp*`, 5, value = `*tmpv*`)
    
    [[2]][[3]]
    `^<-`(x, 2, value = `*tmpv*`)
    

    或者你可以获取 Lisp 样式表示,看看它是如何解析的(以及传递的结果)

    showTree(quote(as.character(x^2+5)))
    (as.character (+ (^ x 2) 5))
    

    【讨论】:

      【解决方案4】:

      自从提出这个问题以来,magrittr 管道在 R 中变得非常流行。所以你的例子是:

      library (magrittr)
      
      fx <- function (x) {
           x %>%
           `^` (2) %>%
           `+` (5)  %>%
           as.character ()
           }
      

      请注意,反引号表示法是因为我实际上是在使用 R 的内置函数,我需要特别引用它们才能以这种方式使用它们。更正常命名的函数(如 exp 或如果我创建了一个辅助函数 add)不需要反引号,并且看起来更像您的示例。

      还要注意%&gt;% 将传入的值作为第一个参数自动传递给下一个函数,尽管您可以更改它。另请注意,R 函数返回计算的最后一个值,因此我不需要 return 或分配计算来返回它。

      这很像其他答案定义的很好的特殊运算符,但它使用了现在在 R 中广泛使用的特定函数。

      【讨论】:

      • magrittr 还为常用函数提供别名,允许您使用管道传递add,而不是在反引号中引用+。有关完整列表,请参阅 ?magrittr::extract
      【解决方案5】:

      这适用于在 F# 中非常相似的 R:

      "%|>%" <- function(x, fun){
          if(is.function(x)) {
            function(...) fun(x(...))
          } else {
            fun(x)
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2011-01-26
        • 1970-01-01
        • 2012-09-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多