在我的数据框中,有多个学生分数的列。我想要对"Quiz"列求和(例如:Quiz1、Quiz2)。但是,我只想要对最高的两个值求和,并忽略其他的值。我想要创建一个新的列,用于存储总和(即最高的两个值之和)。
一个问题是,在某些行中,有些学生的成绩并列为前两名。例如,Aaron获得了42分的高分,但是接下来有两个分数并列获得第二高分(即36分)。
数据
df <-
structure(
list(
Student = c("Aaron", "James", "Charlotte", "Katie", "Olivia",
"Timothy", "Grant", "Chloe", "Judy", "Justin"),
ID = c(30016, 87311, 61755, 55323, 94839, 38209, 34096,
98432, 19487, 94029),
Quiz1 = c(31, 25, 41, 10, 35, 19, 27, 42, 15, 20),
Quiz2 = c(42, 33, 34, 22, 23, 38, 48, 49, 23, 30),
Quiz3 = c(36, 36, 34, 32, 43, 38, 44, 42, 42, 37),
Quiz4 = c(36, 43, 39, 46, 40, 38, 43, 35, 41, 41)
),
row.names = c(NA, -10L),
class = c("tbl_df", "tbl", "data.frame")
)
我知道可以使用
pivot_longer
来实现这一点,它允许我按组排列,然后为每个学生选择前两个值。这个方法可以正常工作,但是我希望能够在tidyverse
中找到更有效的方法,而不必来回旋转数据。
我尝试了什么
library(tidyverse)
df %>%
pivot_longer(-c(Student, ID)) %>%
group_by(Student, ID) %>%
arrange(desc(value), .by_group = TRUE) %>%
slice_head(n = 2) %>%
pivot_wider(names_from = name, values_from = value) %>%
ungroup() %>%
mutate(Total = rowSums(select(., starts_with("Quiz")), na.rm = TRUE))
我还知道,如果我想要在每一行上将所有列加起来,那么我可以像上面使用 rowSums
。然而,我不确定如何对 4 个小测验列中仅顶部 2 个值使用 rowSums
。
期望输出
# A tibble: 10 × 7
Student ID Quiz2 Quiz3 Quiz1 Quiz4 Total
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Aaron 30016 42 36 NA NA 78
2 Charlotte 61755 NA NA 41 39 80
3 Chloe 98432 49 NA 42 NA 91
4 Grant 34096 48 44 NA NA 92
5 James 87311 NA 36 NA 43 79
6 Judy 19487 NA 42 NA 41 83
7 Justin 94029 NA 37 NA 41 78
8 Katie 55323 NA 32 NA 46 78
9 Olivia 94839 NA 43 NA 40 83
10 Timothy 38209 38 38 NA NA 76
collapse
没有问题,那么一种快速的逐行选项是ftransform(gvr(df, "Student|ID"), dapply(gvr(df, "^Quiz"), MARGIN = 1, FUN = function(x) replace(x, radixorder(radixorder(x)) %in% 1:2, NA))) %>% ftransform(Total = rowSums(gvr(., "^Quiz"), na.rm = TRUE))
。 - akrun