【问题标题】:Select the row with the maximum value in each group选择每组中最大值的行
【发布时间】:2014-08-24 20:24:41
【问题描述】:

在每个主题有多个观察的数据集中。对于每个主题,我想选择具有最大值“pt”的行。例如,使用以下数据集:

ID    <- c(1,1,1,2,2,2,2,3,3)
Value <- c(2,3,5,2,5,8,17,3,5)
Event <- c(1,1,2,1,2,1,2,2,2)

group <- data.frame(Subject=ID, pt=Value, Event=Event)
#   Subject pt Event
# 1       1  2     1
# 2       1  3     1
# 3       1  5     2 # max 'pt' for Subject 1
# 4       2  2     1
# 5       2  5     2
# 6       2  8     1
# 7       2 17     2 # max 'pt' for Subject 2
# 8       3  3     2
# 9       3  5     2 # max 'pt' for Subject 3

主题 1、2 和 3 的 pt 值分别为 5、17 和 5。

如何首先找到每个主题的最大 pt 值,然后将此观察结果放入另一个数据框中?生成的数据框应该只有每个主题的最大 pt 值。

【问题讨论】:

标签: r dataframe r-faq


【解决方案1】:

最直观的方法是在dplyr中使用group_bytop_n函数

    group %>% group_by(Subject) %>% top_n(1, pt)

你得到的结果是

    Source: local data frame [3 x 3]
    Groups: Subject [3]

      Subject    pt Event
        (dbl) (dbl) (dbl)
    1       1     5     2
    2       2    17     2
    3       3     5     2

【讨论】:

  • dplyr 在您想要访问组中的最小值和最大值时也很有用,因为这些值可以作为数组使用。所以可以先按pt降序排序,然后用pt[1]或first(pt)取最大值:group %&gt;% group_by(Subject) %&gt;% arrange(desc(pt), .by_group = TRUE) %&gt;% summarise(max_pt=first(pt), min_pt=last(pt), Event=first(Event))
  • 如果有平局,这将包括多行。使用slice(which.max(pt)) 每组只包含一行。
【解决方案2】:

使用 dplyr 1.0.2 现在有两种方法可以做到这一点,一种是长手,另一种是使用动词cross():

      # create data
      ID    <- c(1,1,1,2,2,2,2,3,3)
      Value <- c(2,3,5,2,5,8,17,3,5)
      Event <- c(1,1,2,1,2,1,2,2,2)
      
      group <- data.frame(Subject=ID, pt=Value, Event=Event)

动词是 max() 但请注意 na.rm = TRUE 这对于封闭问题中存在 NA 的示例很有用:Merge rows in a dataframe where the rows are disjoint and contain NAs:

       group %>% 
        group_by(Subject) %>% 
        summarise(pt = max(pt, na.rm = TRUE),
                  Event = max(Event, na.rm = TRUE))

如果只有几列,这是可以的,但如果表有很多列,cross() 很有用。该动词的示例通常使用 summarise(across(start_with...),但在此示例中,列不以相同的字符开头。它们可以更改或列出的位置:

    group %>% 
        group_by(Subject) %>% 
        summarise(across(1:ncol(group)-1, max, na.rm = TRUE, .names = "{.col}"))

注意动词cross() 1 指的是第一列第一个实际列之后,因此使用ncol(group) 将不起作用,因为列太多(使其位置4 而不是大于 3)。

【讨论】:

    【解决方案3】:

    自 {dplyr} v1.0.0(2020 年 5 月)以来,新的 slice_* 语法取代了 top_n()

    另见https://dplyr.tidyverse.org/reference/slice.html

    library(tidyverse)
    
    ID    <- c(1,1,1,2,2,2,2,3,3)
    Value <- c(2,3,5,2,5,8,17,3,5)
    Event <- c(1,1,2,1,2,1,2,2,2)
    
    group <- data.frame(Subject=ID, pt=Value, Event=Event)
    
    group %>% 
      group_by(Subject) %>% 
      slice_max(pt)
    #> # A tibble: 3 x 3
    #> # Groups:   Subject [3]
    #>   Subject    pt Event
    #>     <dbl> <dbl> <dbl>
    #> 1       1     5     2
    #> 2       2    17     2
    #> 3       3     5     2
    

    reprex package (v0.3.0.9001) 于 2020 年 8 月 18 日创建

    会话信息
    sessioninfo::session_info()
    #> ─ Session info ───────────────────────────────────────────────────────────────
    #>  setting  value                                      
    #>  version  R version 4.0.2 Patched (2020-06-30 r78761)
    #>  os       macOS Catalina 10.15.6                     
    #>  system   x86_64, darwin17.0                         
    #>  ui       X11                                        
    #>  language (EN)                                       
    #>  collate  en_US.UTF-8                                
    #>  ctype    en_US.UTF-8                                
    #>  tz       Europe/Berlin                              
    #>  date     2020-08-18                                 
    #> 
    #> ─ Packages ───────────────────────────────────────────────────────────────────
    #>  package     * version    date       lib source                            
    #>  assertthat    0.2.1      2019-03-21 [1] CRAN (R 4.0.0)                    
    #>  backports     1.1.8      2020-06-17 [1] CRAN (R 4.0.1)                    
    #>  blob          1.2.1      2020-01-20 [1] CRAN (R 4.0.0)                    
    #>  broom         0.7.0      2020-07-09 [1] CRAN (R 4.0.2)                    
    #>  cellranger    1.1.0      2016-07-27 [1] CRAN (R 4.0.0)                    
    #>  cli           2.0.2      2020-02-28 [1] CRAN (R 4.0.0)                    
    #>  colorspace    1.4-1      2019-03-18 [1] CRAN (R 4.0.0)                    
    #>  crayon        1.3.4      2017-09-16 [1] CRAN (R 4.0.0)                    
    #>  DBI           1.1.0      2019-12-15 [1] CRAN (R 4.0.0)                    
    #>  dbplyr        1.4.4      2020-05-27 [1] CRAN (R 4.0.0)                    
    #>  digest        0.6.25     2020-02-23 [1] CRAN (R 4.0.0)                    
    #>  dplyr       * 1.0.1      2020-07-31 [1] CRAN (R 4.0.2)                    
    #>  ellipsis      0.3.1      2020-05-15 [1] CRAN (R 4.0.0)                    
    #>  evaluate      0.14       2019-05-28 [1] CRAN (R 4.0.0)                    
    #>  fansi         0.4.1      2020-01-08 [1] CRAN (R 4.0.0)                    
    #>  forcats     * 0.5.0      2020-03-01 [1] CRAN (R 4.0.0)                    
    #>  fs            1.5.0      2020-07-31 [1] CRAN (R 4.0.2)                    
    #>  generics      0.0.2      2018-11-29 [1] CRAN (R 4.0.0)                    
    #>  ggplot2     * 3.3.2      2020-06-19 [1] CRAN (R 4.0.1)                    
    #>  glue          1.4.1      2020-05-13 [1] CRAN (R 4.0.0)                    
    #>  gtable        0.3.0      2019-03-25 [1] CRAN (R 4.0.0)                    
    #>  haven         2.3.1      2020-06-01 [1] CRAN (R 4.0.0)                    
    #>  highr         0.8        2019-03-20 [1] CRAN (R 4.0.0)                    
    #>  hms           0.5.3      2020-01-08 [1] CRAN (R 4.0.0)                    
    #>  htmltools     0.5.0      2020-06-16 [1] CRAN (R 4.0.1)                    
    #>  httr          1.4.2      2020-07-20 [1] CRAN (R 4.0.2)                    
    #>  jsonlite      1.7.0      2020-06-25 [1] CRAN (R 4.0.2)                    
    #>  knitr         1.29       2020-06-23 [1] CRAN (R 4.0.2)                    
    #>  lifecycle     0.2.0      2020-03-06 [1] CRAN (R 4.0.0)                    
    #>  lubridate     1.7.9      2020-06-08 [1] CRAN (R 4.0.1)                    
    #>  magrittr      1.5        2014-11-22 [1] CRAN (R 4.0.0)                    
    #>  modelr        0.1.8      2020-05-19 [1] CRAN (R 4.0.0)                    
    #>  munsell       0.5.0      2018-06-12 [1] CRAN (R 4.0.0)                    
    #>  pillar        1.4.6      2020-07-10 [1] CRAN (R 4.0.2)                    
    #>  pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.0.0)                    
    #>  purrr       * 0.3.4      2020-04-17 [1] CRAN (R 4.0.0)                    
    #>  R6            2.4.1      2019-11-12 [1] CRAN (R 4.0.0)                    
    #>  Rcpp          1.0.5      2020-07-06 [1] CRAN (R 4.0.2)                    
    #>  readr       * 1.3.1      2018-12-21 [1] CRAN (R 4.0.0)                    
    #>  readxl        1.3.1      2019-03-13 [1] CRAN (R 4.0.0)                    
    #>  reprex        0.3.0.9001 2020-08-13 [1] Github (tidyverse/reprex@23a3462) 
    #>  rlang         0.4.7      2020-07-09 [1] CRAN (R 4.0.2)                    
    #>  rmarkdown     2.3.3      2020-07-26 [1] Github (rstudio/rmarkdown@204aa41)
    #>  rstudioapi    0.11       2020-02-07 [1] CRAN (R 4.0.0)                    
    #>  rvest         0.3.6      2020-07-25 [1] CRAN (R 4.0.2)                    
    #>  scales        1.1.1      2020-05-11 [1] CRAN (R 4.0.0)                    
    #>  sessioninfo   1.1.1      2018-11-05 [1] CRAN (R 4.0.2)                    
    #>  stringi       1.4.6      2020-02-17 [1] CRAN (R 4.0.0)                    
    #>  stringr     * 1.4.0      2019-02-10 [1] CRAN (R 4.0.0)                    
    #>  styler        1.3.2.9000 2020-07-05 [1] Github (pat-s/styler@51d5200)     
    #>  tibble      * 3.0.3      2020-07-10 [1] CRAN (R 4.0.2)                    
    #>  tidyr       * 1.1.1      2020-07-31 [1] CRAN (R 4.0.2)                    
    #>  tidyselect    1.1.0      2020-05-11 [1] CRAN (R 4.0.0)                    
    #>  tidyverse   * 1.3.0      2019-11-21 [1] CRAN (R 4.0.0)                    
    #>  utf8          1.1.4      2018-05-24 [1] CRAN (R 4.0.0)                    
    #>  vctrs         0.3.2      2020-07-15 [1] CRAN (R 4.0.2)                    
    #>  withr         2.2.0      2020-04-20 [1] CRAN (R 4.0.0)                    
    #>  xfun          0.16       2020-07-24 [1] CRAN (R 4.0.2)                    
    #>  xml2          1.3.2      2020-04-23 [1] CRAN (R 4.0.0)                    
    #>  yaml          2.2.1      2020-02-01 [1] CRAN (R 4.0.0)                    
    #> 
    #> [1] /Users/pjs/Library/R/4.0/library
    #> [2] /Library/Frameworks/R.framework/Versions/4.0/Resources/library
    

    【讨论】:

      【解决方案4】:

      base 中,您可以使用 ave 获取每个组的 max 并将其与 pt 进行比较,并获取一个逻辑向量来子集 data.frame

      group[group$pt == ave(group$pt, group$Subject, FUN=max),]
      #  Subject pt Event
      #3       1  5     2
      #7       2 17     2
      #9       3  5     2
      

      或者已经在函数中比较了。

      group[as.logical(ave(group$pt, group$Subject, FUN=function(x) x==max(x))),]
      #group[ave(group$pt, group$Subject, FUN=function(x) x==max(x))==1,] #Variant
      #  Subject pt Event
      #3       1  5     2
      #7       2 17     2
      #9       3  5     2
      

      【讨论】:

        【解决方案5】:

        bytapply 的一个版本,用于数据帧:

        res <- by(group, group$Subject, FUN=function(df) df[which.max(df$pt),])
        

        它返回一个by类的对象,所以我们将它转​​换为数据框:

        do.call(rbind, b)
          Subject pt Event
        1       1  5     2
        2       2 17     2
        3       3  5     2
        

        【讨论】:

          【解决方案6】:

          另一个data.table解决方案:

          library(data.table)
          setDT(group)[, head(.SD[order(-pt)], 1), by = .(Subject)]
          

          【讨论】:

            【解决方案7】:

            另一个data.table 选项:

            library(data.table)
            setDT(group)
            group[group[order(-pt), .I[1L], Subject]$V1]
            

            或另一个(可读性较差但速度稍快):

            group[group[, rn := .I][order(Subject, -pt), {
                rn[c(1L, 1L + which(diff(Subject)>0L))]
            }]]
            

            计时码:

            library(data.table)
            nr <- 1e7L
            ng <- nr/4L
            set.seed(0L)
            DT <- data.table(Subject=sample(ng, nr, TRUE), pt=1:nr)#rnorm(nr))
            DT2 <- copy(DT)
            
            
            microbenchmark::microbenchmark(times=3L,
                mtd0 = {a0 <- DT[DT[, .I[which.max(pt)], by=Subject]$V1]},
                mtd1 = {a1 <- DT[DT[order(-pt), .I[1L], Subject]$V1]},
                mtd2 = {a2 <- DT2[DT2[, rn := .I][
                    order(Subject, -pt), rn[c(TRUE, diff(Subject)>0L)]
                ]]},
                mtd3 = {a3 <- unique(DT[order(Subject, -pt)], by="Subject")}
            )
            fsetequal(a0[order(Subject)], a1[order(Subject)])
            #[1] TRUE
            fsetequal(a0[order(Subject)], a2[, rn := NULL][order(Subject)])
            #[1] TRUE
            fsetequal(a0[order(Subject)], a3[order(Subject)])
            #[1] TRUE
            

            时间安排:

            Unit: seconds
             expr      min       lq     mean   median       uq      max neval
             mtd0 3.256322 3.335412 3.371439 3.414502 3.428998 3.443493     3
             mtd1 1.733162 1.748538 1.786033 1.763915 1.812468 1.861022     3
             mtd2 1.136307 1.159606 1.207009 1.182905 1.242359 1.301814     3
             mtd3 1.123064 1.166161 1.228058 1.209257 1.280554 1.351851     3
            

            【讨论】:

              【解决方案8】:

              另一种基本解决方案

              group_sorted <- group[order(group$Subject, -group$pt),]
              group_sorted[!duplicated(group_sorted$Subject),]
              
              # Subject pt Event
              #       1  5     2
              #       2 17     2
              #       3  5     2
              

              pt(降序)对数据框进行排序,然后删除Subject中重复的行

              【讨论】:

                【解决方案9】:

                另一种基本的 R 解决方案:

                merge(aggregate(pt ~ Subject, max, data = group), group)
                
                  Subject pt Event
                1       1  5     2
                2       2 17     2
                3       3  5     2
                

                【讨论】:

                  【解决方案10】:

                  这是另一个data.table 解决方案,因为which.max 不适用于字符

                  library(data.table)
                  group <- data.table(Subject=ID, pt=Value, Event=Event)
                  
                  group[, .SD[order(pt, decreasing = TRUE) == 1], by = Subject]
                  

                  【讨论】:

                    【解决方案11】:

                    dplyr 解决方案:

                    library(dplyr)
                    ID <- c(1,1,1,2,2,2,2,3,3)
                    Value <- c(2,3,5,2,5,8,17,3,5)
                    Event <- c(1,1,2,1,2,1,2,2,2)
                    group <- data.frame(Subject=ID, pt=Value, Event=Event)
                    
                    group %>%
                        group_by(Subject) %>%
                        summarize(max.pt = max(pt))
                    

                    这会产生以下数据框:

                      Subject max.pt
                    1       1      5
                    2       2     17
                    3       3      5
                    

                    【讨论】:

                    • 我认为 OP 希望将 Event 列保留在子集中,在这种情况下您可以这样做:df %&gt;% group_by(Subject) %&gt;% filter(pt == max(pt))(如果存在则包括领带)
                    【解决方案12】:

                    如果你想要一个主题的最大 pt 值,你可以简单地使用:

                       pt_max = as.data.frame(aggregate(pt~Subject, group, max))
                    

                    【讨论】:

                      【解决方案13】:
                      do.call(rbind, lapply(split(group,as.factor(group$Subject)), function(x) {return(x[which.max(x$pt),])}))
                      

                      使用基地R

                      【讨论】:

                        【解决方案14】:

                        另一个选项是slice

                        library(dplyr)
                        group %>%
                             group_by(Subject) %>%
                             slice(which.max(pt))
                        #    Subject    pt Event
                        #    <dbl> <dbl> <dbl>
                        #1       1     5     2
                        #2       2    17     2
                        #3       3     5     2
                        

                        【讨论】:

                          【解决方案15】:

                          使用data.table 的更短的解决方案:

                          setDT(group)[, .SD[which.max(pt)], by=Subject]
                          #    Subject pt Event
                          # 1:       1  5     2
                          # 2:       2 17     2
                          # 3:       3  5     2
                          

                          【讨论】:

                          • 请注意,这可能比@Arun 上面提出的group[group[, .I[which.max(pt)], by=Subject]$V1] 慢;查看比较here
                          • 我喜欢这个,因为它对于我当前的上下文来说足够快,并且比 .I 版本更容易理解
                          • setDT(group)[, .SD[ pt== max(pt) ] , by=Subject]
                          【解决方案16】:

                          这是data.table 解决方案:

                          require(data.table) ## 1.9.2
                          group <- as.data.table(group)
                          

                          如果您想在每个组中保留与pt 的最大值对应的所有条目:

                          group[group[, .I[pt == max(pt)], by=Subject]$V1]
                          #    Subject pt Event
                          # 1:       1  5     2
                          # 2:       2 17     2
                          # 3:       3  5     2
                          

                          如果您只想要pt 的第一个最大值:

                          group[group[, .I[which.max(pt)], by=Subject]$V1]
                          #    Subject pt Event
                          # 1:       1  5     2
                          # 2:       2 17     2
                          # 3:       3  5     2
                          

                          在这种情况下,它没有任何区别,因为您的数据中的任何组中都没有多个最大值。

                          【讨论】:

                          • 自 2014 年以来 data.table 发生了很多变化,这仍然是这个问题的最快/最佳解决方案吗?
                          • @Ben,在这种情况下,最快的答案仍然是这个,是的。 .SD 这些案例的优化仍在列表中。关注#735
                          • 嗨,这里的 $V1 是什么? #菜鸟
                          • 访问自动命名的列。在没有它的情况下运行它以更好地理解。
                          • @HappyCoding,看看?`.I`,看看那里的解释和例子是否有帮助?
                          【解决方案17】:

                          我不确定你想对 Event 列做什么,但如果你也想保留它,怎么样

                          isIDmax <- with(dd, ave(Value, ID, FUN=function(x) seq_along(x)==which.max(x)))==1
                          group[isIDmax, ]
                          
                          #   ID Value Event
                          # 3  1     5     2
                          # 7  2    17     2
                          # 9  3     5     2
                          

                          这里我们使用ave 查看每个“ID”的“值”列。然后我们确定哪个值是最大值,然后将其转换为一个逻辑向量,我们可以使用它来对原始 data.frame 进行子集化。

                          【讨论】:

                          • 非常感谢,但我还有另一个问题。为什么在这种方法中使用 with function,因为 ave(Value, ID, FUN=function(x) seq_along(x)==which.max(x))==1 工作得非常好?我有点困惑。
                          • 我使用了with,因为在group data.frame 内部和外部都提供数据有点奇怪。如果您使用read.table 或其他方式读取数据,则需要使用with,因为这些列名在data.frame 之外不可用。
                          猜你喜欢
                          • 2011-05-29
                          • 2021-10-11
                          • 1970-01-01
                          • 2022-11-13
                          • 1970-01-01
                          • 1970-01-01
                          • 2020-08-19
                          • 1970-01-01
                          • 2020-05-04
                          相关资源
                          最近更新 更多