为什么 dplyr 的过滤器会从因子变量中删除 NA 值?
Why does dplyr's filter drop NA values from a factor variable?
当我使用 dplyr
包中的 filter
来降低因子变量的水平时,filter
也会降低 NA
值。这是一个例子:
library(dplyr)
set.seed(919)
(dat <- data.frame(var1 = factor(sample(c(1:3, NA), size = 10, replace = T))))
# var1
# 1 <NA>
# 2 3
# 3 3
# 4 1
# 5 1
# 6 <NA>
# 7 2
# 8 2
# 9 <NA>
# 10 1
filter(dat, var1 != 1)
# var1
# 1 3
# 2 3
# 3 2
# 4 2
这似乎不太理想——我只想删除 var1 == 1
.
处的行
看起来这是因为任何 ,然后 filter
掉落。因此,例如,filter(dat, !(var1 %in% 1))
会产生正确的结果。但是有没有办法告诉 filter
不要删除 NA
值?
你可以使用这个:
filter(dat, var1 != 1 | is.na(var1))
var1
1 <NA>
2 3
3 3
4 <NA>
5 2
6 2
7 <NA>
而且不会。
同样只是为了完成,删除 NA 是 filter
的预期行为,正如您从以下内容中看到的那样:
test_that("filter discards NA", {
temp <- data.frame(
i = 1:5,
x = c(NA, 1L, 1L, 0L, 0L)
)
res <- filter(temp, x == 1)
expect_equal(nrow(res), 2L)
})
上述测试取自 github 的 filter
测试。
我经常将 identical
映射到 mapply
...
(注意:我相信由于 R 3.6.0 的变化,set.seed
和 sample
最终得到不同的测试数据)
library(dplyr, warn.conflicts = FALSE)
set.seed(919)
(dat <- data.frame(var1 = factor(sample(c(1:3, NA), size = 10, replace = T))))
#> var1
#> 1 3
#> 2 1
#> 3 <NA>
#> 4 3
#> 5 1
#> 6 3
#> 7 2
#> 8 3
#> 9 2
#> 10 1
filter(dat, var1 != 1)
#> var1
#> 1 3
#> 2 3
#> 3 3
#> 4 2
#> 5 3
#> 6 2
filter(dat, !mapply(identical, as.numeric(var1), 1))
#> var1
#> 1 3
#> 2 <NA>
#> 3 3
#> 4 3
#> 5 2
#> 6 3
#> 7 2
它也适用于数字和字符串(可能更常见的用例)...
library(dplyr, warn.conflicts = FALSE)
set.seed(919)
(dat <- data.frame(var1 = sample(c(1:3, NA), size = 10, replace = T),
var2 = letters[sample(c(1:3, NA), size = 10, replace = T)],
stringsAsFactors = FALSE))
#> var1 var2
#> 1 3 <NA>
#> 2 1 a
#> 3 NA a
#> 4 3 b
#> 5 1 b
#> 6 3 <NA>
#> 7 2 a
#> 8 3 c
#> 9 2 <NA>
#> 10 1 b
filter(dat, !mapply(identical, var1, 1L))
#> var1 var2
#> 1 3 <NA>
#> 2 NA a
#> 3 3 b
#> 4 3 <NA>
#> 5 2 a
#> 6 3 c
#> 7 2 <NA>
filter(dat, !mapply(identical, var2, 'a'))
#> var1 var2
#> 1 3 <NA>
#> 2 3 b
#> 3 1 b
#> 4 3 <NA>
#> 5 3 c
#> 6 2 <NA>
#> 7 1 b
前面给出的答案很好,但是当你的过滤语句涉及到很多字段的函数时,变通方法可能就不那么好了。此外,谁想要使用 mapply
非矢量化 identical
。这是另一个更简单的解决方案,使用 coalesce
filter(dat, coalesce( var1 != 1, TRUE))
当我使用 dplyr
包中的 filter
来降低因子变量的水平时,filter
也会降低 NA
值。这是一个例子:
library(dplyr)
set.seed(919)
(dat <- data.frame(var1 = factor(sample(c(1:3, NA), size = 10, replace = T))))
# var1
# 1 <NA>
# 2 3
# 3 3
# 4 1
# 5 1
# 6 <NA>
# 7 2
# 8 2
# 9 <NA>
# 10 1
filter(dat, var1 != 1)
# var1
# 1 3
# 2 3
# 3 2
# 4 2
这似乎不太理想——我只想删除 var1 == 1
.
看起来这是因为任何 filter
掉落。因此,例如,filter(dat, !(var1 %in% 1))
会产生正确的结果。但是有没有办法告诉 filter
不要删除 NA
值?
你可以使用这个:
filter(dat, var1 != 1 | is.na(var1))
var1
1 <NA>
2 3
3 3
4 <NA>
5 2
6 2
7 <NA>
而且不会。
同样只是为了完成,删除 NA 是 filter
的预期行为,正如您从以下内容中看到的那样:
test_that("filter discards NA", {
temp <- data.frame(
i = 1:5,
x = c(NA, 1L, 1L, 0L, 0L)
)
res <- filter(temp, x == 1)
expect_equal(nrow(res), 2L)
})
上述测试取自 github 的 filter
测试。
我经常将 identical
映射到 mapply
...
(注意:我相信由于 R 3.6.0 的变化,set.seed
和 sample
最终得到不同的测试数据)
library(dplyr, warn.conflicts = FALSE)
set.seed(919)
(dat <- data.frame(var1 = factor(sample(c(1:3, NA), size = 10, replace = T))))
#> var1
#> 1 3
#> 2 1
#> 3 <NA>
#> 4 3
#> 5 1
#> 6 3
#> 7 2
#> 8 3
#> 9 2
#> 10 1
filter(dat, var1 != 1)
#> var1
#> 1 3
#> 2 3
#> 3 3
#> 4 2
#> 5 3
#> 6 2
filter(dat, !mapply(identical, as.numeric(var1), 1))
#> var1
#> 1 3
#> 2 <NA>
#> 3 3
#> 4 3
#> 5 2
#> 6 3
#> 7 2
它也适用于数字和字符串(可能更常见的用例)...
library(dplyr, warn.conflicts = FALSE)
set.seed(919)
(dat <- data.frame(var1 = sample(c(1:3, NA), size = 10, replace = T),
var2 = letters[sample(c(1:3, NA), size = 10, replace = T)],
stringsAsFactors = FALSE))
#> var1 var2
#> 1 3 <NA>
#> 2 1 a
#> 3 NA a
#> 4 3 b
#> 5 1 b
#> 6 3 <NA>
#> 7 2 a
#> 8 3 c
#> 9 2 <NA>
#> 10 1 b
filter(dat, !mapply(identical, var1, 1L))
#> var1 var2
#> 1 3 <NA>
#> 2 NA a
#> 3 3 b
#> 4 3 <NA>
#> 5 2 a
#> 6 3 c
#> 7 2 <NA>
filter(dat, !mapply(identical, var2, 'a'))
#> var1 var2
#> 1 3 <NA>
#> 2 3 b
#> 3 1 b
#> 4 3 <NA>
#> 5 3 c
#> 6 2 <NA>
#> 7 1 b
前面给出的答案很好,但是当你的过滤语句涉及到很多字段的函数时,变通方法可能就不那么好了。此外,谁想要使用 mapply
非矢量化 identical
。这是另一个更简单的解决方案,使用 coalesce
filter(dat, coalesce( var1 != 1, TRUE))