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

时间:2018-05-10 04:28:01

标签: r dplyr tidyverse data-cleaning

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

m = dict(zip([False, True], 'tf'))
f = lambda d: d.sort_values('diff').duplicated('pic_code').map(m)
df.eval('diff=abs(weight - mpe_wgt)').assign(keep=f)

   pic_code  weight  mpe_wgt keep  diff
0      1234      45       34    f  11.0
1      1234      32       23    t   9.0
2     45344      54       35    t  19.0
3       234      76       98    t  22.0
4       234      65       12    f  53.0

列u只是位于数据集末尾的列。数据集相当大,因此我试图找到一种有效的方法来生成列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
                            .
                            .
                            .

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

3 个答案:

答案 0 :(得分:3)

您的原始数据:

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

答案 1 :(得分:2)

以下是我如何做到这一点:

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

答案 2 :(得分:1)

我使用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)]})

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

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

如果您想取消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参数设置为&#34;,&#34;,这是我任意选择的分隔符。你可以调整这个只是一个空间&#34; &#34;,或分号,或任何你想要的。

同样,可以通过删除person_names的单独行来将其压缩为一行答案。