【问题标题】:R - Applying condition across multiple columns ignoring NAR - 在多个列中应用条件忽略 NA
【发布时间】:2021-12-02 04:53:15
【问题描述】:

假设我有以下数据框:

x <- c(1, 1, 2, 3, 4, 5)
y <- c(1, 1, 1, 3, 4, 5)
z <- c(NA, 1, 1, 3, 4, NA)

得到:

x  y  z
1  1  NA
1  1  1
2  1  1
3  3  3
4  4  4
5  4  NA

我想得到一个条件语句,如果所有 non-NA x、y 和 z 值都等于 1,那么它将被标记为 1,我将如何写这个脚本?

例如,我想要的是以下内容:

x  y  z  flag1
1  1  NA 1
1  1  1  1
2  1  1  0
3  3  3  0
4  4  4  0
5  4  NA 0

此外,我还想标记是否有任何变量包含 4,忽略 NA,以便我可以得到:

x  y  z  flag1 flag2
1  1  NA 1     0
1  1  1  1     0
2  1  1  0     0
3  3  3  0     0
4  4  4  0     1
5  4  NA 0     1

【问题讨论】:

    标签: r if-statement conditional-statements


    【解决方案1】:

    这是一个使用rowwisec_across 的选项:

    library(dplyr)
    
    df %>% 
      rowwise() %>% 
      mutate(flag1 = as.numeric(all(c_across() == 1, na.rm = T)),
             flag2 = as.numeric(any(c_across() == 4, na.rm = T))) %>% 
      ungroup()
    

    c_across 会将每一行组合成一个原子向量,以便与您的条件进行比较。

    注意:默认情况下,c_across 适用于 所有 列。您可以使用任何 tidyselect 语法来更改它。例如,x:z

    输出

          x     y     z flag1 flag2
      <dbl> <dbl> <dbl> <dbl> <dbl>
    1     1     1    NA     1     0
    2     1     1     1     1     0
    3     2     1     1     0     0
    4     3     3     3     0     0
    5     4     4     4     0     1
    6     5     4    NA     0     1
    

    【讨论】:

      【解决方案2】:

      这是使用allany 进行旋转的另一种替代方法:

      library(tidyr)
      library(dplyr)
      
      df %>% 
        pivot_longer(
          cols=everything()
        ) %>% 
        mutate(id = as.integer(gl(n(), 3, n()))) %>% 
        group_by(id) %>% 
        mutate(flag1 = ifelse(all(value == 1, na.rm=TRUE), 1,0),
               flag2 = ifelse(any(value == 4, na.rm=TRUE), 1,0)) %>% 
        pivot_wider(
          names_from = name, 
          values_from = value
        ) %>% 
        ungroup() %>% 
        select(x,y,z,flag1, flag2)
      

      输出:

            x     y     z flag1 flag2
        <dbl> <dbl> <dbl> <dbl> <dbl>
      1     1     1    NA     1     0
      2     1     1     1     1     0
      3     2     1     1     0     0
      4     3     3     3     0     0
      5     4     4     4     0     1
      6     5     4    NA     0     1
      

      【讨论】:

        【解决方案3】:
        library(tidyverse)
        
        df = tibble(
          x = c(1, 1, 2, 3, 4, 5),
          y = c(1, 1, 1, 3, 4, 5),
          z = c(NA, 1, 1, 3, 4, NA)
        )
        
        
        df %>% mutate(
          flag1 = ifelse((x==1 | is.na(x)) & (y==1 | is.na(y)) & (z==1 | is.na(z)), 1, 0),
          flaf2 = ifelse((x==4 | is.na(x)) | (y==4 | is.na(y)) | (z==4 | is.na(z)), 1, 0)
        )
        
        

        输出

        # A tibble: 6 x 5
              x     y     z flag1 flaf2
          <dbl> <dbl> <dbl> <dbl> <dbl>
        1     1     1    NA     1     1
        2     1     1     1     1     0
        3     2     1     1     0     0
        4     3     3     3     0     0
        5     4     4     4     0     1
        6     5     5    NA     0     1
        

        更新 1

        注意,当所有变量都是NA 时,您不能忘记决定要做什么。以下是其中一种可能解决方案的更正版本。

        library(tidyverse)
        
        df = tibble(
          x = c(1, 1, 2, 3, 4, 5, NA),
          y = c(1, 1, 1, 3, 4, 5, NA),
          z = c(NA, 1, 1, 3, 4, NA, NA)
        )
        
        
        df %>% mutate(
          flag1 = ifelse(is.na(x) & is.na(y) & is.na(z), NA, 
                         ifelse((x==1 | is.na(x)) & (y==1 | is.na(y)) & (z==1 | is.na(z)), 1, 0)),
          flag2 = ifelse(is.na(x) & is.na(y) & is.na(z), NA,
                         ifelse((x==4 | is.na(x)) | (y==4 | is.na(y)) | (z==4 | is.na(z)), 1, 0))
        )
        
        

        输出

        # A tibble: 7 x 5
              x     y     z flag1 flag2
          <dbl> <dbl> <dbl> <dbl> <dbl>
        1     1     1    NA     1     1
        2     1     1     1     1     0
        3     2     1     1     0     0
        4     3     3     3     0     0
        5     4     4     4     0     1
        6     5     5    NA     0     1
        7    NA    NA    NA    NA    NA
        

        【讨论】:

          【解决方案4】:

          最简单的是rowSums

          df$flag <-  +(!rowSums(df != 1, na.rm = TRUE) & !!rowSums(!is.na(df)))
          df$flag2 <- +(rowSums(df == 4, na.rm = TRUE) > 0 & !!rowSums(!is.na(df)))
          

          -输出

          > df
            x y  z flag flag2
          1 1 1 NA    1     0
          2 1 1  1    1     0
          3 2 1  1    0     0
          4 3 3  3    0     0
          5 4 4  4    0     1
          6 5 4 NA    0     1
          

          tidyverse 中,我们可以使用if_allif_any 来创建这些列

          library(dplyr)
          df %>%
              mutate(flag1 = +(if_all(everything(),  ~is.na(.)| . %in% 1)), 
                      flag2 = +(if_any(x:z, ~ . %in% 4)))
            x y  z flag1 flag2
          1 1 1 NA     1     0
          2 1 1  1     1     0
          3 2 1  1     0     0
          4 3 3  3     0     0
          5 4 4  4     0     1
          6 5 4 NA     0     1
          

          数据

          df <-structure(list(x = c(1, 1, 2, 3, 4, 5), y = c(1, 1, 1, 3, 4, 
          4), z = c(NA, 1, 1, 3, 4, NA)), class = "data.frame", row.names = c(NA, 
          -6L))
          

          【讨论】:

          • 当我在我的实际数据集上运行这个类似的代码时,它会被标记为“1”。有没有办法忽略所有 NA 的观察结果?
          • @ssjjaca 我正在考虑这个问题。早些时候,我认为这不会是一个案例。您可以使用 is.na 添加条件(如更新中的那样)或使用 is.na 在计算中跳过这些行
          【解决方案5】:

          这是一个比@Akrun 的答案更详细的版本(在更大的数据集上更慢),但更可定制:

          flag1 <- ifelse( (x == 1 | is.na(x) ) &
                           (y == 1 | is.na(y) ) &
                           (z == 1 | is.na(z) ), 1, 0)
          
          flag2 <- ifelse( x == 4 | y == 4 | z == 4, 1, 0)
          

          如果你有一堆这样的向量,你可以将它们存储在一个矩阵或 data.frame 中,这样你就不需要列出每一列来进行计算:

          mat <- cbind(x,y,z)
          
          flag1 <- apply(mat, 1, function(r) sum(r==1 | is.na(r)) == length(r))
          flag2 <- apply(mat, 1, function(r) any(r==4, na.rm=T))
          

          【讨论】:

            【解决方案6】:

            使用应用功能:

            apply(df, 1, function(x) +all(x == 1,na.rm = 1))
            [1] 1 1 0 0 0 0
            apply(df, 1, function(x) +any(x == 4,na.rm = 1))
            [1] 0 0 0 0 1 0
            

            使用的数据:

            df
              x y  z
            1 1 1 NA
            2 1 1  1
            3 2 1  1
            4 3 3  3
            5 4 4  4
            6 5 5 NA
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-12-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-10-20
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多