【问题标题】:Max value of for each column for distinct rows of data frame in rr 中不同数据帧行的每列的最大值
【发布时间】:2020-09-21 04:51:24
【问题描述】:

假设有一个有序的 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

目的是获取每个组/列的最大值及其 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

【问题讨论】:

  • 您可以简化将所有最大值放入一个衬里 apply(df[,2:5],2,function(x) df$ID[which.max(x)]) 但您仍然会遇到 ID 重复的问题。
  • 我不明白你的问题。您说您想要“每个组/列的最大值及其 ID,并且每一对都需要来自不同的行(唯一 ID)”。因此,对于 A 列,最大值为 96,ID 为 123,但对于 C 列,最大值再次为 ID 123。什么决定了应将 ID 123 分配给 A 还是 C?你能澄清你想要什么吗?这建议您首先按行获取最大组值,但在这种情况下,应将 ID 23 分配给 C,因为这是该行的最大值..?
  • 我们希望最大化它们的值的总和,因此 A+B+C+D 将是最大的值(对于每个不同的 ID)。所以我选择 96 作为 A 的 ID,因为它大于 C 的值 - 95。
  • 如果除第一行以外的所有行的C值都非常低,您是否仍宁愿选择A作为第一行而不是C?换句话说,您是否尝试在选择一组唯一 ID 时最大化 A - D 值的总和?

标签: r dataframe dplyr row multiple-columns


【解决方案1】:

这是一个处理重复 ID 的dplyr 解决方案。首先,我们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】:

    另一个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 进行迭代,直接产生一个小标题。

    输出是:

    # A tibble: 4 x 2
      group    ID
      <chr> <dbl>
    1 A       123
    2 B       721
    3 C       123
    4 D        21
    

    【讨论】:

    • 我不确定您的输出 Roland,然后我重新阅读了 OP 的问题并意识到我按错误的变量分组。也喜欢你的回答 +1
    【解决方案3】:

    基于this answer并使用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  
    

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2015-11-28
        • 2020-05-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-13
        • 2021-10-24
        • 2018-08-18
        • 2020-12-23
        相关资源
        最近更新 更多