【问题标题】:Check condition and return name of column for which the condition is fulfilled检查条件并返回满足条件的列的名称
【发布时间】:2018-10-23 21:06:07
【问题描述】:

我有一个如下所示的数据框

df_start <- data.frame(
  a = c(1, 1, 1, 1, 1), 
  b = c(0, 1, 0, 0, 0), 
  c = c(1, 0, 0, 0, 0), 
  n = c(0, 0, 0, 1, 0))

如果df_start[,2:n] 中的任何列(其中 n 显示数据帧的最后一列)等于 df$a,我想测试条件,然后创建两个新列,其中第一个列返回 1,如果条件为 TRUE,否则为 0,另一个给出条件为 TRUE 的列的名称。

我设法创建了这样的第一列:

library(dplyr)

# check condition
df_start <- df_start %>% mutate(cond = ifelse(a == b | a == c | a == n, 1, 0))

尽管我认为我需要一种不同的方法,因为我可能每次都有不同数量的列。所以我需要测试a 列以及从第 2 列到最后一列的所有列的条件,但我还需要知道满足哪个列的条件。

期望的输出:

# desired output
df_end <- data.frame(a = c(1, 1, 1, 1, 1), 
                     b = c(0, 1, 0, 0, 0), 
                     c = c(1, 0, 0, 0, 0), 
                     n = c(0, 0, 0, 1, 0),
                     cond = c(1,1,0,1,0),
                     col_name = c("c", "b", NA, "n", NA))

有没有办法用 dplyr 或 base R 来做到这一点?尽管赞赏任何其他解决方案。

【问题讨论】:

    标签: r filter dplyr


    【解决方案1】:

    以下仅使用基础 R。
    请注意,要创建列cond,不需要ifelse

    df_end <- df_start
    df_end$cond <- with(df_start, as.integer(a == b | a == c | a == n))
    df_end$col_name <- NA
    inx <- apply(df_start[-1] == df_start[[1]], 1, function(x) min(which(x)) + 1)
    is.na(inx) <- is.infinite(inx)
    df_end$col_name <- names(df_start)[inx]
    
    df_end
    #  a b c n cond col_name
    #1 1 0 1 0    1        c
    #2 1 1 0 0    1        b
    #3 1 0 0 0    0     <NA>
    #4 1 0 0 1    1        n
    #5 1 0 0 0    0     <NA>
    

    【讨论】:

      【解决方案2】:

      您可以尝试tidyverse。我喜欢将gatherspread 用于此类方法。

      library(tidyverse)
      df_start %>% 
        rownames_to_column() %>% 
        gather(k, v, -a, -rowname) %>% 
        group_by(rowname) %>% 
        mutate(cond=ifelse(any(a==v), 1, 0)) %>% 
        mutate(col_name=ifelse(cond==1, k[v==1], NA)) %>% 
        ungroup() %>% 
        spread(k, v) %>% 
        select(-rowname)
      # A tibble: 5 x 6
            a  cond col_name     b     c     n
        <dbl> <dbl> <chr>    <dbl> <dbl> <dbl>
      1     1     1 c            0     1     0
      2     1     1 b            1     0     0
      3     1     0 NA           0     0     0
      4     1     1 n            0     0     1
      5     1     0 NA           0     0     0
      

      或者不改变管道/应用解决方案,如

      df_start %>% 
        mutate(col_name=apply(.[-1], 1, function(x, y) y[x==1], colnames(.)[-1])) %>% 
        mutate(cond=as.numeric(apply(.[-ncol(.)], 1, function(x) any(x[1] == x[-1]))))
        a b c n col_name cond
      1 1 0 1 0        c    1
      2 1 1 0 0        b    1
      3 1 0 0 0             0
      4 1 0 0 1        n    1
      5 1 0 0 0             0
      

      【讨论】:

        【解决方案3】:

        另一种基本 R 解决方案:

        m <- df_start[,1] == df_start[,2:4]
        
        df_start$cond <- rowSums(m)
        df_start$col_name[!!rowSums(m)] <- names(df_start[2:4])[max.col(m) * rowSums(m)]
        

        给出:

        > df_start
          a b c n cond col_name
        1 1 0 1 0    1        c
        2 1 1 0 0    1        b
        3 1 0 0 0    0     <NA>
        4 1 0 0 1    1        n
        5 1 0 0 0    0     <NA>
        

        【讨论】:

          【解决方案4】:

          @Jimbou 与 tidyverse 的绝佳解决方案。为了完成,您可以通过更改 Jimbous 代码的最后一行来安排结果:

          select(-c(rowname, cond, col_name), c(cond, col_name))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-01-22
            • 1970-01-01
            • 2018-03-05
            • 2017-12-08
            • 2022-12-10
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多