r中数据框中每列不同行的最大值。

问题描述 投票:1回答:1

假设有一个有序的df,其中有一个ID列,其他列包含数字数据,按最后一列排序。

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

我们的目的是得到每个groupcolumn的最大值,与其ID,每对需要来自不同的行(唯一ID)。对于一个列的两个ID有相同的值,要选取Rating比较好的那个。

我所做的是使用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
r dataframe dplyr row multiple-columns
1个回答
3
投票

我看到有些解决方案不能处理重复的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

3
投票

这里有一个 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

2
投票

另一个 dplyrpurrr 解决方案,不如Allan的简洁。

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

0
投票

根据 本回答 并使用 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  
© www.soinside.com 2019 - 2024. All rights reserved.