【问题标题】:filter for complete cases in data.frame using dplyr (case-wise deletion)使用 dplyr 过滤 data.frame 中的完整案例(按案例删除)
【发布时间】:2014-04-16 17:05:36
【问题描述】:

是否可以使用 dplyr 为完整案例过滤 data.frame?当然,complete.cases 包含所有变量的列表。但这是 a) 当有很多变量时很冗长 b) 当变量名称未知时是不可能的(例如,在处理任何 data.frame 的函数中)。

library(dplyr)
df = data.frame(
    x1 = c(1,2,3,NA),
    x2 = c(1,2,NA,5)
)

df %.%
  filter(complete.cases(x1,x2))

【问题讨论】:

  • complete.cases 不仅接受向量。它也需要整个数据帧。
  • 但这不适用于dplyr 的过滤功能。我想我不够清楚并更新了我的问题。
  • 如果你能准确地演示它如何不能与 dplyr 一起使用会有所帮助,但是当我使用过滤器尝试它时,它工作得很好。

标签: r dplyr magrittr


【解决方案1】:

dplyr >= 1.0.4

if_anyif_alldplyr 的较新版本中可用,以在 filter 函数中应用类似于 across 的语法。如果您的数据框中有其他变量不属于您认为的完整案例的一部分,这可能会很有用。例如,如果您只希望以“x”开头的列中没有缺失的行:

library(dplyr)
df = data.frame(
  x1 = c(1,2,3,NA),
  x2 = c(1,2,NA,5),
  y = c(NA, "A", "B", "C")
)

df %>% 
  dplyr::filter(if_all(starts_with("x"), ~!is.na(.)))

  x1 x2    y
1  1  1 <NA>
2  2  2    A

有关这些功能的更多信息,请参阅link

【讨论】:

    【解决方案2】:

    试试这个:

    df %>% na.omit
    

    或者这个:

    df %>% filter(complete.cases(.))
    

    或者这个:

    library(tidyr)
    df %>% drop_na
    

    如果要根据一个变量的缺失进行过滤,请使用条件:

    df %>% filter(!is.na(x1))
    

    df %>% drop_na(x1)
    

    其他答案表明na.omit 上面的解决方案要慢得多,但这必须与它返回na.action 属性中省略行的行索引的事实相平衡,而上面的其他解决方案则没有。

    str(df %>% na.omit)
    ## 'data.frame':   2 obs. of  2 variables:
    ##  $ x1: num  1 2
    ##  $ x2: num  1 2
    ##  - attr(*, "na.action")= 'omit' Named int  3 4
    ##    ..- attr(*, "names")= chr  "3" "4"
    

    添加已更新以反映最新版本的 dplyr 和 cmets。

    添加已更新以反映最新版本的 tidyr 和 cmets。

    【讨论】:

    • 刚回来回答,看到你有用的回答!
    • 谢谢!我添加了一些基准测试结果。 na.omit() 性能很差,但速度很快。
    • 现在也可以使用:df %&gt;% filter(complete.cases(.))。不确定最近 dplyr 的变化是否使这成为可能。
    • 正如@jan-katins 指出的那样,Tidyverse 函数被称为drop_na,所以你现在可以这样做:df %&gt;% drop_na()
    【解决方案3】:

    以下是格洛腾迪克回复的一些基准测试结果。 na.omit() 花费的时间是其他两个解决方案的 20 倍。我认为如果 dplyr 有一个功能可能会作为过滤器的一部分。

    library('rbenchmark')
    library('dplyr')
    
    n = 5e6
    n.na = 100000
    df = data.frame(
        x1 = sample(1:10, n, replace=TRUE),
        x2 = sample(1:10, n, replace=TRUE)
    )
    df$x1[sample(1:n, n.na)] = NA
    df$x2[sample(1:n, n.na)] = NA
    
    
    benchmark(
        df %>% filter(complete.cases(x1,x2)),
        df %>% na.omit(),
        df %>% (function(x) filter(x, complete.cases(x)))()
        , replications=50)
    
    #                                                  test replications elapsed relative
    # 3 df %.% (function(x) filter(x, complete.cases(x)))()           50   5.422    1.000
    # 1               df %.% filter(complete.cases(x1, x2))           50   6.262    1.155
    # 2                                    df %.% na.omit()           50 109.618   20.217
    

    【讨论】:

      【解决方案4】:

      这是一个简短的函数,可让您指定不应有任何 NA 值的列(基本上是 dplyr::select 可以理解的所有内容)(以 pandas df.dropna() 为模型):

      drop_na <- function(data, ...){
          if (missing(...)){
              f = complete.cases(data)
          } else {
              f <- complete.cases(select_(data, .dots = lazyeval::lazy_dots(...)))
          }
          filter(data, f)
      }
      

      [drop_na is now part of tidyr: 上面可以替换成library("tidyr")]

      例子:

      library("dplyr")
      df <- data.frame(a=c(1,2,3,4,NA), b=c(NA,1,2,3,4), ac=c(1,2,NA,3,4))
      df %>% drop_na(a,b)
      df %>% drop_na(starts_with("a"))
      df %>% drop_na() # drops all rows with NAs
      

      【讨论】:

      • 如果能够添加像 0.5 这样的截止值并按列处理它会不会更有用?案例:消除具有 50% 及以上缺失数据的变量。示例:data[, -which(colMeans(is.na(data)) > 0.5)] 如果能用 tidyr 来做这件事就好了。
      • @Monduiz 这意味着添加更多数据(其中一个变量有很多 NA)可能会导致管道中的下一步失败,因为现在缺少所需的变量......
      • 对,有道理。
      【解决方案5】:

      为了完整起见,dplyr::filter 可以完全避免,但仍然可以通过使用magrittr:extract[ 的别名)来组成链:

      library(magrittr)
      df = data.frame(
        x1 = c(1,2,3,NA),
        x2 = c(1,2,NA,5))
      
      df %>%
        extract(complete.cases(.), )
      

      额外的好处是速度,这是filterna.omit 变体中最快的方法(使用@Miha Trošt 微基准测试)。

      【讨论】:

      • 当我用 Miha Trošt 的数据做基准测试时,我发现使用 extract() 几乎比 filter() 慢十倍。但是,当我用df &lt;- df[1:100, 1:10] 创建一个较小的数据框时,图片会发生变化,extract() 是最快的。
      • 你是对的。看起来 magrittr::extract 只有在 Miha Trošt 基准测试中 n &lt;= 5e3 时才是最快的方式。
      【解决方案6】:

      这对我有用:

      df %>%
        filter(complete.cases(df))    
      

      或者更笼统一点:

      library(dplyr) # 0.4
      df %>% filter(complete.cases(.))
      

      这样做的好处是数据可以在传递给过滤器之前在链中进行修改。

      另一个具有更多列的基准:

      set.seed(123)
      x <- sample(1e5,1e5*26, replace = TRUE)
      x[sample(seq_along(x), 1e3)] <- NA
      df <- as.data.frame(matrix(x, ncol = 26))
      library(microbenchmark)
      microbenchmark(
        na.omit = {df %>% na.omit},
        filter.anonymous = {df %>% (function(x) filter(x, complete.cases(x)))},
        rowSums = {df %>% filter(rowSums(is.na(.)) == 0L)},
        filter = {df %>% filter(complete.cases(.))},
        times = 20L,
        unit = "relative")
      
      #Unit: relative
      #             expr       min        lq    median         uq       max neval
       #         na.omit 12.252048 11.248707 11.327005 11.0623422 12.823233    20
       #filter.anonymous  1.149305  1.022891  1.013779  0.9948659  4.668691    20
       #         rowSums  2.281002  2.377807  2.420615  2.3467519  5.223077    20
       #          filter  1.000000  1.000000  1.000000  1.0000000  1.000000    20
      

      【讨论】:

      • 我用“。”更新了你的答案。在 complete.cases 中并添加了基准 - 希望你不介意 :-)
      • :) 我没有。谢谢。
      • 我发现df %&gt;% slice(which(complete.cases(.))) 的执行速度比上述基准测试中的过滤器方法快约 20%。
      • 值得注意的是,如果您在 dplyr 管道中将此过滤器与其他 dplyr 命令(例如 group_by())一起使用,则需要在尝试完成过滤之前添加 %&gt;% data.frame() %&gt;%。案例(。)因为它不适用于小标题或分组小标题或其他东西。或者至少,这是我的经历。
      【解决方案7】:

      试试这个

      df[complete.cases(df),] #output to console
      

      甚至这个

      df.complete <- df[complete.cases(df),] #assign to a new data.frame
      

      上述命令负责检查所有列(变量)的完整性 在你的 data.frame 中。

      【讨论】:

      • 谢谢。我想我还不够清楚(问题已更新)。我知道 complete.cases(df) 但我想将 dplyr 作为过滤器功能的一部分。这将允许在 dplyr 链等中进行巧妙的集成。
      • 检查@G.Grothendieck 的答案
      • dplyr:::do.data.frame 中,env$. &lt;- .data 语句将点添加到环境中。 magrittr::"%>%"`中没有这样的语句`
      • 抱歉一定是评论输入错地方了。
      猜你喜欢
      • 2016-06-10
      • 1970-01-01
      • 2022-10-14
      • 2020-04-16
      • 1970-01-01
      • 2021-06-01
      • 2023-01-17
      相关资源
      最近更新 更多