【问题标题】:Check row wise if value is present in column and update new column row wise如果列中存在值,则按行检查并按行更新新列
【发布时间】:2019-05-12 22:15:01
【问题描述】:

在 R 中,如果列表中的任何值(例如 2、3 或 4)出现在三列中的任意一列中,我如何检查每一行,然后在第四列中更改该行?

假设我有一个 df:

 A B C D
 1 1 1
 2 1 1
 3 1 1

我想写(没有for循环) 如果第 n 行(列 A 或 B 或 C)== 2 或 3 或 4 则 D[1,]=1,否则 = 0

如果我的数字出现在三个特定列中的任何一个中,则基本上逐行检查,如果是,则将第四列更新为 1,如果不是 0。

谢谢,

【问题讨论】:

    标签: r


    【解决方案1】:

    你可以使用apply

    vec <- 2:4
    df1$D <- apply(df1,1, function(x) any(vec %in% x)) +0
    #   A B C D
    # 1 1 1 1 0
    # 2 2 1 1 1
    # 3 3 1 1 1
    

    tidyverse 版本,可能更高效,因为apply 涉及一些矩阵转换:

    library(tidyverse)
    df1 %>% mutate(D = pmap_int(.,~any(vec %in% .)))
    #   A B C D
    # 1 1 1 1 0
    # 2 2 1 1 1
    # 3 3 1 1 1
    

    数据

    df1 <- data.frame(A = c(1, 2,3), 
                       B = c(1, 1, 1), 
                       C = c(1, 1, 1))
    

    【讨论】:

      【解决方案2】:

      这里是你如何使用dplyr

      library(dplyr)
      test <- data.frame(A = c(1, 2, 3), 
                         B = c(1, 1, 1), 
                         C = c(1, 1, 1))
      
      testColumns <- c(2, 3, 4)                         # Values you want to flag 
      

      现在我们已经有了我们的数据框和一个带有我们想要在新列中标记的值的向量,让我们使用 rowwise() 告诉 R 查看数据框的每一行,然后组合 @987654328 @ 根据各种情况创建一个新列 D
      我们指定测试用例,然后使用case_when() 指定它们所需的值。

      我们是这样做的:

      test <- test %>% 
        rowwise() %>%                                   # Look at test on a 'by row' basis'
        mutate(D = case_when(A %in% testColumns ~ 1,    # use mutate to create a new column D
                             B %in% testColumns ~ 1,
                             C %in% testColumns ~ 1, 
                             TRUE               ~ 0))
      

      这给了我们下表:

      print(test)
      ## A tibble: 3 x 4
      #      A     B     C     D
      #  <dbl> <dbl> <dbl> <dbl>
      #1     1     1     1     0
      #2     2     1     1     1
      #3     3     1     1     1
      

      以下是我们使用的一些函数的一些有用链接:
      mutate()
      rowwise()
      case_when()

      【讨论】:

      • 使用data.table 的一个优点是您可以使用.SD 而不是指定每个列名,因此在具有大量列的表上执行此操作同样容易。当然,dplyr 可能会这样,我不是很熟悉 :)
      • 这是一个很好的问题,我不确定它是如何工作的。调查一下!
      • 感谢您的帮助。我是否必须创建一个新的数据框,或者我可以将 D 列添加到“测试”中吗?我将运行多个测试,创建 8 个我想附加到我的数据框的新列。
      • 在这种情况下,您可以使用%&lt;&gt;%,这称为复合赋值管道运算符。这就像常规管道运算符%&gt;% 一样工作,但它将结果分配回原始对象。请参阅此链接以获取帮助:Pipes in R Tutorial For Beginners
      • 我更新了我的回复,使用我上面指出的转发管道。
      【解决方案3】:

      只有这三个条件你可以做到

      df1$D <- as.integer(rowSums(df1 == 2 | df1 == 3 | df1 == 4) >= 1) # or maybe df1 >=2 & df1 <= 4
      df1
      #  A B C D
      #1 1 1 1 0
      #2 2 1 1 1
      #3 3 1 1 1
      

      数据

      df1 <- structure(list(A = 1:3, B = c(1L, 1L, 1L), C = c(1L, 1L, 1L)), .Names = c("A", 
      "B", "C"), class = "data.frame", row.names = c(NA, -3L))
      

      【讨论】:

        【解决方案4】:

        tidyverse 中的一种方法:

        df %>%
         rowid_to_column() %>% #Creating an unique row ID
         gather(var, val, -rowid) %>% #Transforming the data from wide to long
         group_by(rowid) %>% #Grouping
         mutate(D = ifelse(any(val %in% c(2, 3, 4)), 1, 0)) %>% #Testing whether any value from a given row is in the specified list 
         spread(var, val) %>% #Returning the data to wide format
         ungroup() %>%
         select(-rowid) #Deleting the redundant variable
        
              D     A     B     C
          <dbl> <int> <int> <int>
        1    0.     1     1     1
        2    1.     2     1     1
        3    1.     3     1     1
        

        【讨论】:

        • 这是一个非常复杂的解决简单问题的方法。使用 rowwise() 和 mutate 将是一种更简单的方法。
        • @OTStats 你的方法绝对比我的更优雅、更直接。但是,我认为看到解决同一问题的不同方法是件好事。
        • 我绝对同意 - 帮助解决许多不同类型的问题。
        【解决方案5】:

        为感兴趣的列名和编号参数化。

        library(tidyverse)
        
        data <-
          data.frame(
            A = c(1, 2, 3), 
            B = c(1, 1, 1), 
            C = c(1, 1, 1)
          )
        
        nums <- c(2, 3, 4)
        cols <- c('A', 'B', 'C')
        
        data$D <-
          data[, cols] %>%
          map(~.x %in% nums) %>%
          reduce(`|`)
        

        【讨论】:

          【解决方案6】:

          这是一种使用data.table的方法:

          library(data.table)
          test <- data.table(A = c(1, 2,3), 
                             B = c(1, 1, 1), 
                             C = c(1, 1, 1))
          checkValues <- c(2, 3, 4)
          
          test[, c("D"):= Reduce(`|`, lapply(.SD, function(x){x %in% checkValues}))]
          
          test
             A B C     D
          1: 1 1 1 FALSE
          2: 2 1 1  TRUE
          3: 3 1 1  TRUE
          

          替换FALSE=0TRUE=1 很容易(将Reduce(|, lapply(.SD, function(x){x %in% c(2, 3, 4)})) 替换为as.numeric(Reduce(|, lapply(.SD, function(x){x %in% c(2, 3, 4)}))),但您似乎使用D 来保存逻辑值,所以将它作为逻辑向量对我来说是有意义的。

          这也将test 更新为通过引用包含D 列,这样效率更高。

          也许还有两个答案:Finding rows containing a value (or values) in any columnAdd multiple columns to R data.table in one function call?

          【讨论】:

            猜你喜欢
            • 2012-11-12
            • 2022-09-26
            • 1970-01-01
            • 2020-08-01
            • 2022-01-06
            • 2010-12-05
            • 1970-01-01
            • 1970-01-01
            • 2014-07-18
            相关资源
            最近更新 更多