在使用子集或 dplyr::filter 时过滤 R 中的行意外删除 NA

Filtering rows in R unexpectedly removes NAs when using subset or dplyr::filter

我有一个数据集 df,我想删除变量 y 没有值 a 的所有行。变量 y 还包含一些 NAs:

df <- data.frame(x=1:3, y=c('a', NA, 'c'))

我可以使用 R 的索引语法实现这一点,如下所示:

df[df$y!='a',]

  x    y
  2 <NA>
  3    c

请注意 returns NA 和值 c - 这正是我想要的。

但是,当我使用 subsetdplyr::filter 尝试相同的操作时,NA 被删除:

subset(df, y!='a')

  x    y
  3    c

dplyr::filter(df, y!='a')
  x    y
  3    c

为什么 subsetdplyr::filter 是这样工作的?这对我来说似乎不合逻辑 - NAa 不同,所以当我指定我想要除变量 y 等于的那些行之外的所有行时,为什么要删除 NA a?

有什么方法可以改变这些函数的行为,而不是明确要求 NAs 返回,即

subset(df, y!='a' | is.na(y))

谢谢

一种解决方法是使用 %in%:

subset(df, !y %in% "a")
dplyr::filter(df, !y %in% "a")

您的“预期”行为示例实际上 return 您在问题中显示的内容。我得到:

> df[df$y != 'a',]
    x    y
NA NA <NA>
3   3    c

可以说,这比 subsetdplyr::filter return 的错误 。请记住,在 R 中,NA 的真正意思是“未知”,因此 df$y != 'a' returns,

> df$y != 'a'
[1] FALSE    NA  TRUE

所以 R 被告知您绝对不想要第一行,您确实想要最后一行,但是您是否想要第二行实际上是“未知”的。结果,它包括一行所有 NAs.

很多人不喜欢这种行为,但事实就是如此。

subsetdplyr::filter 做出不同的默认选择,即简单地删除 NA 行,这可以说是准确的。

但实际上,这里的教训是,如果你的数据有 NAs,那只意味着你需要在所有点上围绕它进行防御性编码,或者通过使用像 is.na(df$y) | df$y != 'a' 这样的条件,或者如另一个答案中所述,使用基于 match.

%in%

来自 base::Extract:

When extracting, a numerical, logical or character NA index picks an unknown element and so returns NA

来自 ?base::subset:

missing values are taken as false [...] For ordinary vectors, the result is simply x[subset & !is.na(subset)]

来自?dplyr::filter

Unlike base subsetting with [, rows where the condition evaluates to NA are dropped