如何解释dplyr消息`summarise()`通过“x”重新分组输出(使用`.groups`参数覆盖)?

200

我在更新到dplyr开发版本0.8.99.9003后,在运行group_by和summarise()时收到了一个新消息(请参见帖子标题)。

这里有一个重新创建输出的示例:

library(tidyverse)
library(hablar)
df <- read_csv("year, week, rat_house_females, rat_house_males, mouse_wild_females, mouse_wild_males 
               2018,10,1,1,1,1
               2018,10,1,1,1,1
               2018,11,2,2,2,2
               2018,11,2,2,2,2
               2019,10,3,3,3,3
               2019,10,3,3,3,3
               2019,11,4,4,4,4
               2019,11,4,4,4,4") %>% 
  convert(chr(year,week)) %>% 
  mutate(total_rodents = rowSums(select_if(., is.numeric))) %>% 
  convert(num(year,week)) %>% 
  group_by(year,week) %>% summarise(average = mean(total_rodents))
输出的tibble是正确的,但出现了以下信息:

summarise() 对 'year' 进行重新分组输出 (可以用 .groups 参数来覆盖)

这该如何解释?为什么它只报告按'year'重新分组而不是按年和周分组?另外,“覆盖”是什么意思?我为什么要这样做?
我认为这个消息并没有指示问题,因为它在dplyr文档中一直出现: https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html 我相信这是一个新消息,因为它只出现在最近的一些SO问题中,比如How to melt pairwise.wilcox.test output using dplyr?R Aggregate over multiple columns(两者都没有涉及重新分组/覆盖消息)。
谢谢!
6个回答

262

这只是一个友好的警告信息,关于结果分组结构;你的输出是正确的。默认情况下,在summarise之前如果有任何分组,它会删除一个组变量,即在group_by中指定的最后一个变量。如果只有一个分组变量,则在summarise之后不会有任何分组属性。如果有多个分组变量,则分组会减少1.因此,在您的示例中,由于汇总的输入有两个变量,因此分组属性减少为一个,即生成的数据框将具有“year”作为分组属性。

一个可重现的示例:

library(dplyr)
mtcars %>%
     group_by(am) %>% 
     summarise(mpg = sum(mpg))
#`summarise()` ungrouping output (override with `.groups` argument)
# A tibble: 2 x 2
#     am   mpg
#* <dbl> <dbl>
#1     0  326.
#2     1  317.

消息是,它正在执行ungroup即当只有单个group_by时,在summarise之后会删除该分组。
mtcars %>% 
   group_by(am, vs) %>% 
   summarise(mpg = sum(mpg))
#`summarise()` regrouping output by 'am' (override with `.groups` argument)
# A tibble: 4 x 3
# Groups:   am [2]
#     am    vs   mpg
#  <dbl> <dbl> <dbl>
#1     0     0  181.
#2     0     1  145.
#3     1     0  118.
#4     1     1  199.

这里,它会删除最后一个分组并重新以“am”进行分组

如果我们检查?summarise,可以发现有.groups参数,默认情况下为"drop_last",其他选项包括"drop""keep""rowwise"

.groups - 结果的分组结构。

"drop_last": 删除分组的最后一层。在版本1.0.0之前,这是唯一支持的选项。

"drop": 所有的分组层次都被删除。

"keep": 与.data相同的分组结构。

"rowwise": 每行为单独的一组。

当未指定.groups时,如果所有结果的大小都为1,则会得到"drop_last",否则为"keep"。此外,除非将选项"dplyr.summarise.inform"设置为FALSE,否则会提示您进行选择。

也就是说,如果我们在summarise中更改.groups,那么不会收到消息,因为已删除分组属性。

mtcars %>% 
    group_by(am) %>%
    summarise(mpg = sum(mpg), .groups = 'drop')
# A tibble: 2 x 2
#     am   mpg
#* <dbl> <dbl>
#1     0  326.
#2     1  317.


mtcars %>%
   group_by(am, vs) %>%
   summarise(mpg = sum(mpg), .groups = 'drop')
# A tibble: 4 x 3
#     am    vs   mpg
#* <dbl> <dbl> <dbl>
#1     0     0  181.
#2     0     1  145.
#3     1     0  118.
#4     1     1  199.


mtcars %>% 
   group_by(am, vs) %>% 
   summarise(mpg = sum(mpg), .groups = 'drop') %>%
   str
#tibble [4 × 3] (S3: tbl_df/tbl/data.frame)
# $ am : num [1:4] 0 0 1 1
# $ vs : num [1:4] 0 1 0 1
# $ mpg: num [1:4] 181 145 118 199

以前未发出此警告,这可能导致 OP 执行 mutate 或其他操作,假定没有分组并导致意外输出。现在,该警告向用户指示我们应该注意有一个分组属性。
注意:.groups 目前处于其生命周期的实验性阶段。因此,行为可能会在未来的版本中进行修改。
根据是否需要基于相同的分组变量进一步转换数据(或不需要),我们可以选择 .groups 中的不同选项。

34
解释这个分组属性的重要性也会很有帮助,因为它不太明显。 - jangorecki
12
如果您使用.groups = 'drop',是否意味着在运行某些其他函数(例如case_when或rowSums)之前不必使用ungroup()? - Susie Derkins
12
如果您正在使用summarise函数,且参数中包含groups = 'drop',那么分组的属性将不再存在,因此您无需使用ungroup(至少在当前情况下,除非在tidyverse中更改了此行为)。 - akrun
75
哦!为了使消息静音(保持旧的“drop_last”默认设置),请使用options(dplyr.summarise.inform=F)。 - Mike Lawrence
17
@MikeLawrence 谢谢!这就是我需要的全部内容。以前能正常工作的代码突然出现警告有点让人不安(不存在所谓“友好”的警告)。 - Fluffy
显示剩余8条评论

14
paraphrasing accepted answer, friendly warning that may cause confusion.

summarise()已经根据 “xxx” 分组输出

应该理解为:输出结果正常,包含所有分组列作为属性,只有分组键可能受到限制。
mtcars 中按 cyl, am 分组计算 mean(mpg) 的示例。
mtcars %>% group_by(cyl, am) %>% summarise(avg_mpg = mean(mpg))
`summarise()` has grouped output by 'cyl'. You can override using the `.groups` argument.
# A tibble: 6 x 3
# Groups:   cyl [3]
    cyl    am avg_mpg
  <dbl> <dbl>   <dbl>
1     4     0    22.9
2     4     1    28.1
3     6     0    19.1
4     6     1    20.6
5     8     0    15.0
6     8     1    15.4
警告提示说,在使用默认的.groups = "drop_last"时,输出中只保留了原始分组键的第一个。请参见第# Groups: cyl [3]行。 尽管如此,属性是完整的,cylam都已定义。 这里快速概述了可用选项,并展示了使用函数group_keys()的结果。
mtcars %>% group_by(cyl, am) %>% summarise(avg_mpg = mean(mpg)) %>% group_keys() 
`summarise()` has grouped output by 'cyl'. You can override using the `.groups` argument.
# A tibble: 3 x 1
    cyl
  <dbl>
1     4
2     6
3     8

mtcars %>% group_by(cyl, am) %>% summarise(avg_mpg = mean(mpg), .groups = "keep") %>% group_keys() 
# A tibble: 6 x 2
    cyl    am
  <dbl> <dbl>
1     4     0
2     4     1
3     6     0
4     6     1
5     8     0
6     8     1

mtcars %>% group_by(cyl, am) %>% summarise(avg_mpg = mean(mpg), .groups = "drop") %>% group_keys() 
# A tibble: 1 x 0

唯一可见的影响是在使用级联汇总时 - 下面的例子只产生一个摘要行,因为组键被删除了。

mtcars %>% group_by(cyl, am) %>% summarise(avg_mpg = mean(mpg), .groups = "drop") %>% summarise(min_avg_mpg = min(avg_mpg))
# A tibble: 1 x 1
  min_avg_mpg
        <dbl>
1   15.0

但是由于所有的分组属性都可用,所以在后续的汇总之前,使用group_by(cyl, am)可以很容易地根据需要重置组键


2
答案在?summarise中有解释: “当未指定.groups时,它基于结果行数进行选择: 如果所有结果都只有1行,则会得到“drop_last”。 如果行数不同,则会得到“keep”。”。
基本上,当存在多个选项用作.groups=参数时,您会收到此类消息。该消息警告您已使用一个选项计算了符合上述条件的统计数据:“drop_last”或“keep”,分别用于具有1个或多个行的结果。 假设您的管道由于某种原因应用了两个或多个分组条件,但仍需要对所有值进行汇总而不考虑分组,可以通过设置.group ='drop'来实现。不幸的是,这只是理论上的,因为正如您在@akrun的示例中所看到的那样,无论设置.group =选项如何,统计值始终保持相同(我将这些不同选项应用于我的数据集之一,并获得了相同的结果和相同的数据框结构(“分组结构由.group=参数控制...”)。但是,通过指定参数.group,不会打印任何消息。
底线是,在使用summarise时,如果没有使用分组条件,则输出统计信息是跨所有行计算的,因此“结果有1行”。当使用一个或多个分组条件时,输出统计信息是在每个组内计算的,因此“行数变化”,具体取决于数据帧中的组数。

1

这在https://r4ds.hadley.nz/data-transform.html#grouping-by-multiple-variables中有解释。

当你对一个按多个变量分组的tibble进行汇总时,每个汇总都会剥离最后一组。 事后看来,这不是一个很好的使函数工作的方式,但是很难在不破坏现有代码的情况下进行更改。为了让过程更加明显,dplyr显示了一条消息


1
为了解决这个问题,可以使用summarise(avg_mpg = mean(mpg), .groups = "drop")。实际上,dplyr将结果表解释为分组表,这就是为什么它会显示警告信息的原因。

0
这可能是由于在具有2个或更多分组列时 summarise_all()summarise(across(everything()...的区别导致的。
> tibble(gr1=c(1,1,2), gr2=c(1,1,2), val=1:3) %>% 
    group_by(gr1, gr2) %>% 
    summarise(across(everything(), mean))

#`summarise()` has grouped output by 'gr1'. 
# You can override using the #`.groups` argument.

# A tibble: 2 x 3
# Groups:   gr1 [2]
    gr1   gr2   val
  <dbl> <dbl> <dbl>
1     1     1   1.5
2     2     2   3


> tibble(gr1=c(1,1,2), gr2=c(1,1,2), val=1:3) %>% 
+     group_by(gr1, gr2) %>% 
+     summarise_all(mean)
# No warnings here

# A tibble: 2 x 3
# Groups:   gr1 [2]
    gr1   gr2   val
  <dbl> <dbl> <dbl>
1     1     1   1.5
2     2     2   3

因此,警告的意思是:尽管everything(),但在summarise()中仍将跳过一些列(分组列)


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