filtre pour les cas complets dans data.frame en utilisant dplyr (suppression par casse)


98

Est-il possible de filtrer un data.frame pour des cas complets en utilisant dplyr? complete.casesavec une liste de toutes les variables fonctionne, bien sûr. Mais c'est a) verbeux quand il y a beaucoup de variables et b) impossible quand les noms des variables ne sont pas connus (par exemple dans une fonction qui traite n'importe quel 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.casesn'accepte pas seulement les vecteurs. Cela prend également des trames de données entières.
joran

Mais cela ne fonctionne pas dans le cadre de dplyrla fonction de filtre de. Je suppose que je n'ai pas été assez clair et j'ai mis à jour ma question.
user2503795

1
Cela aiderait si vous pouviez démontrer exactement comment cela ne fonctionne pas avec dplyr, mais quand je l'essaye avec un filtre, cela fonctionne très bien.
joran

Réponses:


186

Essaye ça:

df %>% na.omit

ou ca:

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

ou ca:

library(tidyr)
df %>% drop_na

Si vous souhaitez filtrer en fonction de l'absence d'une variable, utilisez une condition:

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

ou

df %>% drop_na(x1)

D'autres réponses indiquent que des solutions ci na.omit- dessus est beaucoup plus lente, mais cela doit être mis en balance avec le fait qu'elle renvoie les indices de ligne des lignes omises dans l' na.actionattribut alors que les autres solutions ci-dessus ne le font pas.

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"

AJOUTÉ Ont mis à jour pour refléter la dernière version de dplyr et des commentaires.

AJOUTÉ Ont mis à jour pour refléter la dernière version de tidyr et des commentaires.


Je viens de revenir pour répondre et j'ai vu votre réponse utile!
infominer

1
Merci! J'ai ajouté quelques résultats de référence. na.omit()fonctionne assez mal mais celui-là est rapide.
user2503795

1
Cela fonctionne maintenant aussi bien: df %>% filter(complete.cases(.)). Je ne sais pas si les changements récents de dplyr ont rendu cela possible.
user2503795

Comme @ points de jan-katins out, la fonction Tidyverse est appelée drop_na, de sorte que vous pouvez faire: df %>% drop_na().
cbrnr

26

Cela fonctionne pour moi:

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

Ou un peu plus général:

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

Cela aurait l'avantage que les données auraient pu être modifiées dans la chaîne avant de les transmettre au filtre.

Un autre benchmark avec plus de colonnes:

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
J'ai mis à jour votre réponse avec "." dans les cas complets et le benchmark ajouté - j'espère que cela ne vous dérange pas :-)
talat

:) Je ne. Je vous remercie.
Miha Trošt

1
J'ai trouvé df %>% slice(which(complete.cases(.)))exécuté ~ 20% plus rapide que l'approche par filtre dans le benchmark ci-dessus.
talat

Il convient de noter que si vous utilisez ce filtre dans un tube de dplyr avec d' autres commandes de dplyr (tels que group_by ()), vous devrez ajouter , %>% data.frame() %>%car il ne fonctionnera pas sur avant d'essayer et filtre sur complete.cases (.) tibbles ou groupes ou quelque chose. Ou du moins, c'est l'expérience que j'ai vécue.
C. Denney

16

Voici quelques résultats de référence pour la réponse de Grothendieck. na.omit () prend 20 fois plus de temps que les deux autres solutions. Je pense que ce serait bien si dplyr avait une fonction pour cela peut-être dans le cadre du filtre.

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

Il s'agit d'une fonction courte qui vous permet de spécifier des colonnes (essentiellement tout ce qui dplyr::selectpeut comprendre) qui ne devraient pas avoir de valeurs NA (modelées d'après 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 fait maintenant partie de tidyr : ce qui précède peut être remplacé par library("tidyr")]

Exemples:

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

Ne serait-il pas encore plus utile de pouvoir ajouter un seuil tel que 0,5 et de le traiter par colonnes? Cas: éliminer les variables avec 50% et plus de données manquantes. Exemple: data [, -which (colMeans (is.na (data))> 0.5)] Ce serait bien de pouvoir le faire avec tidyr.
Monduiz

@Monduiz Cela signifierait que l'ajout de plus de données (où une variable a alors beaucoup de NA) pourrait échouer à l'étape suivante du pipeline car une variable nécessaire est maintenant manquante ...
Jan Katins

Oui, cela a du sens.
Monduiz

6

essaye ça

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

OU même ça

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

Les commandes ci-dessus se chargent de vérifier l'exhaustivité de toutes les colonnes (variables) de votre data.frame.


Merci. Je suppose que je n'étais pas assez clair (question mise à jour). Je connais complete.cases (df) mais j'aimerais le faire avec dplyrdans le cadre de la fonction de filtre. Cela permettrait une intégration soignée dans les chaînes dplyr, etc.
user2503795

Vérifiez la réponse de @ G.Grothendieck
infominer

Dans dplyr:::do.data.framela déclaration env$. <- .dataajoute un point à l'environnement. Aucune déclaration de ce type dans magrittr :: "%>%" `
G. Grothendieck

Désolé doit avoir entré le commentaire au mauvais endroit.
G. Grothendieck

3

Juste pour être complet, dplyr::filterpeut être complètement évité mais toujours capable de composer des chaînes simplement en utilisant magrittr:extract(un alias de [):

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

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

Le bonus supplémentaire est la vitesse, c'est la méthode la plus rapide parmi les variantes filteret na.omit(testée avec les microbenchmarks @Miha Trošt).


Quand je fais le benchmark avec les données de Miha Trošt, je trouve que l'utilisation extract()est presque dix fois plus lente que filter(). Cependant, lorsque je crée un bloc de données plus petit avec df <- df[1:100, 1:10], l'image change et extract()est la plus rapide.
Stibu

Vous avez raison. Il semble que ce magrittr::extractsoit le moyen le plus rapide uniquement lorsque le n <= 5e3benchmark Miha Trošt.
mbask
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.