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

use*_*194 3 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
Run Code Online (Sandbox Code Playgroud)

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

df %>% filter(a == 1)

A tibble: 3 x 1
      a
  <dbl>
1     1
2     1
3     1
Run Code Online (Sandbox Code Playgroud)

过滤不等于 1 的行,我希望返回 df 的剩余 3 行,但事实并非如此:

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

A tibble: 0 x 1
 ... with 1 variables: a <dbl>
Run Code Online (Sandbox Code Playgroud)

因此,虽然在第一种情况下NA被解释为不等于 1,但在第二种情况下,它被解释为等于 1。我在这里缺少逻辑吗?

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

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

A tibble: 3 x 1
      a
  <dbl>
1    NA
2    NA
3    NA
Run Code Online (Sandbox Code Playgroud)

但对我来说,仅将这个运算符与一个元素(而不是向量)一起使用似乎很奇怪。

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

Z.L*_*Lin 5

这是由于 的行为造成的%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
Run Code Online (Sandbox Code Playgroud)

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

> a %in% 1
[1]  TRUE  TRUE  TRUE FALSE FALSE FALSE
> !(a %in% 1)
[1] FALSE FALSE FALSE  TRUE  TRUE  TRUE
Run Code Online (Sandbox Code Playgroud)

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

> match(a, 1)
[1]  1  1  1 NA NA NA
Run Code Online (Sandbox Code Playgroud)

所以不,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
Run Code Online (Sandbox Code Playgroud)

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