R根据先前列的比较生成值

R Generating values based on comparison of previous columns

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

          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]))

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

以下是我的处理方法:

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)), .)

我会使用 apply 函数:)

设置我们的名称向量

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

1:5 就在那里,以防您的数据集中有其他列您不想考虑进行最小检查。

现在我们可以在自定义函数上使用 apply,该函数 return 每一行中具有最低值的列中的名称。

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

我们的自定义函数正如我已经描述的那样,apply 只是将函数应用于数据框或矩阵的每一列或每一行。第二个参数 1 表示行,如果我们想要列,我们可以将其更改为 2.

which.min 只是 returns 最小值所在的元素编号。 person_names 按顺序排列我们的名字,which.min return 是一个数字,表示哪个名字的值最小。

如果你想取消 person_names 变量,你可以将这一切压缩成一个单行解决方案。

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 参数设置为“,”,这是我任意选择的分隔符。您可以将其调整为 space " ",或分号,或您想要的任何内容。

同样,通过删除 person_names.

的单独行,可以将其压缩为一行答案

您的原始数据:

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")

我们可以使用 tidyrdplyr 从宽转换为长,进行计算和聚合,然后在最后将它们重新组合在一起。

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