【问题标题】:How to omit rows with NA in only two columns in R?如何在 R 的两列中省略带有 NA 的行?
【发布时间】:2014-08-05 17:16:48
【问题描述】:

我想省略 两个 列中 both 中出现 NA 的行。

我熟悉na.omitis.nacomplete.cases,但不知道如何使用它们来获得我想要的东西。例如,我有以下数据框:

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

我想只删除那些NA出现在xy列中的行(不包括z中的任何内容),以提供

x   y   z
1   4   8
2   5   9
3   6   11
NA  7   NA

有没有人知道一个简单的方法来做到这一点?使用 na.omitis.nacomplete.cases 不起作用。

【问题讨论】:

    标签: r na


    【解决方案1】:
    df[!with(df,is.na(x)& is.na(y)),]
    #      x y  z
    #1  1 4  8
    #2  2 5  9
    #4  3 6 11
    #5 NA 7 NA
    

    我确实在一个稍大的数据集上进行了基准测试。结果如下:

    set.seed(237)
    df <- data.frame(x=sample(c(NA,1:20), 1e6, replace=T), y= sample(c(NA, 1:10), 1e6, replace=T), z= sample(c(NA, 5:15), 1e6,replace=T)) 
    
    f1 <- function() df[!with(df,is.na(x)& is.na(y)),]
    f2 <- function() df[rowSums(is.na(df[c("x", "y")])) != 2, ]
    f3 <- function()  df[ apply( df, 1, function(x) sum(is.na(x))>1 ), ] 
    
    library(microbenchmark)
    
    microbenchmark(f1(), f2(), f3(), unit="relative")
    Unit: relative
    #expr       min        lq    median        uq       max neval
    # f1()  1.000000  1.000000  1.000000  1.000000  1.000000   100
    # f2()  1.044812  1.068189  1.138323  1.129611  0.856396   100
    # f3() 26.205272 25.848441 24.357665 21.799930 22.881378   100
    

    【讨论】:

    • 感谢 akrun - 我使用了您的答案,因为它是最短的,但选择了另一个,因为它首先发布。再次感谢。 :)
    • @Thomas,请不要根据这些参数更改接受的答案。如果 akrun 的答案是您正在寻找的(听起来确实如此),那么他们应该为此获得赞誉。我的 FGITW 参考资料只是对您决定选择哪个答案的方法的一个小插曲。我的回答的主要优点是它易于应用于不仅仅是少数列。
    • Okie Dokie! :) 将答案改回 akrun!
    • @Thomas,根据您描述的参数,这应该是 Amanda Mahto。抱歉,讨论晚了。
    • @Thomas,如果您有兴趣,我已经更新了我的答案,以显示我所指的关于我建议的rowSums + is.na 方法的易用性。
    【解决方案2】:

    您可以申请对行进行切片:

    sel <- apply( df, 1, function(x) sum(is.na(x))>1 )
    

    然后你可以选择:

    df[ sel, ]
    

    要忽略 z 列,只需从应用中省略它:

    sel <- apply( df[,c("x","y")], 1, function(x) sum(is.na(x))>1 )
    

    如果他们都必须是TRUE,只需将函数稍微改一下即可:

    sel <- apply( df[,c("x","y")], 1, function(x) all(is.na(x)) )
    

    这里的其他解决方案更具体地解决了这个特定问题,但apply 值得学习,因为它解决了许多其他问题。代价就是速度(通常需要注意小数据集和速度测试):

    > microbenchmark( df[!with(df,is.na(x)& is.na(y)),], df[rowSums(is.na(df[c("x", "y")])) != 2, ], df[ apply( df, 1, function(x) sum(is.na(x))>1 ), ] )
    Unit: microseconds
                                                  expr     min       lq   median       uq      max neval
                  df[!with(df, is.na(x) & is.na(y)), ]  67.148  71.5150  76.0340  86.0155 1049.576   100
            df[rowSums(is.na(df[c("x", "y")])) != 2, ] 132.064 139.8760 145.5605 166.6945  498.934   100
     df[apply(df, 1, function(x) sum(is.na(x)) > 1), ] 175.372 184.4305 201.6360 218.7150  321.583   100
    

    【讨论】:

    • 非常感谢您的回答,非常感谢您的时间和帮助。
    • 没问题。您对一个相对简单的问题得到了一些很好的答案,这说明您提出问题的能力很好:-)
    【解决方案3】:

    rowSumsis.na 一起使用,如下所示:

    > df[rowSums(is.na(df[c("x", "y")])) != 2, ]
       x y  z
    1  1 4  8
    2  2 5  9
    4  3 6 11
    5 NA 7 NA
    

    跳上基准测试车,并展示我所说的关于这是一个相当容易概括的解决方案的内容,请考虑以下几点:

    ## Sample data with 10 columns and 1 million rows
    set.seed(123)
    df <- data.frame(replicate(10, sample(c(NA, 1:20), 
                                          1e6, replace = TRUE)))
    

    首先,如果您只对两列感兴趣,这就是它的样子。两种解决方案都非常清晰且简短。速度非常接近。

    f1 <- function() {
      df[!with(df, is.na(X1) & is.na(X2)), ]
    } 
    f2 <- function() {
      df[rowSums(is.na(df[1:2])) != 2, ]
    } 
    
    library(microbenchmark)
    microbenchmark(f1(), f2(), times = 20)
    # Unit: milliseconds
    #  expr      min       lq   median       uq      max neval
    #  f1() 745.8378 1100.764 1128.047 1199.607 1310.236    20
    #  f2() 784.2132 1101.695 1125.380 1163.675 1303.161    20
    

    接下来,让我们看看同样的问题,但这一次,我们正在考虑前 5 列中的 NA 值。此时rowSums的方式稍微快一些,语法变化不大。

    f1_5 <- function() {
      df[!with(df, is.na(X1) & is.na(X2) & is.na(X3) &
                 is.na(X4) & is.na(X5)), ]
    } 
    f2_5 <- function() {
      df[rowSums(is.na(df[1:5])) != 5, ]
    } 
    
    microbenchmark(f1_5(), f2_5(), times = 20)
    # Unit: seconds
    #    expr      min       lq   median       uq      max neval
    #  f1_5() 1.275032 1.294777 1.325957 1.368315 1.572772    20
    #  f2_5() 1.088564 1.169976 1.193282 1.225772 1.275915    20
    

    【讨论】:

    • 您好 Ananda Mahto,我不确定您的意思,但首先发布了 akrun 的答案。
    • 感谢阿南达,我选择了您的答案,因为它首先发布。
    【解决方案4】:

    dplyr解决方案

    require("dplyr")
    df %>% filter_at(.vars = vars(x, y), .vars_predicate = any_vars(!is.na(.)))
    

    可以使用.vars 参数修改为采用任意数量的列


    更新:dplyr 1.0.4

    df %>%
      filter(!if_all(c(x, y), is.na))
    

    查看类似答案:https://stackoverflow.com/a/66136167/6105259

    【讨论】:

      【解决方案5】:

      这也是非常基本的dplyr解决方案:

      library(dplyr)
      
      df %>%
        filter(!(is.na(x) & is.na(y)))
      
         x y  z
      1  1 4  8
      2  2 5  9
      3  3 6 11
      4 NA 7 NA
      

      【讨论】:

        猜你喜欢
        • 2020-06-17
        • 1970-01-01
        • 2012-06-30
        • 2023-01-18
        • 1970-01-01
        • 2019-06-02
        • 2021-07-02
        • 2012-07-04
        • 1970-01-01
        相关资源
        最近更新 更多