dplyrを使用してdata.frame内の完全なケースをフィルタリング(ケースごとの削除)


97

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))

4
complete.casesベクターを受け入れるだけではありません。データフレーム全体も​​必要です。
joran 14年

しかし、それはdplyrのフィルター機能の一部としては機能しません。私は十分に明確ではなかったと思い、質問を更新しました。
user2503795 2014年

1
それがdplyrでどのように機能しないかを正確に示すことができれば役立ちますが、私がフィルターでそれを試すと、それはうまく機能します。
joran

回答:


183

これを試して:

df %>% na.omit

またはこれ:

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

またはこれ:

library(tidyr)
df %>% drop_na

1つの変数の欠落に基づいてフィルタリングする場合は、条件を使用します。

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とコメントの最新バージョンを反映するように更新されました。

追加更新された最新バージョンのtidyrとコメントを反映しました。


ちょうど答えに戻ってきて、あなたの役に立つ答えを見ました!
infominer 2014年

1
ありがとう!いくつかのベンチマーク結果を追加しました。na.omit()パフォーマンスはかなり悪いですが、1つは高速です。
user2503795 14年

1
これも同様に機能しますdf %>% filter(complete.cases(.))。dplyrの最近の変更によってこれが可能になったかどうかは不明です。
user2503795 2015年

@ jan-katinsが指摘しているように、Tidyverse関数が呼び出されるdrop_naため、次のことが可能になりますdf %>% drop_na()
cbrnr 2018年

26

これは私にとってはうまくいきます:

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

1
回答を「。」に更新しました。complete.casesおよび追加されたベンチマーク-気にしないでください:-)
talat

:)私はしません。ありがとうございました。
ミハトロシュ

1
df %>% slice(which(complete.cases(.)))上記のベンチマークでは、フィルターアプローチよりも約20%高速であることがわかりました 。
talat

このフィルターを他のdplyrコマンド(group_by()など)と一緒にdplyrパイプで使用している場合は%>% data.frame() %>%、complete.cases(。)でフィルターをかける前に追加する必要があることに注意してください。ティブルまたはグループ化されたティブルまたは何か。または、少なくとも、それは私の経験です。
C.デニー

16

グロタンディークの回答のベンチマーク結果は次のとおりです。na.omit()には、他の2つのソリューションの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

12

これは、dplyr::selectNA値を持たない列(基本的には理解できるすべてのもの)を指定できる短い関数です(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は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、2017

@Monduizこれは、必要な変数が欠落しているため、データを追加すると(変数にNAが多い場合)パイプラインの次のステップで失敗する可能性があることを意味します...
Jan Katins

そうですね。
Monduiz、2017

6

これを試して

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鎖にきちんと統合を可能にする
user2503795

@ G.Grothendieckによる回答を確認してください
infominer

dplyr:::do.data.frame声明env$. <- .data環境にドットが追加されます。magrittr :: "%>%" `にはそのようなステートメントはありません
G. Grothendieck

コメントを間違った場所に入力したに違いありません。
G.グロタンディーク2014年

3

完全を期すために、完全に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(.), )

追加のボーナスは速度です。これはfilterおよびna.omitバリアントの中で最速の方法です(@MihaTroštマイクロベンチマークを使用してテスト済み)。


MihaTroštのデータを使ってベンチマークを行うと、を使用するextract()よりも10倍も遅いことがわかりますfilter()。ただし、で小さいデータフレームを作成するとdf <- df[1:100, 1:10]、画像が変化しextract()、最速になります。
Stibu 2017年

あなたは正しいです。MihaTroštベンチマークでmagrittr::extractのみ、最速の方法であるように見えn <= 5e3ます。
mbask 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.