lọc các trường hợp hoàn chỉnh trong data.frame bằng cách sử dụng dplyr (xóa theo từng trường hợp)


97

Có thể lọc data.frame cho các trường hợp hoàn chỉnh bằng dplyr không? complete.casesvới một danh sách tất cả các biến hoạt động, tất nhiên. Nhưng đó là a) dài dòng khi có rất nhiều biến và b) không thể khi tên biến không được biết (ví dụ: trong một hàm xử lý bất kỳ khung dữ liệu nào).

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.caseskhông chỉ chấp nhận vectơ. Nó cũng cần toàn bộ khung dữ liệu.
joran

Nhưng điều đó không hoạt động như một phần của dplyrchức năng bộ lọc của. Tôi đoán tôi đã không đủ rõ ràng và cập nhật câu hỏi của mình.
user2503795 Ngày

1
Sẽ hữu ích nếu bạn có thể chứng minh chính xác cách nó không hoạt động với dplyr, nhưng khi tôi thử nó với bộ lọc, nó hoạt động tốt.
Joran

Câu trả lời:


185

Thử cái này:

df %>% na.omit

hoặc cái này:

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

hoặc cái này:

library(tidyr)
df %>% drop_na

Nếu bạn muốn lọc dựa trên sự thiếu hụt của một biến, hãy sử dụng điều kiện:

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

hoặc là

df %>% drop_na(x1)

Các câu trả lời khác chỉ ra rằng các giải pháp ở trên na.omitchậm hơn nhiều nhưng điều đó phải được cân bằng với thực tế là nó trả về chỉ số hàng của các hàng bị bỏ qua trong na.actionthuộc tính trong khi các giải pháp khác ở trên thì không.

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"

ADDED Đã cập nhật để phản ánh phiên bản mới nhất của dplyr và nhận xét.

ADDED Đã cập nhật để phản ánh phiên bản mới nhất của sắp xếp và nhận xét.


Chỉ cần quay lại để trả lời và thấy câu trả lời hữu ích của bạn!
infominer

1
Cảm ơn! Tôi đã thêm một số kết quả điểm chuẩn. na.omit()hoạt động khá kém nhưng nhanh.
user2503795

1
Điều này bây giờ làm việc cũng như: df %>% filter(complete.cases(.)). Không chắc liệu những thay đổi gần đây trong dplyr có thực hiện được điều này hay không.
user2503795

Như @ Jan-katins điểm ra, chức năng Tidyverse được gọi drop_na, vì vậy bây giờ bạn có thể làm: df %>% drop_na().
cbrnr

26

Điều này phù hợp với tôi:

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

Hoặc tổng quát hơn một chút:

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

Điều này sẽ có lợi thế là dữ liệu có thể đã được sửa đổi trong chuỗi trước khi chuyển nó đến bộ lọc.

Một điểm chuẩn khác với nhiều cột hơn:

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
Tôi đã cập nhật câu trả lời của bạn bằng "." trong complete.cases và benchmark thêm - hy vọng bạn không nhớ :-)
Talat

:) Tôi không. Cảm ơn bạn.
Miha Trošt

1
Tôi nhận thấy df %>% slice(which(complete.cases(.)))hoạt động nhanh hơn ~ 20% so với phương pháp tiếp cận bộ lọc trong điểm chuẩn ở trên.
talat

Cần lưu ý rằng nếu bạn đang sử dụng bộ lọc này trong một đường ống dplyr với các lệnh dplyr khác (chẳng hạn như group_by ()), bạn sẽ cần phải thêm %>% data.frame() %>%trước khi thử và lọc trên hoàn thành. Vali (.) Vì nó sẽ không hoạt động trên đá cuội hoặc đá cuội nhóm hoặc thứ gì đó. Hoặc ít nhất, đó là kinh nghiệm mà tôi đã có.
C. Denney

16

Dưới đây là một số kết quả tiêu chuẩn cho câu trả lời của Grothendieck. na.omit () mất thời gian gấp 20 lần so với hai giải pháp còn lại. Tôi nghĩ sẽ rất tuyệt nếu dplyr có một chức năng cho điều này có thể là một phần của bộ lọc.

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

Đây là một hàm ngắn cho phép bạn chỉ định các cột (về cơ bản là mọi thứ dplyr::selectcó thể hiểu được) không được có bất kỳ giá trị NA nào (được mô hình hóa theo 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 hiện là một phần của ngăn nắp : phần trên có thể được thay thế bằng library("tidyr")]

Ví dụ:

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

Sẽ không hữu ích hơn nếu có thể thêm một ngưỡng như 0,5 và xử lý nó theo từng cột? Trường hợp: loại bỏ các biến có dữ liệu bị thiếu từ 50% trở lên. Ví dụ: data [, -which (colMeans (is.na (data))> 0.5)] Sẽ rất tuyệt nếu bạn có thể làm điều này với slimr.
Monduiz

@Monduiz Điều này có nghĩa rằng việc bổ sung các dữ liệu hơn (trong đó một biến sau đó có rất nhiều NA) có thể không bước tiếp theo trong các đường ống vì một biến cần thiết hiện nay là mất tích ...
Jan Katins

Đúng, có lý.
Monduiz

6

thử cái này

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

HOẶC thậm chí cái này

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

Các lệnh trên có nhiệm vụ kiểm tra tính đầy đủ cho tất cả các cột (biến) trong data.frame của bạn.


Cảm ơn. Tôi đoán tôi không đủ rõ ràng mặc dù (câu hỏi được cập nhật). Tôi biết về complete.case (df) nhưng tôi muốn làm điều đó với dplyrnhư một phần của chức năng bộ lọc. Điều đó sẽ cho phép tích hợp gọn gàng trong chuỗi dplyr, vv
user2503795

Kiểm tra câu trả lời của @ G.Grothendieck
infominer Ngày

Trong dplyr:::do.data.framecâu lệnh env$. <- .datathêm dấu chấm vào môi trường. Không có tuyên bố nào như vậy trong magrittr :: "%>%" `
G. Grothendieck

Xin lỗi chắc đã nhập bình luận sai chỗ.
G. Grothendieck

3

Chỉ vì mục đích hoàn chỉnh, dplyr::filtercó thể tránh hoàn toàn nhưng vẫn có thể tạo chuỗi chỉ bằng cách sử dụng magrittr:extract(bí danh của [):

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

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

Phần thưởng bổ sung là tốc độ, đây là phương pháp nhanh nhất trong số các phương pháp filterna.omitcác biến thể (được thử nghiệm bằng cách sử dụng @Miha Trošt microbenchmarks).


Khi tôi làm điểm chuẩn với dữ liệu của Miha Trošt, tôi thấy rằng việc sử dụng extract()chậm hơn gần mười lần filter(). Tuy nhiên, khi tôi tạo một khung dữ liệu nhỏ hơn với df <- df[1:100, 1:10], hình ảnh sẽ thay đổi và extract()nhanh nhất.
Stibu

Bạn nói đúng. Có vẻ như đây magrittr::extractlà cách nhanh nhất chỉ khi n <= 5e3ở trong tiêu chuẩn Miha Trošt.
mbask
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.