如何将匿名函数传递给dplyr的summarise函数。

5
我有一个简单的数据框,包含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))

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个回答

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

运算符被切换为<=,因为您想要在goal > actual时得到0,否则得到1
请注意,您可以使用匿名函数。问题出在if()语句上。例如,使用
sum((function(x, y) x <= y)(goal, actual)) 

它可以按你所要求的方式运行。


1
这个回答很好。我故意过度复杂化了我的尝试,因为我想看看一个更复杂/通用的匿名函数如何传递。 - Michael Szczepaniak
1
@MichaelSzczepaniak - 注意,你可以使用匿名函数。它是if()语句让你感到困惑。例如,sum((function(x, y) x <= y)(goal, actual))就可以运行。 - Rich Scriven
1
那正是我正在寻找的。感谢您解释了这个(两次;-)。 - Michael Szczepaniak

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

我并没有太多使用数据表的经验,但是我知道它们的好处,所以我相信在未来我一定会使用类似的东西。 - Michael Szczepaniak

0
发现自己需要再次做类似的事情(一年后),但是与原始问题中提供的简单函数相比,这次需要使用更复杂的函数。最初接受的答案利用了问题的特定功能,但更一般的方法在此处被提及。使用这种方法,我最终想要的答案是这样的:
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)))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接