2016-08-15 70 views
4

爲什麼在下面的代碼中dplyr的filter不會返回與基本R子集相同的數據幀?由於NA的原因,無法對數據幀進行子集(過濾)

事實上,他們沒有按預期工作。我想刪除觀察/行,其中,同時,b==1 AND c==1。也就是說,我只想刪除第三行。

require(dplyr) 
df <- data.frame(a=c(0,0,0,0,1,1,1), b=c(0,0,1,1,0,0,1), c=c(1,NA,1,NA,1,NA,NA)) 

filter(df, !(b==1 & c==1)) 

df[!(df$b==1 & df$c==1),] 

回答

3

或者使用complete.casesNA轉換爲FALSE結果中的邏輯載體,使您可以選擇相應的行了否定後,這使用的事實,NA & F = F

filter(df, !(b == 1 & c == 1 & complete.cases(df[c('b', 'c')]))) 

# a b c 
# 1 0 0 1 
# 2 0 0 NA 
# 3 0 1 NA 
# 4 1 0 1 
# 5 1 0 NA 
# 6 1 1 NA 

更符合邏輯此處涉及NA的操作,乍一看有點混淆,但他們遵循以下邏輯:

NA & F 
# [1] FALSE 
NA | T 
# [1] TRUE 
NA & T 
# [1] NA 
NA | F 
# [1] NA 
1

是的,NA值會導致問題。下面是4級的解決方法:

方法1:2-步驟排除

n <- (df$b+df$c==2) 
df[n %in% c(NA, "FALSE"),] 
a b c 
1 0 0 1 
2 0 0 NA 
4 0 1 NA 
5 1 0 1 
6 1 0 NA 
7 1 1 NA 

方法2:條件求和

df[!(complete.cases(df$b,df$c) & df$b+df$c == 2),] 
a b c 
1 0 0 1 
2 0 0 NA 
4 0 1 NA 
5 1 0 1 
6 1 0 NA 
7 1 1 NA 

方法3:循環/功能

filterwithNA <- function(df,n){ 
    for(i in 1:nrow(df)){ 
    if(!is.na(df$b[i]) & !(is.na(df$c[i]))){ 
     if(df$b[i] == n & df$c[i] == n){ 
     df <- df[-i,] 
     } 
    } 
    } 
    return(df) 
} 

filterwithNA(df, n=1) 
a b c 
1 0 0 1 
2 0 0 NA 
4 0 1 NA 
5 1 0 1 
6 1 0 NA 
7 1 1 NA 

方法4:臨時數字替換

df[is.na(df)] <- 999 

df[!(df$b==1 & df$c==1),] 
df[df==999] <- NA 
df 
a b c 
1 0 0 1 
2 0 0 NA 
4 0 1 NA 
5 1 0 1 
6 1 0 NA 
7 1 1 NA 
+0

這是一個解決方案,@ Hack-R。我認爲這不是最好的,但它的工作原理。在我看來,R(和dplyr)應該更好地處理這些類型的NAs,畢竟它們是生活的一部分。 –

+0

@RodrigoRemedio是的,「NA」一直都是問題所在。相信我,我明白。 –

+0

@RodrigoRemedio我又增加了2個解決方案 –

2

使用data.table

library(data.table) 
setDT(df)[df[,!(b==1 & c== 1& complete.cases(.SD[, c('b', 'c'), with = FALSE]))]] 
# a b c 
#1: 0 0 1 
#2: 0 0 NA 
#3: 0 1 NA 
#4: 1 0 1 
#5: 1 0 NA 
#6: 1 1 NA 
3

這是我能想到的最簡單的選擇:

filter(df, !((b==1 & c==1) %in% TRUE)) 
# a b c 
#1 0 0 1 
#2 0 0 NA 
#3 0 1 NA 
#4 1 0 1 
#5 1 0 NA 
#6 1 1 NA 

# or equivalently in data.table 
dt[!((b==1 & c==1) %in% TRUE)] 

另一個,也許更詳細的/清除選項是使用!(b==1 & c==1) | is.na(b+c)作爲比較。

+0

不錯。如果'isTRUE'是矢量化的,這將是這個邏輯的一個很好的表達。我想可以強制問題'df%>%filter(!Vectorize(isTRUE)(b == 1&c == 1))',但我不確定這是否更清晰。 – alistaire