W zbiorze danych z wieloma obserwacjami dla każdego przedmiotu chcę wziąć podzbiór zawierający tylko maksymalną wartość danych dla każdego rekordu. Na przykład z następującym zestawem danych:
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)
Temat 1, 2 i 3 mają największą wartość pkt odpowiednio 5, 17 i 5.
Jak mógłbym najpierw znaleźć największą wartość pt dla każdego tematu, a następnie umieścić tę obserwację w innej ramce danych? Wynikowa ramka danych powinna mieć tylko największe wartości pt dla każdego tematu.
Odpowiedzi:
Oto
data.table
rozwiązanie:require(data.table) ## 1.9.2 group <- as.data.table(group)
Jeśli chcesz zachować wszystkie wpisy odpowiadające maksymalnym wartościom
pt
w każdej grupie:group[group[, .I[pt == max(pt)], by=Subject]$V1] # Subject pt Event # 1: 1 5 2 # 2: 2 17 2 # 3: 3 5 2
Jeśli chcesz tylko pierwszą maksymalną wartość
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
W tym przypadku nie ma to znaczenia, ponieważ w żadnej grupie danych nie ma wielu wartości maksymalnych.
źródło
.SD
optymalizacja dla tych przypadków jest nadal na liście. Miej oko na # 735 .?`.I`
i sprawdź, czy wyjaśnienia i przykłady pomogą?Najbardziej intuicyjną metodą jest użycie funkcji group_by i top_n w programie dplyr
group %>% group_by(Subject) %>% top_n(1, pt)
Wynik, który otrzymasz, jest
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
źródło
group %>% group_by(Subject) %>% arrange(desc(pt), .by_group = TRUE) %>% summarise(max_pt=first(pt), min_pt=last(pt), Event=first(Event))
slice(which.max(pt))
do dołączania tylko jednego wiersza na grupę.Krótsze rozwiązanie wykorzystujące
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
źródło
group[group[, .I[which.max(pt)], by=Subject]$V1]
zaproponowane powyżej przez @Arun; zobacz porównania tutaj.I
wersjąInną opcją jest
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
źródło
dplyr
Rozwiązanie: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))
Daje to następującą ramkę danych:
Subject max.pt 1 1 5 2 2 17 3 3 5
źródło
Event
kolumnę w podzbiorze, w którym to przypadku możesz zrobić:df %>% group_by(Subject) %>% filter(pt == max(pt))
(obejmuje remisy, jeśli są obecne)Nie byłem pewien, co chcesz zrobić z kolumną Event, ale jeśli chcesz to również zachować, co powiesz na to
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
Tutaj używamy,
ave
aby spojrzeć na kolumnę „Wartość” dla każdego „ID”. Następnie określamy, która wartość jest maksymalna, a następnie przekształcamy ją w wektor logiczny, którego możemy użyć do podzbioru pierwotnego data.frame.źródło
with
ponieważ trochę dziwne jest mieć dane dostępne zarówno wewnątrz, jak i na zewnątrzgroup
ramki data.frame. Jeśli wczytujesz dane za pomocąread.table
lub czegoś, musisz użyć,with
ponieważ te nazwy kolumn nie byłyby dostępne poza data.frame.do.call(rbind, lapply(split(group,as.factor(group$Subject)), function(x) {return(x[which.max(x$pt),])}))
Korzystanie z Base
R
źródło
Od wersji {dplyr} 1.0.0 (maj 2020 r.) Obowiązuje nowa
slice_*
składnia, która zastępujetop_n()
.Zobacz także 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
Utworzono 18.08.2020 przez pakiet reprex (v0.3.0.9001)
Informacje o sesjisessioninfo::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
źródło
Kolejne rozwiązanie podstawowe
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
Uporządkuj ramkę danych
pt
(malejąco), a następnie usuń wiersze zduplikowane w programieSubject
źródło
Jeszcze jedno podstawowe rozwiązanie R:
merge(aggregate(pt ~ Subject, max, data = group), group) Subject pt Event 1 1 5 2 2 2 17 2 3 3 5 2
źródło
Oto inne
data.table
rozwiązanie, ponieważwhich.max
nie działa na postacilibrary(data.table) group <- data.table(Subject=ID, pt=Value, Event=Event) group[, .SD[order(pt, decreasing = TRUE) == 1], by = Subject]
źródło
by
jest wersjątapply
dla ramek danych:res <- by(group, group$Subject, FUN=function(df) df[which.max(df$pt),])
Zwraca obiekt klasy,
by
więc konwertujemy go na ramkę danych:do.call(rbind, b) Subject pt Event 1 1 5 2 2 2 17 2 3 3 5 2
źródło
W bazie możesz użyć,
ave
aby uzyskaćmax
na grupę i porównać to zpt
i uzyskać wektor logiczny do podzbiorudata.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
Lub porównaj to już w funkcji.
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
źródło
Inna
data.table
opcja:library(data.table) setDT(group) group[group[order(-pt), .I[1L], Subject]$V1]
Lub inny (mniej czytelny, ale nieco szybszy):
group[group[, rn := .I][order(Subject, -pt), { rn[c(1L, 1L + which(diff(Subject)>0L))] }]]
kod czasowy:
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
czasy:
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
źródło
Inne
data.table
rozwiązanie:library(data.table) setDT(group)[, head(.SD[order(-pt)], 1), by = .(Subject)]
źródło
Korzystając z dplyr 1.0.2, można to zrobić na dwa sposoby, jeden to long hand, a drugi to czasownik w poprzek ():
# 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)
Długą ręką czasownik to max (), ale zwróć uwagę na na.rm = TRUE, co jest przydatne w przykładach, w których istnieją NA, jak w zamkniętym pytaniu: Scal wiersze w ramce danych, w której wiersze są rozłączne i zawierają NA :
group %>% group_by(Subject) %>% summarise(pt = max(pt, na.rm = TRUE), Event = max(Event, na.rm = TRUE))
Jest to w porządku, jeśli jest tylko kilka kolumn, ale jeśli tabela ma wiele kolumn w poprzek (), jest przydatne. Przykłady tego czasownika często zawierają podsumowanie (w poprzek (początek_with ... ale w tym przykładzie kolumny nie zaczynają się tymi samymi znakami). Można je zmienić lub wymienić pozycje:
group %>% group_by(Subject) %>% summarise(across(1:ncol(group)-1, max, na.rm = TRUE, .names = "{.col}"))
Uwaga dotycząca czasownika w poprzek () 1 odnosi się do pierwszej kolumny po pierwszej rzeczywistej kolumnie, więc użycie ncol (grupa) nie zadziała, ponieważ jest to zbyt wiele kolumn (ustawia pozycję 4 zamiast 3).
źródło
Jeśli chcesz uzyskać największą wartość pt dla tematu, możesz po prostu użyć:
pt_max = as.data.frame(aggregate(pt~Subject, group, max))
źródło