在NA存在的情况下否定过滤条件会产生反直觉的结果

时间:2017-09-14 08:11:55

标签: r dplyr

我在一个关于大型数据帧的复杂语句中偶然发现了dplyr::filter的行为,这基本上归结为NA值的处理:

df <- tibble(a = c(rep(1,3), 
               rep(NA, 3)))

A tibble: 6 x 1
      a
  <dbl>
1     1
2     1
3     1
4    NA
5    NA
6    NA

过滤等于1的行会得到预期的结果:

df %>% filter(a == 1)

A tibble: 3 x 1
      a
  <dbl>
1     1
2     1
3     1

对不等于1的行进行过滤,我希望返回剩下的3行df,但情况并非如此:

df %>% filter(!a == 1)

A tibble: 0 x 1
 ... with 1 variables: a <dbl>

因此,在第一种情况下,NA被解释为不等于1,在第二种情况下,它被解释为等于1.是否存在我在这里缺少的逻辑?

我知道我可以使用%in%来获得预期的结果:

df %>% filter(!a %in% 1)

A tibble: 3 x 1
      a
  <dbl>
1    NA
2    NA
3    NA

但是我觉得使用这个运算符只有一个元素(而不是一个向量)似乎很奇怪。

所以我向专家提问:这是filter的预期行为吗?在否定过滤条件时使用%in%是否常见?

1 个答案:

答案 0 :(得分:4)

这是由于%in%的行为,而不是filter

让我们使用一个简单的例子:

a = c(1, 1, 1, NA, NA, NA)

> a == 1
[1] TRUE TRUE TRUE   NA   NA   NA
> a != 1
[1] FALSE FALSE FALSE    NA    NA    NA
> !(a == 1)
[1] FALSE FALSE FALSE    NA    NA    NA

我们看到当我们使用关系运算符==!=时,输入中的NA值在输出中保持为NA。然而...

> a %in% 1
[1]  TRUE  TRUE  TRUE FALSE FALSE FALSE
> !(a %in% 1)
[1] FALSE FALSE FALSE  TRUE  TRUE  TRUE

使用%in%运算符,输入中的NA值在输出中变为FALSE。由于这应该是match()更直观的界面,让我们来看看它:

> match(a, 1)
[1]  1  1  1 NA NA NA

所以不,match()本身并不是这样做的,至少不是默认参数。但是,帮助文件?match解释了:

  

%in%目前定义为
  "%in%" <- function(x, table) match(x, table, nomatch = 0) > 0

你有它。当我们使用a %in% 1时,我们实际上正在执行以下操作:

> match(a, 1, nomatch = 0L)
[1] 1 1 1 0 0 0

> match(a, 1, nomatch = 0L) > 0L
[1]  TRUE  TRUE  TRUE FALSE FALSE FALSE

因此,当filter()运算符与%in%否定一起使用时,!会返回带有NA值的行。