【问题标题】:How to pass an anonymous function to dplyr summarise如何将匿名函数传递给 dplyr 总结
【发布时间】:2018-08-25 09:46:34
【问题描述】:

我有一个包含 3 列的简单数据框:namegoalactual。 因为这是对更大数据框的简化,所以我想使用 dplyr 来计算每个人实现目标的次数。

df <- data.frame(name = c(rep('Fred', 3), rep('Sally', 4)),
                 goal = c(4,6,5,7,3,8,5), actual=c(4,5,5,3,3,6,4))

结果应该是这样的:

我应该能够传递类似于下面显示的匿名函数,但语法不太正确:

library(dplyr)
g <- group_by(df, name)
summ <- summarise(g, met_goal = sum((function(x,y) {
                                       if(x>y){return(0)}
                                       else{return(1)}
                                     })(goal, actual)
                                    )
                  )

当我运行上面的代码时,我看到了其中 3 个错误:

警告信息: 1:如果(x == y){: 条件的长度 > 1,并且只使用第一个元素

【问题讨论】:

    标签: r dplyr anonymous-function


    【解决方案1】:

    我们在goalactual 中有相等长度的向量,所以这里适合使用关系运算符。然而,当我们在一个简单的if() 语句中使用它们时,我们可能会得到意想不到的结果,因为if() 需要长度为1 的向量。由于我们有相等长度的向量并且我们需要二进制结果,因此采用逻辑向量的总和是最好的方法,如下所示。

    group_by(df, name) %>%
        summarise(met_goal = sum(goal <= actual))
    # A tibble: 2 x 2
        name met_goal
      <fctr>    <int>
    1   Fred        2
    2  Sally        1
    

    运算符切换到&lt;=,因为您希望0 用于goal &gt; actual,否则1

    请注意,您可以使用匿名函数。是 if() 声明让你失望了。例如,使用

    sum((function(x, y) x <= y)(goal, actual)) 
    

    会按照您要求的方式工作。

    【讨论】:

    • 这很好地回答了这个问题。我确实故意使我的尝试过于复杂,因为我想看看如何传递一个更复杂/通用的匿名函数。
    • @MichaelSzczepaniak - 请注意,您可以使用匿名函数。是 if() 声明让你失望了。例如,sum((function(x, y) x &lt;= y)(goal, actual)) 可以工作。
    • 这正是我想要的。感谢您解释这一点(两次 ;-)。
    【解决方案2】:

    使用data.table的解决方案:

    您要求dplyr 解决方案,但由于实际数据要大得多,您可以使用data.tablefoo 是你要应用的函数。

    foo <- function(x, y) {
        res <- 0
        if (x <= y) {
            res <- 1
        }
        return(res)
    }
    
    library(data.table)
    setDT(df)
    setkey(df, name)[, foo(goal, actual), .(name, 1:nrow(df))][, sum(V1), name]
    

    如果你喜欢管道,那么你可以使用这个:

    library(magrittr)
    setDT(df) %>%
        setkey(name) %>%
        .[, foo(goal, actual), .(name, 1:nrow(.))] %>%
        .[, .(met_goal = sum(V1)), name]
    
        name met_goal
    1:  Fred        2
    2: Sally        1
    

    【讨论】:

    • 我对数据表的工作不多,但我知道它们的好处,所以我相信我将来会使用这样的东西。
    【解决方案3】:

    发现自己需要再次(一年后)做类似的事情,但功能比原始问题中提供的简单功能更复杂。最初接受的答案利用了问题的特定特征,但更通用的方法涉及here。使用这种方法,我最终得到的答案是这样的:

    library(dplyr)
    
    df <- data.frame(name = c(rep('Fred', 3), rep('Sally', 4)),
                     goal = c(4,6,5,7,3,8,5), actual=c(4,5,5,3,3,6,4))
    
    my_func = function(act, goa) {
      if(act < goa) {
        return(0)
      } else {
        return(1)
      }
    }
    
    g <- group_by(df, name)
    summ = df %>% group_by(name) %>%
      summarise(met_goal = sum(mapply(my_func, .data$actual, .data$goal)))
    
    > summ
    # A tibble: 2 x 2
      name  met_goal
      <fct>    <dbl>
    1 Fred         2
    2 Sally        1
    

    最初的问题是指使用匿名函数。本着这种精神,最后一部分看起来像这样:

    g <- group_by(df, name)
    summ = df %>% group_by(name) %>%
      summarise(met_goal = sum(mapply(function(act, go) {
                                        if(act < go) {
                                          return(0)
                                        } else {
                                          return(1)
                                        }
                                      }, .data$actual, .data$goal)))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-12
      • 1970-01-01
      • 2020-07-04
      • 2017-06-06
      • 2021-01-19
      • 1970-01-01
      • 2012-10-28
      • 1970-01-01
      相关资源
      最近更新 更多