R 选择每组中具有最大值的行

R 选择每组中具有最大值的行,r,dataframe,r-faq,R,Dataframe,R Faq,在一个数据集中,每个主题都有多个观察结果。对于每个主题,我想选择最大值为“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

在一个数据集中,每个主题都有多个观察结果。对于每个主题,我想选择最大值为“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

ID我不确定您想对事件列做什么,但是如果您也想保留它,那么

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

isIDmaxA
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))
require(data.table) ## 1.9.2
group <- as.data.table(group)
merge(aggregate(pt ~ Subject, max, data = group), group)

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

下面是一个
数据表
解决方案:

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))
require(data.table) ## 1.9.2
group <- as.data.table(group)
merge(aggregate(pt ~ Subject, max, data = group), group)

  Subject pt Event
1       1  5     2
2       2 17     2
3       3  5     2
library(data.table)
setDT(group)[, head(.SD[order(-pt)], 1), by = .(Subject)]
如果您只需要
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

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

使用
数据的较短解决方案。表

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

最直观的方法是在dplyr中使用group_by和top_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

另一个选项是
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
库(dplyr)
组%>%
分组依据(受试者)%>%
切片(哪个最大值(pt))
#主题pt事件
#      
#1       1     5     2
#2       2    17     2
#3       3     5     2

使用Base
R

如果您想要受试者的最大pt值,只需使用:

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

下面是另一个
data.table
解决方案,因为
哪个.max
对字符不起作用

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

group[, .SD[order(pt, decreasing = TRUE) == 1], by = Subject]
库(data.table)
组另一基础溶液

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

group\u排序一个以上的基本R解决方案:

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))
require(data.table) ## 1.9.2
group <- as.data.table(group)
merge(aggregate(pt ~ Subject, max, data = group), group)

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

另一个
数据表
选项:

library(data.table)
setDT(group)
group[group[order(-pt), .I[1L], Subject]$V1]
或其他(可读性较差但速度稍快):

定时代码:

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

另一个
数据表
解决方案:

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))
require(data.table) ## 1.9.2
group <- as.data.table(group)
merge(aggregate(pt ~ Subject, max, data = group), group)

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

by
是数据帧的
tapply
版本:

res <- by(group, group$Subject, FUN=function(df) df[which.max(df$pt),])
在base中,您可以使用
ave
来获得每组的
max
,并将其与
pt
进行比较,并获得一个逻辑向量来子集
数据。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
自{dplyr}v1.0.0(2020年5月)以来,新的
slice.*
语法取代了
top\n()

另见


使用dplyr 1.0.2现在有两种方法,一种是long hand,另一种是使用动词Cross():

如果只有几列,这是可以的,但是如果表中跨多个列,则()非常有用。此动词的示例通常带有summary(over)(start_with…)但在本例中,列的开头字符不同。它们可以更改,也可以列出位置:

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

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

我认为OP希望将
事件
列保留在子集中,在这种情况下,您可以这样做:
df%>%group\u by(Subject)%%>%filter(pt==max(pt))
(包括领带,如果有的话)非常感谢,但我这里还有另一个问题。既然ave(Value,ID,FUN=函数(x)seq_along(x)=which.max(x)),为什么在这个方法中使用with function==1非常好用?我有点困惑。我使用了
,因为在
组的内部和外部都有可用的数据是有点奇怪的
data.frame。如果你用
read.table
或其他什么东西读取数据,你需要使用
,因为这些列名在我之外是不可用的取消data.frame.saw as data.table自2014年以来发生了很多变化,这仍然是这个问题最快/最好的解决方案吗?@Ben,在这种情况下,最快的答案仍然是这个,是的。
.SD
这些情况的优化仍然在列表中。注意。嗨,这里的$V1是什么?\nooba访问自动命名列。在没有它的情况下运行它为了更好地理解。@HappyCoding,请看一看
?`.I`
,看看其中的解释和示例是否有帮助?这是非常密切相关的,但与最小值相关,而不是与最大值相关:当您要访问组中的最小值和最大值时,dplyr也很有用,因为这些值可以作为数组使用。因此,您可以首先进行排序通过pt降序,然后使用pt[1]或first(pt)获得最高值:
group%>%group\u by(Subject)%%>%arrange(desc(pt),.by\u group=TRUE)%%>%summary(max\u pt=first(pt),min\u pt=last(pt),Event=first(Event))
如果有联系,这将包括多行。使用
slice(which.max(pt))
每个组只包含一行。请注意,这可能比@Arun建议的
组[group[,.I[which.max(pt)],by=Subject]$V1]要慢。
请参见上面的比较我喜欢这一行,因为它对于我当前的上下文来说足够快,而且与
.I
versionsetDT(group)[,.SD[pt==max(pt)]相比,我更容易摸索,by=主题]