R: dplyr 和 row_number() 没有按预期枚举

R: dplyr and row_number() does not enumerate as expected

我想枚举分组产生的 dataframe/tibble 的每条记录。该索引是根据定义的顺序。如果我使用 row_number() 它会枚举但在组内。但我希望它在不考虑前一个分组的情况下进行枚举。

这是一个例子。为了简单起见,我使用了最小的数据框:

library(dplyr)

df0 <- data.frame( x1 = rep(LETTERS[1:2],each=2)
                 , x2 = rep(letters[1:2], 2)
                 , y = floor(abs(rnorm(4)*10))
)
df0
#   x1 x2  y
# 1  A  a 12
# 2  A  b 24
# 3  B  a  0
# 4  B  b 12

现在,我将其分组 table:

 df1 <- df0 %>% group_by(x1,x2) %>% summarize(y=sum(y))

这给了我一个 class tibble 的对象:

 # A tibble: 4 x 3
 # Groups:   x1 [?]
 #   x1    x2        y
 #   <fct> <fct> <dbl>
 # 1 A     a        12
 # 2 A     b        24
 # 3 B     a         0
 # 4 B     b        12

我想使用 row_numer():

向此 table 添加行号
 df2 <- df1 %>% arrange(desc(y)) %>% mutate(index = row_number())
 df2
 # A tibble: 4 x 4
 # Groups:   x1 [2]
 #   x1    x2        y index
 #   <fct> <fct> <dbl> <int>
 # 1 A     b        24     1
 # 2 A     a        12     2
 # 3 B     b        12     1
 # 4 B     a         0     2

row_number() 确实在前一个分组中进行了枚举。这不是我的本意。这可以避免首先将 tibble 转换为数据帧:

 df2 <- df2 %>% as.data.frame() %>% arrange(desc(y)) %>% mutate(index = row_number())
 df2
 #   x1 x2  y index
 # 1  A  b 24     1
 # 2  A  a 12     2
 # 3  B  b 12     3
 # 4  B  a  0     4

我的问题是:这种行为是故意的吗? 如果是:将以前的数据处理合并到 tibble 中不是很危险吗?合并了哪种类型的处理? 目前我会把tibble转换成dataframe来避免这种意想不到的结果。

详细说明我的评论:是的,保留分组是有意的,并且在许多情况下很有用。如果您不了解 group_by 的工作原理,那只会很危险——任何函数都是如此。要撤消 group_by,您调用 ungroup

看看 group_by docs,因为它们非常详尽,并解释了此函数如何与其他函数交互、分组如何分层等。文档还解释了每次调用 summarise 删除了一层分组——它可能会让您对发生的事情感到困惑。

例如,你可以按x1x2分组,汇总y,然后创建一个行号,它会根据x1给你行(summarise去掉一层分组,即去掉x2分组)。然后取消分组允许您根据整个数据框获取行号。

library(dplyr)

df0 %>%
  group_by(x1, x2) %>%
  summarise(y = sum(y)) %>%
  mutate(group_row = row_number()) %>%
  ungroup() %>%
  mutate(all_df_row = row_number())
#> # A tibble: 4 x 5
#>   x1    x2        y group_row all_df_row
#>   <fct> <fct> <dbl>     <int>      <int>
#> 1 A     a        12         1          1
#> 2 A     b         2         2          2
#> 3 B     a        10         1          3
#> 4 B     b        23         2          4

一个用例——我可能每天都这样做是为了工作——是获取多个组内的总和(同样,x1x2),然后找到这些值在其中的份额他们更大的一组(剥去一层分组后,这是x1)和mutate。同样,我在这里取消组合以显示份额而不是整个数据框。

df0 %>%
  group_by(x1, x2) %>%
  summarise(y = sum(y)) %>%
  mutate(share_in_group = y / sum(y)) %>%
  ungroup() %>%
  mutate(share_all_df = y / sum(y))
#> # A tibble: 4 x 5
#>   x1    x2        y share_in_group share_all_df
#>   <fct> <fct> <dbl>          <dbl>        <dbl>
#> 1 A     a        12          0.857       0.255 
#> 2 A     b         2          0.143       0.0426
#> 3 B     a        10          0.303       0.213 
#> 4 B     b        23          0.697       0.489

reprex package (v0.2.1)

创建于 2018-10-11

正如 camille 很好地展示的那样,有充分的理由希望 summarize() 的结果保留额外的分组层,这是一种 documented 行为,因此本身并不真正危险或意外。

然而,一个额外的提示是,如果您只是想在 summarize() 之后调用 ungroup(),您不妨使用 summarize(.groups = "drop"),这将 return 和 ungrouped tibble 并为您节省一行代码。

library(tidyverse)

df0 <- data.frame(
  x1 = rep(LETTERS[1:2], each = 2),
  x2 = rep(letters[1:2], 2),
  y = floor(abs(rnorm(4) * 10))
)

df0 %>% 
  group_by(x1,x2) %>% 
  summarize(y=sum(y), .groups = "drop") %>% 
  arrange(desc(y)) %>% 
  mutate(index = row_number())
#> # A tibble: 4 x 4
#>   x1    x2        y index
#>   <chr> <chr> <dbl> <int>
#> 1 A     b         8     1
#> 2 A     a         2     2
#> 3 B     a         2     3
#> 4 B     b         1     4

reprex package (v2.0.1)

于 2022-02-06 创建