2016-08-15 2 views
4

Почему в коде ниже dplyr's filter не возвращает тот же файл data.frame, что и подмножество базы 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.cases для преобразования NA в 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: Loop/Функция

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) должны лучше справляться с этими типами NA, ведь они являются частью жизни. –

+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

Nice. Если 'isTRUE' были векторизованы, это было бы хорошим выражением этой логики. Наверное, можно заставить проблему «df%>% filter (! Vectorize (isTRUE) (b == 1 & c == 1))', но я не уверен, что это яснее. – alistaire