假设有一个带有 ID 列的有序 df,而其他包含数字数据的列按最后一列排序。
ID <- c(123, 142, 21, 562, 36, 721, 847, 321)
A <- c(96, 83, 73, 47, 88, 65, 72, 67)
B <- c(72, 69, 88, 75, 63, 89, 48, 80)
C <- c(95, 94, 94, 94, 65, 81, 75, 75)
D <- c(63, 88, 89, 88, 89, 79, 88, 79)
Rating <- c(97, 95, 92, 87, 85, 83, 79, 77)
df <- data.frame(ID, A, B, C, D, Rating)
df
# ID A B C D Rating
#1 123 96 72 95 63 97
#2 142 83 69 94 88 95
#3 21 73 88 94 89 92
#4 562 47 75 94 88 87
#5 36 88 63 65 89 85
#6 721 65 89 81 79 83
#7 847 72 48 75 88 79
#8 321 67 80 75 79 77
目的是获取每个组/列的最大值及其 ID,并且每对都需要来自不同的行(唯一 ID(。对于列值相同的两个 ID,请选择评级较高的 ID。
我所做的是使用 apply(( 函数从每列中获取最大值,提取具有该值的 ID,并将它们全部连接到数据框中。因为我仍然缺少第 4 列的 ID,所以我使用反连接来取出以前的 ID,并重复该过程以获取此数据框:
my_max <- data.frame(apply(df, 2, max))
A2 <- df[which(df$A == my_max[2,1]),]%>% dplyr::select(ID, A)
B2 <- df[which(df$B == my_max[3,1]),]%>% dplyr::select(ID, B)
C2 <- df[which(df$C == my_max[4,1]),]%>% dplyr::select(ID, C)
D2 <- df[which(df$D == my_max[5,1]),]%>% dplyr::select(ID, D)
all <- full_join(A2, B2, by='ID') %>% full_join(C2, by='ID') %>% full_join(D2, by='ID')
all <- all[-c(4),]
df <- anti_join(df, all, by='ID')
my_max <- data.frame(apply(df, 2, max))
C2 <- df[which(df$C == my_max[4,1]),]%>% dplyr::select(ID, C)
all <- all %>% full_join(C2, by='ID')
all <- all[-c(5),-c(4)]
最后给我:
all
# ID A B D C.y
#1 123 96 NA NA NA
#2 721 NA 89 NA NA
#3 21 NA NA 89 NA
#4 142 NA NA NA 94
有没有更干净或简洁/有效的方法来做到这一点?不一定是相同的方式,也许只是 ID 和角色,例如:
# ID Group
#1 123 A
#2 721 B
#3 142 C
#4 21 D
我看到某些解决方案无法处理重复的ID。例如,我们组 A 和 C 的 ID 均为 123。
要获得与问题中的最终结果类似的输出,处理重复 ID 的另一种解决方案如下
# initialization
variables <- c("A", "B", "C", "D")
df_max <- data.frame(ID = numeric(length(variables)), Group = variables)
for(column in variables){
temp_id <- df %>%
filter(!(ID %in% df_max$ID)) %>%
arrange(desc(!!rlang::sym(column)), desc(Rating)) %>%
slice(1) %>%
select(ID) %>%
as.numeric(ID)
df_max[df_max$Group == column, "ID"] <- temp_id
}
基本上,filter
步骤确保我们不考虑已经选择的ID。
输出
# > df_max
#
# ID Group
# 1 123 A
# 2 721 B
# 3 142 C
# 4 21 D
这是一个dplyr
的解决方案,可以处理重复的 ID。首先,我们pivot_longer
将所有字母放在一列中。然后我们group_by
这些信件。最后,在每个字母中,我们按值(和值中的联系评级(排序,并选择第一个元素以获取每个 ID。
library(dplyr)
df %>%
pivot_longer(cols = c("A", "B", "C", "D")) %>%
group_by(Group = name) %>%
summarise(ID = ID[order(-value, -Rating)[1]])
#> # A tibble: 4 x 2
#> Group ID
#> <chr> <dbl>
#> 1 A 123
#> 2 B 721
#> 3 C 123
#> 4 D 21
另一个dplyr
/purrr
的解决方案,不如艾伦的简洁。
find_max <- function(gg){
tibble(
group=gg,
ID= df %>% select(all_of(c(gg,"Rating","ID"))) %>%
arrange_all(desc) %>% slice(1) %>% pull(ID))
}
c("A","B","C","D") %>% map_dfr(find_max)
这个想法是使用dplyr::arrange
按组和Rating
(降序(对数据框进行排序,然后保留第一行(最大值(的ID
。迭代是使用purrr::map_dfr
进行的,它直接产生 tibble。
输出为:
# A tibble: 4 x 2
group ID
<chr> <dbl>
1 A 123
2 B 721
3 C 123
4 D 21
基于这个答案并使用dplyr
:
df %>%
group_by(ID) %>%
mutate(max.val = pmax(A, B, C, D)[which.max(Rating)]) %>%
summarise_each(list(max)) %>%
mutate(top.col=apply(.[,2:5], 1, function(x) names(x)[which.max(x)])) %>%
select(-c(A, B, C, D, Rating))
你得到
# A tibble: 8 x 3
ID max.val top.col
<dbl> <dbl> <chr>
1 21 94 C
2 36 89 D
3 123 96 A
4 142 94 C
5 321 80 B
6 562 94 C
7 721 89 B
8 847 88 D