问题下面的评论讨论了在很多情况下,您可以在dplyr或相关包中找到避免使用do
的替代方法,并且问题中的示例都属于这种情况。但是,为了直接回答问题而不是通过替代方案:
使用和不使用do
的区别
在数据框的上下文中,使用do
和不使用do
的关键区别在于:
没有自动插入点号在do
内部的代码将不会自动将点号插入第一个参数中。例如,在问题中,do(summarise(Mean_2014 = mean(Y2014)))
的代码将变成do(summarise(., Mean_2014 = mean(Y2014)))
,需要加上点号,因为点号不会自动插入。这是因为do
是%>%
的右侧函数,而不是summarize
。虽然这很重要,以便我们在需要时插入点号,但如果目的仅是避免自动将点号插入第一个参数,则我们可以使用大括号来获得该效果:whatever %>% { myfun(arg1, arg2) }
也不会自动将点号插入myfun
调用的第一个参数。
尊重group_by只有专门编写以尊重 group_by
的函数才会这样做。这里有两个问题。(1)只有专为group_by
编写的函数才会针对每个组运行一次。 mutate
、summarize
和do
是每个组运行一次的函数的示例(还有其他函数)。(2)即使函数对每个组运行一次,仍然存在如何处理点号的问题。我们关注两种情况(不是完整列表):(i)如果不使用do
,则如果在表达式中的函数调用中使用点号作为参数,则它将引用忽略group_by
的整个输入。这可能是magrittr的点替换规则的结果,因为它对group_by
一无所知。(ii)另一方面,在do
中,点始终指当前组的行。例如,请比较这两个输出并注意点在哪里运行:在使用do
的第一种情况下,点引用3行,在第二种情况下引用所有6行。这是尽管summarize
尊重group_by
,因为它对每个组运行一次。
BOD$g <- c(1, 1, 1, 2, 2, 2)
BOD %>% group_by(g) %>% do(summarize(., nr = nrow(.)))
BOD %>% group_by(g) %>% summarize(nr = nrow(.))
更多信息请参见?do
。
问题中的代码
现在我们将浏览问题中的代码。由于问题中从未定义mydata
,因此我们使用下面的第一行代码来定义它以便提供具体示例。
mydata <- data.frame(Index = rep(c("A", "C", "I"), each = 3), Y2014 = 1)
mydata %>%
filter(Index %in% c("A", "C", "I")) %>%
group_by(Index) %>%
do(head(., 2))
上面的代码对于每个三组生成2行,总共6行。如果省略了do
关键字,那么就会忽略group_by
,并且仅生成两行数据,其中点号被视为输入的全部9行,而不仅仅是每个分组的一部分。(在这种特定情况下,dplyr提供了自己的head
替代方法,以避免这些问题,但为了阐明一般性原则,我们坚持使用问题中的代码。)
问题中的以下代码会生成一个错误,因为点插入没有在do
内完成,所以应该作为summarize的第一个参数即数据框输入缺失:
mydata
group_by(Index)
do(summarise(Mean_2014 = mean(Y2014)))
## Error in mean(Y2014) : object 'Y2014' not found
如果我们从上面的代码中删除do
,如问题中的最后一行代码所示,则它可以工作,因为点插入已执行。或者,如果我们添加点do(summarise(., Mean_2014 = mean(Y2014)))
也可以工作,尽管在这种情况下do
似乎是多余的,因��summarize
已经遵守group_by
,所以没有必要将其包装在do
中。
mydata %>%
group_by(Index) %>%
summarise(Mean_2014 = mean(Y2014))
mean
,您可以直接使用summarise
而不需要使用do
。 对于第一个案例,也可以使用slice
而不是do
,即%>% slice(1:2)
。 对于许多其他的 tidyverse 函数,如map
等,很少需要使用do
。 一个可能的用例是在按组构建某些模型时。 - akrundo()
在这个(优秀的)答案中。do
的另一个具体案例:在dplyr中导致长度不等于1或组长度的分组操作。相关dplyr
问题。 - Henrik