Cách chọn hàng có giá trị lớn nhất trong mỗi nhóm


94

Trong một tập dữ liệu có nhiều quan sát cho mỗi chủ đề, tôi muốn lấy một tập con chỉ có giá trị dữ liệu lớn nhất cho mỗi bản ghi. Ví dụ, với một tập dữ liệu sau:

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)

Chủ đề 1, 2 và 3 có giá trị pt lớn nhất lần lượt là 5, 17 và 5.

Đầu tiên tôi có thể tìm giá trị pt lớn nhất cho mỗi đối tượng như thế nào, sau đó đặt quan sát này vào khung dữ liệu khác? Khung dữ liệu kết quả chỉ nên có các giá trị pt lớn nhất cho mỗi chủ đề.


2
Điều này có liên quan rất chặt chẽ nhưng ở mức tối thiểu thay vì tối đa stackoverflow.com/questions/24070714/…
David Arenburg

Câu trả lời:


96

Đây là một data.tablegiải pháp:

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

Nếu bạn muốn giữ tất cả các mục nhập tương ứng với giá trị tối đa của ptmỗi nhóm:

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

Nếu bạn chỉ muốn giá trị tối đa đầu tiên của 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

Trong trường hợp này, nó không tạo ra sự khác biệt vì không có nhiều giá trị tối đa trong bất kỳ nhóm nào trong dữ liệu của bạn.


2
xem như data.table đã có rất nhiều thay đổi kể từ năm 2014, đây có còn là giải pháp nhanh nhất / tốt nhất cho câu hỏi này không?
Ben

2
@Ben, trong trường hợp này, câu trả lời nhanh nhất vẫn là cái này, vâng. .SDtối ưu hóa cho những trường hợp này vẫn còn trong danh sách. Hãy để mắt đến # 735 .
Arun

6
Xin chào, $ V1 ở đây là gì? #noob
sriharsha KB

1
Truy cập cột được đặt tên tự động. Chạy nó mà không có nó để hiểu rõ hơn.
Arun

2
@HappyCoding, hãy xem ?`.I`và xem giải thích và ví dụ ở đó có giúp ích gì không?
Arun

64

Phương pháp trực quan nhất là sử dụng hàm group_by và top_n trong dplyr

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

Kết quả bạn nhận được là

    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

2
dplyr cũng hữu ích khi bạn muốn truy cập giá trị nhỏ nhất và lớn nhất trong một nhóm vì các giá trị có sẵn dưới dạng một mảng. Vì vậy, trước tiên bạn có thể sắp xếp theo pt giảm dần và sau đó sử dụng pt [1] hoặc pt đầu tiên (pt) để nhận được giá trị cao nhất: group %>% group_by(Subject) %>% arrange(desc(pt), .by_group = TRUE) %>% summarise(max_pt=first(pt), min_pt=last(pt), Event=first(Event))
cw '

3
Điều này sẽ bao gồm nhiều hàng nếu có ràng buộc. Sử dụng slice(which.max(pt))để chỉ bao gồm một hàng cho mỗi nhóm.
cakraww

36

Một giải pháp ngắn hơn sử dụng 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

4
Lưu ý rằng, điều này có thể chậm hơn so group[group[, .I[which.max(pt)], by=Subject]$V1]với đề xuất ở trên của @Arun; thấy sự so sánh ở đây
Valentin

1
Tôi như thế này vì nó đủ nhanh cho bối cảnh hiện tại của tôi và dễ dàng hơn để grok cho tôi vs các .Iphiên bản
arvi1000

setDT (group) [, .SD [pt == max (pt)], by = Subject]
Ferroao

20

Một lựa chọn khác là 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

14

Một dplyrgiải pháp:

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

Điều này tạo ra khung dữ liệu sau:

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

11
Tôi nghĩ rằng OP muốn giữ các Eventcột trong tập hợp con trong trường hợp này bạn có thể làm: df %>% group_by(Subject) %>% filter(pt == max(pt))(bao gồm mối quan hệ nếu có)
Talat

8

Tôi không chắc bạn muốn làm gì với cột Sự kiện, nhưng nếu bạn cũng muốn giữ lại cột đó, thì sao

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

Ở đây chúng tôi sử dụng aveđể xem cột "Giá trị" cho mỗi "ID". Sau đó, chúng tôi xác định giá trị nào là giá trị lớn nhất và sau đó biến giá trị đó thành một vectơ logic mà chúng tôi có thể sử dụng để tập hợp con dữ liệu ban đầu.frame.


Cảm ơn rất nhiều nhưng tôi có một câu hỏi khác ở đây. Tại sao lại sử dụng với hàm trong phương thức này vì ave (Giá trị, ID, FUN = function (x) seq_along (x) == which.max (x)) == 1 hoạt động rất tốt? Tôi cảm thấy hơi khó hiểu.
Xinting WANG

Tôi đã sử dụng withvì hơi kỳ lạ khi có dữ liệu cả bên trong và bên ngoài groupdata.frame. Nếu bạn đọc dữ liệu bằng read.tablehoặc cái gì đó, bạn sẽ cần sử dụng withvì những tên cột đó sẽ không có sẵn bên ngoài data.frame.
MrFlick vào

6
do.call(rbind, lapply(split(group,as.factor(group$Subject)), function(x) {return(x[which.max(x$pt),])}))

Sử dụng cơ sở R


6

Vì {dplyr} v1.0.0 (tháng 5 năm 2020) có slice_*cú pháp mới thay thế top_n().

Xem thêm 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

Được tạo vào 2020-08-18 bởi gói reprex (v0.3.0.9001)

Thông tin phiên
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

5

Một giải pháp cơ bản khác

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

Sắp xếp thứ tự khung dữ liệu theo pt(giảm dần) và sau đó loại bỏ các hàng trùng lặp trongSubject


3

Thêm một giải pháp cơ bản R:

merge(aggregate(pt ~ Subject, max, data = group), group)

  Subject pt Event
1       1  5     2
2       2 17     2
3       3  5     2

2

Đây là một data.tablegiải pháp khác , vì which.maxkhông hoạt động trên các ký tự

library(data.table)
group <- data.table(Subject=ID, pt=Value, Event=Event)

group[, .SD[order(pt, decreasing = TRUE) == 1], by = Subject]

1

bylà một phiên bản của tapplykhung dữ liệu:

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

Nó trả về một đối tượng của lớp byđể chúng tôi chuyển đổi nó thành khung dữ liệu:

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

1

Trong cơ sở, bạn có thể sử dụng aveđể lấy maxmỗi nhóm và so sánh điều này với ptvà lấy một vectơ logic để tập hợp con 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

Hoặc so sánh nó đã có trong chức năng.

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

0

Một data.tablelựa chọn khác :

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

Hoặc khác (ít đọc hơn nhưng nhanh hơn một chút):

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

mã thời gian:

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

thời gian:

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

0

Một data.tablegiải pháp khác :

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

-1

Nếu bạn muốn giá trị pt lớn nhất cho một chủ đề, bạn có thể chỉ cần sử dụng:

   pt_max = as.data.frame(aggregate(pt~Subject, group, max))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.