กรองสำหรับกรณีที่สมบูรณ์ใน data.frame โดยใช้ dplyr (การลบกรณีที่ชาญฉลาด)


101

เป็นไปได้ไหมที่จะกรอง data.frame สำหรับกรณีทั้งหมดโดยใช้ dplyr complete.casesด้วยรายการตัวแปรทั้งหมดที่ใช้งานได้แน่นอน แต่นั่นคือ a) verbose เมื่อมีตัวแปรจำนวนมากและ b) เป็นไปไม่ได้เมื่อไม่รู้จักชื่อตัวแปร (เช่นในฟังก์ชันที่ประมวลผล 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.casesไม่เพียงแค่ยอมรับเวกเตอร์ ต้องใช้กรอบข้อมูลทั้งหมดด้วย
joran

แต่นั่นใช้ไม่ได้เป็นส่วนหนึ่งของdplyrฟังก์ชันตัวกรอง ฉันเดาว่าฉันไม่ชัดเจนพอและอัปเดตคำถามของฉัน
user2503795

1
มันจะช่วยได้ถ้าคุณสามารถแสดงให้เห็นว่ามันใช้ไม่ได้กับ dplyr แต่เมื่อฉันลองใช้กับฟิลเตอร์มันก็ใช้ได้ดี
joran

คำตอบ:


189

ลองสิ่งนี้:

df %>% na.omit

หรือสิ่งนี้:

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

หรือสิ่งนี้:

library(tidyr)
df %>% drop_na

หากคุณต้องการกรองตามความหายไปของตัวแปรหนึ่งให้ใช้เงื่อนไข:

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

หรือ

df %>% drop_na(x1)

คำตอบอื่น ๆ ระบุว่าโซลูชันข้างต้นna.omitช้ากว่ามาก แต่จะต้องสมดุลกับข้อเท็จจริงที่ว่ามันส่งคืนดัชนีแถวของแถวที่ละไว้ในna.actionแอตทริบิวต์ในขณะที่โซลูชันอื่น ๆ ข้างต้นไม่ทำ

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มีการอัปเดตเพื่อแสดงเวอร์ชันล่าสุดของ dplyr และความคิดเห็น

ADDEDมีการอัปเดตเพื่อแสดงเวอร์ชันล่าสุดของ tidyr และความคิดเห็น


เพิ่งกลับมาตอบและเห็นคำตอบที่เป็นประโยชน์ของคุณ!
infominer

1
ขอบคุณ! ฉันเพิ่มผลการเปรียบเทียบบางส่วน na.omit()ทำงานได้ค่อนข้างแย่ แต่ก็เร็ว
user2503795

1
ตอนนี้ใช้งานได้เช่นกัน: df %>% filter(complete.cases(.)). ไม่แน่ใจว่าการเปลี่ยนแปลงล่าสุดใน dplyr ทำให้สิ่งนี้เป็นไปได้หรือไม่
user2503795

ในฐานะที่เป็น @ Jan-katins คะแนนจากฟังก์ชั่นที่เรียกว่า Tidyverse ดังนั้นตอนนี้คุณสามารถทำได้:drop_na df %>% drop_na()
cbrnr

26

สิ่งนี้ใช้ได้กับฉัน:

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

หรือทั่วไปเล็กน้อย:

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

สิ่งนี้จะมีข้อได้เปรียบที่สามารถแก้ไขข้อมูลในห่วงโซ่ก่อนที่จะส่งต่อไปยังตัวกรอง

เกณฑ์มาตรฐานอื่นที่มีคอลัมน์เพิ่มเติม:

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
ฉันอัปเดตคำตอบของคุณด้วย "." ใน complete.cases และเกณฑ์มาตรฐานที่เพิ่มเข้ามา - หวังว่าคุณจะไม่รังเกียจ :-)
Talat

:) ฉันไม่. ขอบคุณ.
Miha Trošt

1
ฉันพบว่า df %>% slice(which(complete.cases(.)))ทำงานเร็วกว่าวิธีการกรองในเกณฑ์มาตรฐานด้านบนประมาณ 20%
Talat

เป็นที่น่าสังเกตว่าหากคุณใช้ตัวกรองนี้ในท่อ dplyr ร่วมกับคำสั่ง dplyr อื่น ๆ (เช่น group_by ()) คุณจะต้องเพิ่ม%>% data.frame() %>%ก่อนที่จะลองและกรองใน complete.cases (.) เพราะมันจะไม่ทำงาน tibbles หรือ tibbles ที่จัดกลุ่มหรือบางสิ่งบางอย่าง หรืออย่างน้อยนั่นก็เป็นประสบการณ์ที่ฉันมี
C. Denney

16

นี่คือผลลัพธ์มาตรฐานบางส่วนสำหรับการตอบกลับของ Grothendieck na.omit () ใช้เวลามากกว่าอีกสองวิธีถึง 20 เท่า ฉันคิดว่ามันคงจะดีถ้า dplyr มีฟังก์ชั่นนี้อาจเป็นส่วนหนึ่งของตัวกรอง

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

นี่เป็นฟังก์ชันสั้น ๆ ที่ให้คุณระบุคอลัมน์ (โดยทั่วไปทุกอย่างที่dplyr::selectเข้าใจได้) ซึ่งไม่ควรมีค่า NA ใด ๆ (จำลองตามแพนด้า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 เป็นส่วนหนึ่งของ tidyr แล้ว : ด้านบนสามารถแทนที่ได้ด้วยlibrary("tidyr")]

ตัวอย่าง:

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 และประมวลผลตามคอลัมน์ได้หรือไม่? กรณี: กำจัดตัวแปรที่มีข้อมูลขาดหายไป 50% ตัวอย่าง: data [, -which (colMeans (is.na (data))> 0.5)] มันจะเป็นการดีที่สามารถทำได้ด้วย tidyr
Monduiz

@Monduiz นี่หมายความว่าการเพิ่มข้อมูลมากขึ้น (โดยที่ตัวแปรมี NA จำนวนมาก) อาจล้มเหลวในขั้นตอนต่อไปในไปป์ไลน์เพราะตอนนี้ตัวแปรที่ต้องการหายไป ...
Jan Katins

ใช่แล้วที่สมเหตุสมผล
Monduiz

6

ลองดู

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

หรือแม้แต่สิ่งนี้

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

คำสั่งข้างต้นดูแลการตรวจสอบความสมบูรณ์ของคอลัมน์ทั้งหมด (ตัวแปร) ใน data.frame ของคุณ


ขอบคุณ. ฉันเดาว่าฉันไม่ชัดเจนพอ (อัปเดตคำถามแล้ว) ฉันรู้เกี่ยวกับ complete.cases (df) แต่ฉันต้องการทำโดยdplyrเป็นส่วนหนึ่งของฟังก์ชันตัวกรอง ซึ่งจะช่วยให้สามารถรวมเข้ากับโซ่ dplyr ได้อย่างเป็นระเบียบ
user2503795

ตรวจคำตอบโดย @ G.Grothendieck
infominer

ในdplyr:::do.data.frameคำสั่งenv$. <- .dataเพิ่มจุดให้กับสภาพแวดล้อม ไม่มีข้อความดังกล่าวใน magrittr :: "%>%" `
G. Grothendieck

ขออภัยต้องป้อนความคิดเห็นผิดตำแหน่ง
G.Grothendieck

3

เพื่อความสมบูรณ์dplyr::filterสามารถหลีกเลี่ยงได้ทั้งหมด แต่ยังคงสามารถสร้างโซ่ได้โดยใช้magrittr:extract(นามแฝงของ[):

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

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

โบนัสเพิ่มเติมคือความเร็วซึ่งเป็นวิธีที่เร็วที่สุดในบรรดาfilterและna.omitตัวแปรต่างๆ (ทดสอบโดยใช้ไมโครเบนช์มาร์กของ @Miha Trošt)


เมื่อฉันทำมาตรฐานที่มีข้อมูลโดย Miha Trost ที่ฉันพบว่าการใช้เป็นเวลาสิบเกือบช้ากว่าextract() filter()อย่างไรก็ตามเมื่อฉันสร้าง data frame ที่เล็กลงdf <- df[1:100, 1:10]ภาพจะเปลี่ยนไปและextract()เร็วที่สุด
Stibu

คุณถูก. ดูเหมือนว่าmagrittr::extractจะเป็นวิธีที่เร็วที่สุดเมื่อn <= 5e3อยู่ในเกณฑ์มาตรฐานของ Miha Troštเท่านั้น
mbask
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.