【问题标题】:When to use "Do" function in dplyr何时在 dplyr 中使用“Do”功能
【发布时间】:2018-06-19 08:26:04
【问题描述】:

我了解到Do 函数用于将函数应用于每个组。

例如,如果我想从变量Index的“A”、“C”和“I”类别中提取前2行,可以使用以下语法。

t <- mydata %>% filter(Index %in% c("A", "C", "I")) %>% group_by(Index) %>% do(head(.,2))

我了解在按索引分组后,do 函数用于计算每个组的 head(.,2)。

但是,在某些情况下,do 根本不被使用。例如,要计算按变量Index 分组的变量Y2014 的平均值,我认为应该使用以下代码。

t <- mydata %>% group_by(Index) %>% do(summarise(Mean_2014 = mean(Y2014)))

但是,上面的语法返回错误

Error in mean(Y2014) : object 'Y2014' not found

但如果我从语法中删除 do,它会返回我真正想要的内容。

t <- mydata %>% group_by(Index) %>% summarise(Mean_2014 = mean(Y2014))

我对在 dplyr 中使用 do 函数感到非常困惑。对我来说似乎不一致。我应该在什么时候使用和不使用do 函数?为什么我应该在第一种情况下使用do 而不是在第二种情况下?

【问题讨论】:

标签: r dplyr


【解决方案1】:

问题下的 cmets 讨论了在许多情况下,您可以在 dplyr 或相关软件包中找到避免使用 do 的替代方案,并且问题中的示例属于这种情况;但是,直接回答这个问题而不是通过替代方法:

使用do和不使用的区别

在数据帧的上下文中,使用do 和不使用do 之间的主要区别是:

  1. 不自动插入点do 中的代码不会将点自动插入到第一个参数中。例如,不是问题中的do(summarise(Mean_2014 = mean(Y2014))) 代码,而是必须用点编写do(summarise(., Mean_2014 = mean(Y2014))),因为点不会自动插入。这是do%&gt;% 而不是summarize 的右侧函数的结果。虽然理解这一点很重要,以便我们在需要时插入点,如果目的只是为了避免将点自动插入第一个参数,我们可以交替使用大括号来获得这种效果:whatever %&gt;% { myfun(arg1, arg2) }not em> 自动插入点作为myfun 调用的第一个参数。

  2. 尊重 group_by 只有专门为尊重group_by 而编写的函数才会这样做。这里有两个问题。 (1) 只有专门为尊重group_by 而编写的函数才会为每个组运行一次​​。 mutatesummarizedo 是每组运行一次​​的函数示例(还有其他函数)。 (2) 即使函数对每个组运行一次​​,也存在如何处理点的问题。我们关注两种情况(不是完整的列表):(i)如果do 没有被使用,那么如果在一个函数调用中对一个参数的表达式使用点,它将引用整个输入而忽略group_by。大概这是 magrittr 的点替换规则的结果,它对group_by 一无所知。另一方面 (ii) 在do dot 内总是指当前组的行。例如,比较这两者的输出并注意在第一种情况下使用 do 时 dot 表示 3 行,而在第二种情况下没有使用 6 行。尽管summarize 尊重group_by,因为它每组运行一次​​。

    BOD$g <- c(1, 1, 1, 2, 2, 2)
    BOD %>% group_by(g) %>% do(summarize(., nr = nrow(.)))
    ## # A tibble: 2 x 2
    ## # Groups: g [2]
    ##       g    nr
    ##   <dbl> <int>
    ## 1  1.00     3
    ## 2  2.00     3
    
    BOD %>% group_by(g) %>% summarize(nr = nrow(.))
    ## # A tibble: 2 x 2
    ##       g    nr
    ##   <dbl> <int>
    ## 1  1.00     6
    ## 2  2.00     6
    

更多信息请参见?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))

## # A tibble: 6 x 2
## # Groups: Index [3]
##   Index  Y2014
##   <fctr> <dbl>
## 1 A       1.00
## 2 A       1.00
## 3 C       1.00
## 4 C       1.00
## 5 I       1.00
## 6 I       1.00

上面的代码为 3 组中的每组生成 2 行,共 6 行。如果我们省略了do,那么它将忽略group_by,只生成两行,点被视为整个9行输入,而不是一次只生成每一组。 (在这种特殊情况下,dplyr 提供了自己的 head 替代方案,可以避免这些问题,但为了说明一般观点,我们坚持问题中的代码。)

问题中的以下代码会产生错误,因为点插入未在 do 内完成,因此缺少 summarise 的第一个参数,即数据框输入:

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))

## # A tibble: 3 x 2
##   Index  Mean_2014
##   <fctr>     <dbl>
## 1 A           1.00
## 2 C           1.00
## 3 I           1.00

【讨论】:

  • 有趣。换句话说,只有 tidyverse-native 函数才能在没有 do? 的情况下正常工作?也就是说,这完全可以正常工作 BOD %&gt;% group_by(g) %&gt;% do(summarize(., nr = n()) 。 nrow(.) 是base 函数的问题吗?
  • 问题是如何处理点。 n() 不涉及点。
  • 好的,我明白了。最重要的是:如果您调用一个引用带有. 的数据框的函数,请始终使用do。这样我们就可以确定“通过”的数据帧是正确的子组。这有意义吗?
  • 但也有 gap_fits &lt;- gap_nested %&gt;% mutate(fit = map(data, ~ lm(lifeExp ~ year, data = .x))) 的情况,如 jennybc.github.io/purrr-tutorial/ls13_list-columns.html 。在这种情况下,只有.x 没有do 似乎没问题。你不觉得这有点不一致吗?
  • .x 是以公式表示法表示的函数的参数。 dot 是管道的 LHS。
猜你喜欢
  • 1970-01-01
  • 2016-07-15
  • 2015-10-11
  • 2018-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多