按组选择DataFrame中的最高值

5
我有以下的df
dat <- data.frame(Cases = c("Student3","Student3","Student3","Student1","Student1",
"Student2","Student2","Student2","Student4"), Class = rep("Math", 9),
Scores = c(9,5,2,7,3,8,5,1,7), stringsAsFactors = F)


> dat
   Cases    Class   Scores
1 Student3  Math      9
2 Student3  Math      5
3 Student3  Math      2
4 Student1  Math      7
5 Student1  Math      3
6 Student2  Math      8
7 Student2  Math      5
8 Student2  Math      1
9 Student4  Math      7

另一方面,我有另一个包含以下信息的数据框df:
d <- data.frame(Cases = c("Student3", "Student1",
"Student2", "Student4"), Class = rep("Math", 4), stringsAsFactors = F)

    Cases  Class
1 Student3  Math
2 Student1  Math
3 Student2  Math
4 Student4  Math

我想提取每个学生的最高分数。因此,我的输出应该是这样的:

> dat_output
    Cases  Class   Scores
1 Student3  Math      9
2 Student1  Math      7
3 Student2  Math      8
4 Student4  Math      7

我试过使用merge,但它并没有仅提取最高的scores


2
可能是重复的问题,与https://dev59.com/dWAf5IYBdhLWcg3wgzDI相同。 - tjebo
2
请返回已翻译的文本:和 https://dev59.com/z14c5IYBdhLWcg3wyMwB#27534390 - tjebo
4
是的,有许多其他帖子都涉及“按组查找最大值”,但是这篇文章还有一个“筛选”步骤:仅对与d数据框中相同的学生子集执行此操作。 - zx8754
4
请仔细阅读问题,它并没有要求组内最大值。 - Ronak Shah
1
即使要查找的最大组列表来自不同的数据框,它仍然是一个“组中的最大值”问题,有许多重复项(仅使用当前标题搜索8+)。 “过滤”步骤没有反映在问题标题中,老实说,应该是一个单独的问题(也曾经被问过)。 - Mikko Marttila
显示剩余3条评论
7个回答

6
我们可以对d中的每个Cases使用sapply,对该Cases子集的dat进行处理,并获取其max分数。
sapply(d$Cases, function(x) max(dat$Scores[dat$Cases %in% x]))

#Student3 Student1 Student2 Student4 
#       9        7        8        7 

要将结果作为数据框返回
transform(d, Scores = sapply(d$Cases, function(x) 
                     max(dat$Scores[dat$Cases %in% x])))

#    Cases Class Scores
# Student3  Math      9 
# Student1  Math      7
# Student2  Math      8
# Student4  Math      7

注意 - 我假设你的 d
d <- data.frame(Cases = c("Student3", "Student1",
      "Student2", "Student4"), Class = rep("Math", 4), stringsAsFactors = F)

另外,tapply()也可以做到这一点。 - jogo
在这种情况下,我该如何将结果转换为“df”? - Cahidora
简单、快速,无需任何库...非常好用!谢谢。 - Cahidora
1
@Cahidora 抱歉,我的头脑有些混乱,我现在想不出比 cbind(d, t(sapply(d$Cases, function(x) { sub_df = dat[dat$Cases %in% x,]; inds = which.max(sub_df$Scores); c(sub_df$Scores[inds], sub_df$New[inds]) }))) 更好的解决方案了,其中 New 是您想要添加的附加列。 - Ronak Shah
@RonakShah非常适合添加列...你是个天才!非常感谢你! - Cahidora
显示剩余5条评论

3
如果我没记错,你不需要d,因为在d中没有比dat更多的信息。
你可以这样做:
dat_output <- aggregate(Scores ~ Cases, dat, max)
dat_output

     Cases Scores
1 Student1      7
2 Student2      8
3 Student3      9
4 Student4      7

1
d 中可能存在一些条目在 dat 中不存在。样本不一定代表实际数据。 - Ronak Shah

3

使用dplyr,并考虑您的d包含来自dat的学生子集的情况

library(dplyr)
inner_join(d, dat %>% group_by(Cases, Class) %>% summarize(Scores=max(Scores)))

# Cases Class Scores
#1 Student3  Math      9
#2 Student1  Math      7
#3 Student2  Math      8
#4 Student4  Math      7

如果顺序不重要,那么以下方法更加高效:
inner_join(dat, d) %>% group_by(Cases, Class) %>% summarize(Scores=max(Scores))
# A tibble: 4 x 3
# Groups:   Cases [?]
#  Cases    Class Scores
#  <chr>    <chr>  <dbl>
#1 Student1 Math       7
#2 Student2 Math       8
#3 Student3 Math       9
#4 Student4 Math       7

这是我更喜欢的答案。我建议先进行连接,然后将合并的数据传输到group_by等函数。 - zx8754
1
是的,但这将改变您最终输出中元素的顺序(顺序将与 d 不同),尽管从查询优化等方面来看它会更有效率。 - Sandipan Dey
好观点,我更喜欢效率而不是数据的排序方式。 - zx8754

3

您也可以使用sqldf包,操作如下:

sqldf("select max(Scores), Cases from dat JOIN d USING(Cases) group by Cases")

应用JOIN操作,group by casesselect max(Scores),Cases来获取所需的输出结果:

   max(Scores)    Cases
1           7    Student1
2           8    Student2
3           9    Student3
4           7    Student4

3
您可以使用 order 按照 Scores 的降序对数据框进行排序。然后删除重复的 Cases。这是一个基于R语言的解决方案。
dat <- dat[order(-dat$Scores),]
dat[duplicated(dat$Cases)==F,]

     Cases Class Scores
1 Student3  Math      9
6 Student2  Math      8
4 Student1  Math      7
9 Student4  Math      7

如果您想确保dat中的所有样本也在d中,您可以首先执行此操作。 %in%执行值匹配。但是,根据上面的例子,它不会有任何区别。
dat <- dat[dat$Cases %in% d$Cases & dat$Class %in% d$Class,]

1
使用 dplyr
df %>% 
  group_by(Cases, Class) %>% 
  summarise(Scores = max(Scores))

# A tibble: 4 x 3
# Groups:   Cases [?]
  Cases    Class Scores
  <chr>    <chr>  <dbl>
1 Student1 Math      7.
2 Student2 Math      8.
3 Student3 Math      9.
4 Student4 Math      7.

考虑你想要匹配这两个数据框:

df %>%  
  right_join(df2, by = c("Cases", "Class")) %>% 
  group_by(Cases, Class) %>% 
  summarise(Scores = max(Scores))

# A tibble: 4 x 3
# Groups:   Cases [?]
  Cases    Class Scores
  <chr>    <chr>  <dbl>
1 Student1 Math      7.
2 Student2 Math      8.
3 Student3 Math      9.
4 Student4 Math      7.

你还需要按类别分组,以防有多个类值。 - Sandipan Dey
经过编辑,现在这看起来像是@SandipanDey答案的副本。 - zx8754
我认为很明显我们正在使用(稍微)不同的方法。 - tmfmnk

1
使用dplyr,按学生分组,并根据分数获取第一个值:
library(dplyr)

dat %>% 
  filter(Cases %in% d$Cases) %>% 
  group_by(Cases) %>% 
  top_n(1, Scores) %>%
  ungroup()

# # A tibble: 4 x 3
#   Cases    Class Scores
#   <chr>    <chr>  <dbl>
# 1 Student1 Math       7
# 2 Student2 Math       8
# 3 Student3 Math       9
# 4 Student4 Math       7

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