如何将匿名函数传递给dplyr汇总

时间:2017-09-22 22:09:38

标签: r dplyr anonymous-function

我有一个包含3列的简单数据框:名称目标实际。 因为这是对更大的数据帧的简化,我想使用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))

enter image description here

结果应如下所示:

enter image description here

我应该可以传递一个类似于下面显示的匿名函数,但是语法不是很正确:

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:在if(x == y){:       条件的长度> 1,只使用第一个元素

3 个答案:

答案 0 :(得分:4)

我们在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

运营商已切换为<=,因为您希望0goal > actual,否则为1

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

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

会以您要求的方式工作。

答案 1 :(得分: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

答案 2 :(得分:0)

发现自己(一年后)需要再次执行类似的操作,但功能要比原始问题中提供的简单功能复杂。最初接受的答案利用了问题的特定功能,但是更普遍的方法是在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)))