提问人:Xinting WANG 提问时间:7/3/2014 最后编辑:HenrikXinting WANG 更新时间:4/24/2023 访问量:190845
选择每个组中具有最大值的行
Select the row with the maximum value in each group
问:
在每个主题具有多个观测值的数据集中。对于每个主题,我想选择最大值为“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 值。
答:
我不确定你想对事件列做什么,但如果你也想保留它,怎么样
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
在这里,我们习惯于查看每个“ID”的“值”列。然后,我们确定哪个值是最大值,然后将其转换为可用于子集原始 data.frame 的逻辑向量。ave
评论
with
group
read.table
with
解决方案: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
评论
Event
df %>% group_by(Subject) %>% filter(pt == max(pt))
这是一个解决方案: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
在这种情况下,这不会有什么区别,因为数据中的任何组中都没有多个最大值。
评论
?`.I`
使用以下更短的解决方案: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
评论
group[group[, .I[which.max(pt)], by=Subject]$V1]
.I
setDT(group)[, .SD[which.max(pt)][1], by=Subject]
最直观的方法就是在group_by
top_n
dplyr
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
评论
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))
另一种选择是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
使用 1.1.0dplyr
slice_max(group, pt, by = 'Subject')
do.call(rbind, lapply(split(group,as.factor(group$Subject)), function(x) {return(x[which.max(x$pt),])}))
使用底座R
如果您想要主题的最大 pt 值,您可以简单地使用:
pt_max = as.data.frame(aggregate(pt~Subject, group, max))
这是另一种解决方案,因为不适用于角色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]
另一个基本解决方案
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
另一个基本 R 解决方案:
merge(aggregate(pt ~ Subject, max, data = group), group)
Subject pt Event
1 1 5 2
2 2 17 2
3 3 5 2
另一种选择: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
另一种解决方案:data.table
library(data.table)
setDT(group)[, head(.SD[order(-pt)], 1), by = .(Subject)]
by
是 for data frames 的一个版本:tapply
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
在 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
或者另外使用.with
group[with(group, pt == ave(pt, Subject, FUN=max)),]
或者已经在函数中进行比较。
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
从 {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 软件包 (v0.3.0.9001) 于 2020-08-18 创建
使用 dplyr 1.0.2 现在有两种方法可以做到这一点,一种是长手,另一种是使用动词 across():
# 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 的示例很有用,如封闭式问题:合并数据帧中的行,其中行不相交且包含 NA:
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}"))
请注意,动词 across() 1 是指第一个实际列之后的第一列,因此使用 ncol(group) 不起作用,因为列太多(使其位置为 4 而不是 3)。
我有时会依靠围绕函数的解决方案,因为这适用于任何类型的列(即不仅仅是数字)。基本上,按您想要的最大值或最小值对表格进行排序,然后取每组的第一组或最后一组。order
我还发现这对于初学者来说相当容易阅读。data.table
library(data.table)
setDT(group)[order(pt)][, last(.SD), by = Subject]
请注意,替换为可实现最小值的等效值。last
first
使用 ,您可以使用 with 来执行内联分组切片:dplyr 1.1.0
slice_max
by
library(dplyr)
group %>%
slice_max(pt, n = 1, by = Subject)
# Subject pt Event
#1 1 5 2
#2 2 17 2
#3 3 5 2
上一个:在 dplyr 函数中使用变量名
评论