dplyr kullanarak data.frame'deki tam vakalar için filtre (vaka bazında silme)


101

Dplyr kullanarak tam vakalar için bir data.frame'i filtrelemek mümkün müdür? complete.caseselbette tüm değişkenlerin bir listesi ile çalışır. Ancak bu a) çok sayıda değişken olduğunda ayrıntılıdır ve b) değişken adları bilinmediğinde (örneğin herhangi bir data.frame'i işleyen bir işlevde) imkansızdır.

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.casessadece vektörleri kabul etmez. Tüm veri çerçevelerini de alır.
joran

Ancak bu, dplyr'nin filtre işlevinin bir parçası olarak çalışmaz. Sanırım yeterince net değildim ve sorumu güncelledim.
user2503795

1
Bunun dplyr ile tam olarak nasıl çalışmadığını gösterebilmeniz yardımcı olur, ancak filtre ile denediğimde gayet iyi çalışıyor.
joran

Yanıtlar:


189

Bunu dene:

df %>% na.omit

veya bu:

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

veya bu:

library(tidyr)
df %>% drop_na

Bir değişkenin eksikliğine göre filtrelemek istiyorsanız, bir koşul kullanın:

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

veya

df %>% drop_na(x1)

Diğer yanıtlar, yukarıdaki çözümlerin na.omitçok daha yavaş olduğunu, ancak bunun, na.actionöznitelikte atlanan satırların satır endekslerini döndürdüğü gerçeğine karşı dengelenmesi gerektiğini, oysa yukarıdaki diğer çözümlerin olmadığını gösterir.

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"

EKLENDİ dplyr'ın en son sürümünü ve yorumları yansıtacak şekilde güncellendi.

EKLENDİ Düzenlemenin en son sürümünü ve yorumları yansıtacak şekilde güncellendi.


Sadece cevaplamak için geri döndüm ve faydalı cevabınızı gördüm!
infominer

1
Teşekkürler! Bazı kıyaslama sonuçları ekledim. na.omit()oldukça kötü bir performans sergiliyor ancak hızlı.
user2503795

1
Bu sorun da çalışır: df %>% filter(complete.cases(.)). Dplyr'deki son değişikliklerin bunu mümkün kılıp kılmadığından emin değilim.
user2503795

Dışarı Ocak-Katins noktaları @ gibi, Tidyverse fonksiyonu denir drop_naşimdi bunu yapabilirsiniz,: df %>% drop_na().
cbrnr

26

Bu benim için çalışıyor:

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

Veya biraz daha genel:

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

Bu, verilerin zincirde filtreye geçirilmeden önce değiştirilebilmesi avantajına sahip olacaktır.

Daha fazla sütun içeren başka bir kıyaslama:

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
Cevabınızı "" ile güncelledim. eksiksiz. vakalarda ve kriter eklendi - umarım aldırmazsın :-)
talat

:) Yapmıyorum. Teşekkür ederim.
Miha Trošt

1
df %>% slice(which(complete.cases(.)))Yukarıdaki karşılaştırmada filtre yaklaşımından ~% 20 daha hızlı performans gösterdiğini buldum .
talat

Bu filtreyi bir dplyr borusunda diğer dplyr komutlarıyla (group_by () gibi) kullanıyorsanız, %>% data.frame() %>%complete.cases (.) Üzerinde denemeyi ve filtrelemeyi denemeden önce eklemeniz gerekeceğini belirtmek gerekir çünkü üzerinde çalışmayacaktır. tibbles veya gruplanmış tibbles falan. Ya da en azından, yaşadığım deneyim buydu.
C.Denney

16

İşte Grothendieck'in cevabı için bazı kıyaslama sonuçları. na.omit (), diğer iki çözümden 20 kat daha fazla zaman alır. Dplyr'in bunun için belki filtrenin bir parçası olarak bir işlevi olsaydı iyi olurdu.

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

Bu, dplyr::selectherhangi bir NA değeri içermeyen sütunları (temelde anlayabilen her şeyi) belirlemenizi sağlayan kısa bir işlevdir (pandas df.dropna () 'dan sonra modellenmiştir ):

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 artık tidyr'in bir parçası : yukarıdakiler ile değiştirilebilir library("tidyr")]

Örnekler:

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 gibi bir kesme noktası ekleyip sütunlara göre işleyebilmek daha da kullanışlı olmaz mıydı? Durum:% 50 ve üzeri eksik veriye sahip değişkenleri eleyin. Örnek: data [, -which (colMeans (is.na (data))> 0.5)] Bunu tidyr ile yapabilmek güzel olurdu.
Monduiz

@Monduiz Bu, daha fazla verinin eklenmesinin (bir değişkenin çok fazla NA'sı olduğu durumlarda) boru hattındaki bir sonraki adımı geçemeyebileceği anlamına gelir çünkü gerekli bir değişken artık eksik ...
Jan Katins

Doğru, mantıklı.
Monduiz

6

bunu dene

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

VEYA bu bile

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

Yukarıdaki komutlar, data.frame'inizdeki tüm sütunların (değişken) eksiksizliğini kontrol etmeye özen gösterir.


Teşekkürler. Sanırım yeterince net değildim (soru güncellendi). Complete.cases (df) hakkında bilgim var ama bunu dplyrfiltre işlevinin bir parçası olarak yapmak istiyorum . Bu, dplyr zincirlerinde vb. Düzgün bir entegrasyona izin verir.
user2503795

Cevabı @ G.Grothendieck tarafından kontrol edin
infominer

Gelen dplyr:::do.data.frameaçıklamaya env$. <- .dataçevreye nokta ekler. Magrittr :: "%>%" içinde böyle bir ifade yok ``
G. Grothendieck

Üzgünüm, yorumu yanlış yere girmiş olmalı.
G. Grothendieck

3

Sadece bütünlük uğruna, tamamen dplyr::filterönlenebilir , ancak yine de yalnızca magrittr:extract(bir takma ad [) kullanarak zincirler oluşturabilir :

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

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

Ek bonus hızdır, bu filterve na.omitçeşitleri arasında en hızlı yöntemdir (@Miha Trošt microbenchmark kullanılarak test edilmiştir).


Miha Trošt tarafından hazırlanan verilerle kıyaslama yaptığımda, kullanmanın extract()neredeyse on kat daha yavaş olduğunu görüyorum filter(). Bununla birlikte, ile daha küçük bir veri çerçevesi oluşturduğumda df <- df[1:100, 1:10], resim değişiyor ve extract()en hızlısı.
Stibu

Haklısın. Sadece Miha Trošt kıyaslamasındayken magrittr::extracten hızlı yol gibi görünüyor n <= 5e3.
mbask
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.