En un conjunto de datos con múltiples observaciones para cada sujeto, quiero tomar un subconjunto con solo el valor máximo de datos para cada registro. Por ejemplo, con el siguiente conjunto de datos:
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)
Los sujetos 1, 2 y 3 tienen el mayor valor de pt de 5, 17 y 5 respectivamente.
¿Cómo podría encontrar primero el valor de pt más grande para cada tema y luego poner esta observación en otro marco de datos? El marco de datos resultante solo debe tener los valores de pt más grandes para cada sujeto.

Respuestas:
He aquí una
data.tablesolución:require(data.table) ## 1.9.2 group <- as.data.table(group)Si desea mantener todas las entradas correspondientes a los valores máximos de
ptcada grupo:group[group[, .I[pt == max(pt)], by=Subject]$V1] # Subject pt Event # 1: 1 5 2 # 2: 2 17 2 # 3: 3 5 2Si solo desea el primer valor máximo de
pt:group[group[, .I[which.max(pt)], by=Subject]$V1] # Subject pt Event # 1: 1 5 2 # 2: 2 17 2 # 3: 3 5 2En este caso, no hace ninguna diferencia, ya que no hay varios valores máximos dentro de ningún grupo en sus datos.
fuente
.SDla optimización para estos casos todavía está en la lista. Eche un vistazo al # 735 .?`.I`y vea si la explicación y los ejemplos allí ayudan.El método más intuitivo es utilizar la función group_by y top_n en dplyr
group %>% group_by(Subject) %>% top_n(1, pt)El resultado que obtienes es
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 2fuente
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))para incluir solo una fila por grupo.Una solución más corta usando
data.table:setDT(group)[, .SD[which.max(pt)], by=Subject] # Subject pt Event # 1: 1 5 2 # 2: 2 17 2 # 3: 3 5 2fuente
group[group[, .I[which.max(pt)], by=Subject]$V1]propuesto anteriormente por @Arun; ver comparaciones aquí.IversiónOtra opcion es
slicelibrary(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 2fuente
Una
dplyrsolucion: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))Esto produce el siguiente marco de datos:
Subject max.pt 1 1 5 2 2 17 3 3 5fuente
Eventcolumna en el subconjunto, en cuyo caso podría hacer:df %>% group_by(Subject) %>% filter(pt == max(pt))(incluye empates si está presente)No estaba seguro de lo que querías hacer con la columna Evento, pero si también quieres conservarla, ¿qué tal si
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 2Aquí usamos
avepara mirar la columna "Valor" para cada "ID". Luego determinamos qué valor es el máximo y luego lo convertimos en un vector lógico que podemos usar para crear subconjuntos de los datos originales.fuente
withporque es un poco extraño tener los datos disponibles tanto dentro como fuera delgroupdata.frame. Si lee los datos conread.tableo algo así, deberá usarloswithporque esos nombres de columna no estarán disponibles fuera del data.frame.do.call(rbind, lapply(split(group,as.factor(group$Subject)), function(x) {return(x[which.max(x$pt),])}))Usando Base
Rfuente
Desde {dplyr} v1.0.0 (mayo de 2020) existe una nueva
slice_*sintaxis que reemplazatop_n().Consulte tambié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 2Creado el 2020-08-18 por el paquete reprex (v0.3.0.9001)
Información de la sesiónsessioninfo::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/libraryfuente
Otra solución base
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 2Ordene el marco de datos por
pt(descendente) y luego elimine las filas duplicadas enSubjectfuente
Una solución de R base más:
merge(aggregate(pt ~ Subject, max, data = group), group) Subject pt Event 1 1 5 2 2 2 17 2 3 3 5 2fuente
Aquí hay otra
data.tablesolución, yawhich.maxque no funciona con personajes.library(data.table) group <- data.table(Subject=ID, pt=Value, Event=Event) group[, .SD[order(pt, decreasing = TRUE) == 1], by = Subject]fuente
byes una versión detapplypara marcos de datos:res <- by(group, group$Subject, FUN=function(df) df[which.max(df$pt),])Devuelve un objeto de clase,
bypor lo que lo convertimos en un marco de datos:do.call(rbind, b) Subject pt Event 1 1 5 2 2 2 17 2 3 3 5 2fuente
En base , puede usar
avepara obtenermaxpor grupo y comparar esto conpty obtener un vector lógico para subconjuntar eldata.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 2O compárelo ya en la función.
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 2fuente
Otra
data.tableopcion:library(data.table) setDT(group) group[group[order(-pt), .I[1L], Subject]$V1]U otro (menos legible pero un poco más rápido):
group[group[, rn := .I][order(Subject, -pt), { rn[c(1L, 1L + which(diff(Subject)>0L))] }]]código de tiempo:
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] TRUEtiempos:
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 3fuente
Otra
data.tablesolucion:library(data.table) setDT(group)[, head(.SD[order(-pt)], 1), by = .(Subject)]fuente
Si desea el mayor valor de pt para un tema, simplemente puede usar:
fuente