Filter für vollständige Fälle in data.frame mit dplyr (fallweises Löschen)


97

Ist es möglich, einen data.frame mit dplyr nach vollständigen Fällen zu filtern? complete.casesmit einer Liste aller Variablen funktioniert natürlich. Dies ist jedoch a) ausführlich, wenn viele Variablen vorhanden sind, und b) unmöglich, wenn die Variablennamen nicht bekannt sind (z. B. in einer Funktion, die einen data.frame verarbeitet).

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.casesakzeptiert nicht nur Vektoren. Es werden auch ganze Datenrahmen benötigt.
Joran

Dies funktioniert jedoch nicht als Teil der dplyrFilterfunktion. Ich glaube, ich war nicht klar genug und habe meine Frage aktualisiert.
user2503795

1
Es wäre hilfreich, wenn Sie genau demonstrieren könnten, wie es mit dplyr nicht funktioniert, aber wenn ich es mit Filter versuche, funktioniert es einwandfrei.
Joran

Antworten:


183

Versuche dies:

df %>% na.omit

oder dieses:

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

oder dieses:

library(tidyr)
df %>% drop_na

Wenn Sie basierend auf dem Fehlen einer Variablen filtern möchten, verwenden Sie eine Bedingung:

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

oder

df %>% drop_na(x1)

Andere Antworten weisen darauf hin, dass die oben genannten Lösungen na.omitviel langsamer sind, dies muss jedoch gegen die Tatsache abgewogen werden, dass Zeilenindizes der ausgelassenen Zeilen in der zurückgegeben werdenna.action Attribut zurückgegeben werden, während dies bei den anderen oben genannten Lösungen nicht der .

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"

HINZUGEFÜGT Aktualisiert, um die neueste Version von dplyr und Kommentare wiederzugeben.

HINZUGEFÜGT Aktualisiert, um die neueste Version von tidyr und Kommentare wiederzugeben.


Bin gerade zurückgekommen, um zu antworten und habe deine nützliche Antwort gesehen!
Infominer

1
Vielen Dank! Ich habe einige Benchmark-Ergebnisse hinzugefügt. na.omit()funktioniert ziemlich schlecht, aber der ist schnell.
user2503795

1
Das funktioniert jetzt auch : df %>% filter(complete.cases(.)). Ich bin mir nicht sicher, ob die letzten Änderungen in dplyr dies ermöglicht haben.
user2503795

Wie @ jan-katins hervorhebt, wird die Tidyverse-Funktion aufgerufen drop_na, sodass Sie jetzt Folgendes tun können : df %>% drop_na().
cbrnr

26

Das funktioniert bei mir:

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

Oder etwas allgemeiner:

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

Dies hätte den Vorteil, dass die Daten in der Kette geändert werden könnten, bevor sie an den Filter übergeben werden.

Ein weiterer Benchmark mit mehr Spalten:

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
Ich habe Ihre Antwort mit "." Aktualisiert. in den complete.cases und hinzugefügtem Benchmark - hoffe es macht dir nichts aus :-)
talat

:) Ich nicht. Danke dir.
Miha Trošt

1
Ich fand df %>% slice(which(complete.cases(.)))~ 20% schneller als der Filter-Ansatz in der obigen Benchmark.
Talat

Wenn Sie diesen Filter in einer dplyr-Pipe mit anderen dplyr-Befehlen (z. B. group_by ()) verwenden, müssen Sie ihn hinzufügen, %>% data.frame() %>%bevor Sie versuchen, auf complete.cases (.) Zu filtern, da er nicht funktioniert tibbles oder gruppierte tibbles oder so. Zumindest war das die Erfahrung, die ich gemacht habe.
C. Denney

16

Hier sind einige Benchmark-Ergebnisse für Grothendiecks Antwort. na.omit () benötigt 20x so viel Zeit wie die beiden anderen Lösungen. Ich denke, es wäre schön, wenn dplyr eine Funktion dafür hätte, vielleicht als Teil des Filters.

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

Dies ist eine kurze Funktion, mit der Sie Spalten angeben können (im Grunde alles, was Sie dplyr::selectverstehen können), die keine NA-Werte haben sollten (modelliert nach 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 ist jetzt Teil von tidyr : das obige kann ersetzt werden durch library("tidyr")]

Beispiele:

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

Wäre es nicht noch nützlicher, einen Cutoff wie 0,5 hinzuzufügen und nach Spalten verarbeiten zu lassen? Fall: Eliminieren Sie Variablen mit 50% und mehr fehlenden Daten. Beispiel: data [, -which (colMeans (is.na (data))> 0.5)] Es wäre schön, dies mit tidyr tun zu können.
Monduiz

@ Monduiz Dies würde bedeuten, dass das Hinzufügen weiterer Daten (wobei eine Variable dann viele NA hat) den nächsten Schritt in der Pipeline fehlschlagen könnte, da jetzt eine benötigte Variable fehlt ...
Jan Katins

Richtig, das macht Sinn.
Monduiz

6

Versuche dies

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

ODER auch das

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

Mit den obigen Befehlen wird die Vollständigkeit aller Spalten (Variablen) in Ihrem data.frame überprüft.


Vielen Dank. Ich glaube, ich war nicht klar genug (Frage aktualisiert). Ich kenne complete.cases (df), möchte dies aber dplyrals Teil der Filterfunktion tun . Das würde eine ordentliche Integration in dplyr-Ketten usw. ermöglichen
user2503795

Überprüfen Sie die Antwort von @ G.Grothendieck
infominer

In dplyr:::do.data.frameder Anweisung wird env$. <- .datader Umgebung ein Punkt hinzugefügt. Keine solche Aussage in magrittr :: "%>%" `
G. Grothendieck

Entschuldigung, der Kommentar muss an der falschen Stelle eingegeben worden sein.
G. Grothendieck

3

Nur der Vollständigkeit halber dplyr::filterkann es insgesamt vermieden werden, aber dennoch in der Lage sein, Ketten zu bilden, indem nur magrittr:extract(ein Alias ​​von [) verwendet wird:

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

df %>%
  extract(complete.cases(.), )

Der zusätzliche Bonus ist die Geschwindigkeit. Dies ist die schnellste Methode unter den filterund na.omit-Varianten (getestet mit @Miha Trošt-Mikrobenchmarks).


Wenn ich den Benchmark mit den Daten von Miha Trošt durchführe, stelle ich fest, dass die Verwendung extract()fast zehnmal langsamer ist als filter(). Wenn ich jedoch einen kleineren Datenrahmen mit erstelle df <- df[1:100, 1:10], ändert sich das Bild und extract()ist das schnellste.
Stibu

Du hast Recht. Es sieht so aus, magrittr::extractals wäre der schnellste Weg nur n <= 5e3im Miha Trošt Benchmark.
mbask
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.