web-dev-qa-db-fra.com

filtre pour les cas complets dans data.frame à l'aide de dplyr (suppression au cas par cas)

Est-il possible de filtrer un data.frame pour des cas complets en utilisant dplyr? complete.cases avec 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))
82
user2503795

Essaye ça:

df %>% na.omit

ou ca:

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

ou ca:

library(tidyr)
df %>% drop_na

Si vous souhaitez filtrer en fonction du caractère manquant d'une variable, utilisez une condition:

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

ou

df %>% drop_na(x1)

D'autres réponses indiquent que parmi les solutions ci-dessus na.omit est beaucoup plus lent, mais il faut tenir compte du fait qu’il renvoie les index des lignes omises dans le na.action attribut 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É Avoir mis à jour pour refléter la dernière version de dplyr et ses commentaires.

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

158
G. Grothendieck

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 repère 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
26
Miha Trošt

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 en tant que 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
13
user2503795

Ceci est une fonction courte qui vous permet de spécifier des colonnes (essentiellement tout ce que dplyr::select Peut comprendre) qui ne devraient pas avoir de valeur NA (modélisées selon 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
10
Jan Katins

essaye ça

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

OU même cela

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

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

6
infominer

Juste pour être complet, dplyr::filter peut être évité tout en étant 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 filter et na.omit variantes (testé avec les micro-repères de @Miha Trošt).

3
mbask