代码之家  ›  专栏  ›  技术社区  ›  lydias

R基于前面列的比较生成值

  •  2
  • lydias  · 技术社区  · 7 年前

    我希望生成一个列(Min),找到所选名称列的最小值,并提取列的名称作为其值。以下是示例数据帧:

              Amy  Abe  Donna  Racheal  Mike     Min       u
              5    34    54     56       23      Amy       0
              43   11    3      33       21      Donna     1
              54   32    21     54       1       Mike      1 
              21   5     43     32       21      Abe       1
              32   21    23     5        32      Racheal   0
              43   2     2      13       45      Abe Donna 1
                                .
                                .
                                .
    

    列u只是数据集末尾的一列。数据集相当大,因此我正在尝试找到一种有效的方法来生成列Min。

    我心中的准则是:

         MinData <- Data %>% mutate(Min = 
         min(colnames(Data)[1:5]))
    

    这只提取列的名称。我应该添加什么以使列能够比较每行中的值并选择具有最小值的列名?

    3 回复  |  直到 7 年前
        1
  •  3
  •   neilfws    7 年前

    您的原始数据:

    df1 <- structure(list(Amy = c(5L, 43L, 54L, 21L, 32L, 43L), 
                          Abe = c(34L, 11L, 32L, 5L, 21L, 2L), 
                          Donna = c(54L, 3L, 21L, 43L, 23L, 2L), 
                          Racheal = c(56L, 33L, 54L, 32L, 5L, 13L), 
                          Mike = c(23L, 21L, 1L, 21L, 32L, 45L), 
                          u = c(0, 1, 1, 1, 0, 1)), 
                          row.names = c(NA, -6L), 
                          class = "data.frame")
    

    我们可以使用 tidyr dplyr 要从宽到长进行转换,请进行计算和聚合,然后在最后将所有内容粘在一起。

    library(dplyr)
    library(tidyr)
    
    df1 %>% 
      gather(name, value, -u) %>%                      # convert from wide to long
      group_by(name) %>% 
      mutate(idx = row_number()) %>%                   # add a grouping variable
      ungroup() %>% 
      group_by(idx) %>% 
      mutate(Min = min(value)) %>%                     # calculate min per group (= per row)
      filter(value == Min) %>%                         # keep names with value = Min
      arrange(idx) %>%                                 # order rows as original data
      select(idx, Min = name) %>% 
      summarise(Min = paste(Min, collapse = ",")) %>%  # combine names where Min tied
      ungroup() %>% 
      select(Min) %>% 
      bind_cols(df1, .)                                # combine column Min (names) with 
                                                       # original data
    
      Amy Abe Donna Racheal Mike u       Min
    1   5  34    54      56   23 0       Amy
    2  43  11     3      33   21 1     Donna
    3  54  32    21      54    1 1      Mike
    4  21   5    43      32   21 1       Abe
    5  32  21    23       5   32 0   Racheal
    6  43   2     2      13   45 1 Abe,Donna
    
        2
  •  2
  •   Melissa Key    7 年前

    以下是我的做法:

    library(tidyverse) # we use dplyr and tidyr
    Data <- Data %>% 
      mutate(row = 1:length(u)) 
    
    MinData <- Data %>% 
      gather(name, score, -u, -row, -Min) %>% 
      group_by(row) %>%
      summarize(Min2 = paste(name[score == min(score)], collapse = " ")) %>% # called "Min2" to differentiate it from the "Min" column provided in the example.
      left_join(df %>% mutate(row = 1:length(u)), .)
    
        3
  •  1
  •   LachlanO    7 年前

    我会使用apply函数,其中:)

    设置名称向量

    person_names= names(df[,1:5]) #Presumably the column names are the names
    

    这个 1:5 如果您的数据集中有其他列,您不希望将其考虑为最小检查,则可以使用。

    现在,我们可以在自定义函数上使用apply,该函数从每行值最小的列中返回名称。

    df$Min <- apply(df[,1:5], 1, function(x){person_names[which.min(x)]})
    

    我们的自定义函数如前所述,apply只是将该函数应用于数据帧或矩阵的每一列或每一行。第二个参数 1 指示行,如果需要列,可以将其更改为 2

    which.min 只返回最小值所在的元素数。 person_names 把我们的名字整理好 哪一个最小值 返回一个数字,该数字指示哪个名称的值最小。

    如果您想消除 person\u姓名 变量

    df$Min <- apply(df[,1:5], 1, function(x){names(df[,1:5])[which.min(x)]})
    

    如果只有5个名称列,请删除 1: 5 ,如果在任何位置都有列,只需将其替换为列名或数字的向量即可。

    编辑: 我看到你对另一个答案的评论。为了适应关系,我将更改自定义函数,以便它检查最小值为x的所有匹配项,然后使用一些自定义分隔符将它们粘贴在一起。我还将修改您的数据,以便Donna和Racheal并列第二排。

    df <- read.table(text = 'Amy  Abe  Donna  Racheal  Mike     Min       u
          5    34    54     56       23      Amy       0
           43   11    3      3       21      Donna     1
           54   32    21     54       1       Mike      1 
           21   5     43     32       21      Abe       1
           32   21    23     5        32      Racheal   0', header = T)
    
    person_names <- names(df[,1:5])
    
    df$Min <- apply(df[,1:5], 1, function(x){paste(person_names[x == min(x)], 
    collapse = ", ")})
    
    > df
      Amy Abe Donna Racheal Mike            Min u
    1   5  34    54      56   23            Amy 0
    2  43  11     3       3   21 Donna, Racheal 1
    3  54  32    21      54    1           Mike 1
    4  21   5    43      32   21            Abe 1
    5  32  21    23       5   32        Racheal 0
    

    我已经设置了 collapse “,”的参数,这是我任意选择的分隔符。您可以将其调整为一个空格“”,或分号,或任何您想要的。

    同样,可以通过去掉 person\u姓名