【问题标题】:How to group by all but one columns?如何按除一列以外的所有列分组?
【发布时间】:2017-01-04 00:09:29
【问题描述】:

我如何告诉group_by 按除给定列之外的所有列对数据进行分组?

使用aggregate,它将是aggregate(x ~ ., ...)

我尝试了group_by(data, -x),但它按 x 的负数分组(即与按 x 分组相同)。

【问题讨论】:

标签: r dplyr


【解决方案1】:

dplyr 版本 1.0+

在即将推出的 dplyr 1.0.0 中,_at 函数正落入被取代的生命周期(即,虽然它们在可预见的未来仍保留在 dplyr 中,但现在有更好的替代方案更积极地发展)。实现这一点的新方法是通过across 函数:

df %>%
  group_by(across(c(-hp)))

dplyr v 0.7+

关于这个问题的一个小更新,因为我自己偶然发现了这个问题,并找到了一个优雅的解决方案,当前版本为 dplyr (0.7.4): 在group_by_at() 中,您可以使用vars() 提供与select() 函数中相同的列名称。这使我们能够按除一列(本例中为hp)以外的所有内容进行分组:

library(dplyr)
df <- as_tibble(mtcars, rownames = "car")
df %>% group_by_at(vars(-hp))

【讨论】:

  • 您甚至可以提供几个要忽略的列:df %&gt;% group_by_at(vars(-hp, -cyl)),而无需使用 c() 构造。超级好看!
  • 它甚至可以工作group_by(across(!hp)。如果现在可以将其标记为答案,那就太好了。
【解决方案2】:

基于 @eipi10 的 dplyr 0.7.0 编辑,group_by_at 似乎是这项工作的正确功能。但是,如果您只是想省略列“x”,那么您可以使用:

new2.0 <- dat %>%
  group_by_at(vars(-x)) %>%
  summarize(mean_value = mean(value))

使用@eipi10 的示例数据:

# Fake data
set.seed(492)
dat <- data.frame(value = rnorm(1000),
             g1 = sample(LETTERS, 1000, replace = TRUE),
             g2 = sample(letters, 1000, replace = TRUE),
             g3 = sample(1:10, replace = TRUE),
             other = sample(c("red", "green", "black"), 1000, replace = TRUE))

new <- dat %>% 
  group_by_at(names(dat)[-grep("value", names(dat))]) %>%
  summarise(meanValue = mean(value))


new2.0 <- dat %>% 
  group_by_at(vars(-value)) %>% 
  summarize(meanValue = mean(value))

identical(new, new2.0)
# [1] TRUE

【讨论】:

    【解决方案3】:

    您可以使用标准评估(group_by_ 而不是group_by)来做到这一点:

    # Fake data
    set.seed(492)
    dat = data.frame(value=rnorm(1000), g1=sample(LETTERS,1000,replace=TRUE),
                     g2=sample(letters,1000,replace=TRUE), g3=sample(1:10, replace=TRUE),
                     other=sample(c("red","green","black"),1000,replace=TRUE))
    
    dat %>% group_by_(.dots=names(dat)[-grep("value", names(dat))]) %>%
      summarise(meanValue=mean(value))
    
           g1     g2    g3  other   meanValue
       <fctr> <fctr> <int> <fctr>       <dbl>
    1       A      a     2  green  0.89281475
    2       A      b     2    red -0.03558775
    3       A      b     5  black -1.79184218
    4       A      c    10  black  0.17518610
    5       A      e     5  black  0.25830392
    ...
    

    请参阅this vignette,了解更多关于dplyr 中标准与非标准评估的信息。

    dplyr0.7.0 的更新

    解决@ÖmerAn 的评论:看起来group_by_at 是进入dplyr 0.7.0 的方式(如果我对此有误,请有人纠正我)。例如:

    dat %>% 
      group_by_at(setdiff(names(dat), "value")) %>%
      summarise(meanValue=mean(value))
    
    # Groups:   g1, g2, g3 [?]
           g1     g2    g3  other   meanValue
       <fctr> <fctr> <int> <fctr>       <dbl>
     1      A      a     2  green  0.89281475
     2      A      b     2    red -0.03558775
     3      A      b     5  black -1.79184218
     4      A      c    10  black  0.17518610
     5      A      e     5  black  0.25830392
     6      A      e     5    red -0.81879788
     7      A      e     7  green  0.30836054
     8      A      f     2  green  0.05537047
     9      A      g     1  black  1.00156405
    10      A      g    10  black  1.26884303
    # ... with 949 more rows
    

    让我们确认两种方法的输出相同(dplyr 0.7.0):

    new = dat %>% 
      group_by_at(setdiff(names(dat), "value")) %>%
      summarise(meanValue=mean(value))
    
    old = dat %>% 
      group_by_(.dots=names(dat)[-grep("value", names(dat))]) %>%
      summarise(meanValue=mean(value))
    
    identical(old, new)
    # [1] TRUE
    

    【讨论】:

    • 也可以用names(dat)[-grep("value", names(dat))]代替setdiff(names(dat), "value")
    • 虽然风险更大,但我想您甚至可以按位置选择:names(dat)[-1]。我们在打高尔夫球,对吧? :)
    • 当然也是一个有效的选择:-)
    • 另外,如果您想一次汇总所有列,请使用dplyr::summarise_each(funs(mean))
    • 如何在最新版本的dplyr 0.7.0 中执行此操作,该版本声称现在已弃用动词的 SE 版本? github.com/tidyverse/dplyr/releases
    猜你喜欢
    • 1970-01-01
    • 2023-01-25
    • 1970-01-01
    • 1970-01-01
    • 2022-07-21
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多