如何使用dplyr选择每个组中具有最大值的行?

162

我希望用dplyr选择每个组中具有最大值的行。

首先,我生成了一些随机数据来说明我的问题。

set.seed(1)
df <- expand.grid(list(A = 1:5, B = 1:5, C = 1:5))
df$value <- runif(nrow(df))
在plyr中,我可以使用自定义函数来选择这一行。
library(plyr)
ddply(df, .(A, B), function(x) x[which.max(x$value),])

在dplyr中,我使用以下代码获取最大值,但不包括具有最大值的行(在本例中为C列)。

library(dplyr)
df %>% group_by(A, B) %>%
    summarise(max = max(value))

我该怎么实现这个?感谢任何建议。

sessionInfo()
R version 3.1.0 (2014-04-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)

locale:
[1] LC_COLLATE=English_Australia.1252  LC_CTYPE=English_Australia.1252   
[3] LC_MONETARY=English_Australia.1252 LC_NUMERIC=C                      
[5] LC_TIME=English_Australia.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] dplyr_0.2  plyr_1.8.1

loaded via a namespace (and not attached):
[1] assertthat_0.1.0.99 parallel_3.1.0      Rcpp_0.11.1        
[4] tools_3.1.0        
6个回答

186

试一试:

result <- df %>% 
             group_by(A, B) %>%
             filter(value == max(value)) %>%
             arrange(A,B,C)

看起来可以工作:

identical(
  as.data.frame(result),
  ddply(df, .(A, B), function(x) x[which.max(x$value),])
)
#[1] TRUE

正如评论中指出的那样,如果您严格要求每个组只返回1行,则根据下面@RoyalITS的回答,可能更倾向于使用slice。 如果存在多个具有相同最大值的行,则此答案将返回多个行。


19
在这种情况下,结果是相同的,因为不存在重复的最大值。否则,使用“filter”方法将返回每个组中所有最大值(行),而OP的ddply方法使用“which.max”仅返回每个组中的一个最大值(第一个)。要复制该行为,另一种选择是在dplyr中使用“slice(which.max(value))”。 - talat
1
根据@talat的评论,可以使用filter(row_number()==1)获得相同的结果。在分组之前对DF进行排序。 - Tiziano

107
df %>% group_by(A,B) %>% slice(which.max(value))

12
slice_max(.data, order_by, ..., n, prop, with_ties = TRUE)现在也是一个不错的选择... (说明:这句话是在讨论某个函数或选项的时候,提到了这个函数或选项也是一个值得考虑的选择) - Ben

86

你可以使用top_n

df %>% group_by(A, B) %>% top_n(n=1)

这将按最后一列(value)进行排名,并返回前 n=1 行。

目前,如果更改此默认值,则会导致错误(请参见 https://github.com/hadley/dplyr/issues/426 )。


10
请注意,领带会导致重复。 - Triamus
3
如果您的值不在最后一列,您可以使用 top_n 的 wt 参数。 df %>% group_by(A,B) %>% top_n(n=1, wt = value) - squiddle

18

这种更冗长的解决方案在出现重复的最大值时提供了更多控制(在本例中,它将随机选择其中一个对应的行)。

library(dplyr)
df %>% group_by(A, B) %>%
  mutate(the_rank  = rank(-value, ties.method = "random")) %>%
  filter(the_rank == 1) %>% select(-the_rank)

我认为这是一个很棒的方法,谢谢分享。 - Danny Bullis

2
更普遍地,我认为您可能希望获得在给定组内已经排序的行的“顶部”。
对于单个值被最大化的情况,您实际上只按一个列排序。但是,通常有用的是通过多个列进行按层次排序(例如:日期列和一列每天时间)。
# Answering the question of getting row with max "value".
df %>% 
  # Within each grouping of A and B values.
  group_by( A, B) %>% 
  # Sort rows in descending order by "value" column.
  arrange( desc(value) ) %>% 
  # Pick the top 1 value
  slice(1) %>% 
  # Remember to ungroup in case you want to do further work without grouping.
  ungroup()

# Answering an extension of the question of 
# getting row with the max value of the lowest "C".
df %>% 
  # Within each grouping of A and B values.
  group_by( A, B) %>% 
  # Sort rows in ascending order by C, and then within that by 
  # descending order by "value" column.
  arrange( C, desc(value) ) %>% 
  # Pick the one top row based on the sort
  slice(1) %>% 
  # Remember to ungroup in case you want to do further work without grouping.
  ungroup()

当您使用 arrange(desc(value)) 时,值是否被分组?我想在那里得到 C 的总和。 - Przemyslaw Remin
@PrzemyslawRemin,我不确定我完全理解“C的总和”在哪里或者你打算如何使用它。在dplyr中,将步骤分开是很好的。您可以使用df%>% group_by(A,B)%>% mutate(s = sum(C)),这将把每个组内的C的总和作为(重复)值s放在每个组内(每行在组内获得相同的总和值)。然后,您可以使用其中一种描述的其他方法来过滤C的最大值,例如...%>% ungroup()%>% slice(which.max(C)) - Kalin

1

对我来说,计算每个组的值的数量很有帮助。将计数表复制到一个新对象中。然后根据第一个分组特征过滤组的最大值。例如:

count_table  <- df %>%
                group_by(A, B) %>%
                count() %>%
                arrange(A, desc(n))

count_table %>% 
    group_by(A) %>%
    filter(n == max(n))

或者

count_table %>% 
    group_by(A) %>%
    top_n(1, n)

3
如果你在问题的数据上运行这个程序,我认为你会发现它回答的不是正确的问题。帖子中的问题是如何找到具有最大值(即“value”列中的数字)的行。这个答案忽略了"value"列,而是找到了每个“A”中最常见的“B”值。 - Gregor Thomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接