我想检查数据框两列中的值是否不匹配,并使用此信息创建新列。我想使用dplyr::mutate
,并且希望能够处理NA
值。可以使用以下代码生成一个简单的示例:
library(dplyr)
let <- c("a", "b", NA)
LET <- c("A")
perms <- expand.grid(
let_2 = let,
LET_2 = LET,
let_1 = let,
LET_1 = LET,
stringsAsFactors = FALSE
) %>%
.[ncol(.):1]
> perms
LET_1 let_1 LET_2 let_2
1 A a A a
2 A a A b
3 A a A <NA>
4 A b A a
5 A b A b
6 A b A <NA>
7 A <NA> A a
8 A <NA> A b
9 A <NA> A <NA>
然后,我要检查1
组中的参数是否与2
组中的相同参数不匹配。这是所需的输出:
> good_perms
LET_1 let_1 LET_2 let_2 LET_mismatch let_mismatch
1 A a A a FALSE FALSE
2 A a A b FALSE TRUE
3 A a A <NA> FALSE TRUE
4 A b A a FALSE TRUE
5 A b A b FALSE FALSE
6 A b A <NA> FALSE TRUE
7 A <NA> A a FALSE TRUE
8 A <NA> A b FALSE TRUE
9 A <NA> A <NA> FALSE FALSE
我认为下面的代码应该可以工作,但是它给出以下输出:
good_perms1 <- perms %>%
dplyr::mutate(LET_mismatch = !isTRUE(LET_1 == LET_2)) %>%
dplyr::mutate(let_mismatch = !isTRUE(let_1 == let_2))
> good_perms1
LET_1 let_1 LET_2 let_2 LET_mismatch let_mismatch
1 A a A a TRUE TRUE
2 A a A b TRUE TRUE
3 A a A <NA> TRUE TRUE
4 A b A a TRUE TRUE
5 A b A b TRUE TRUE
6 A b A <NA> TRUE TRUE
7 A <NA> A a TRUE TRUE
8 A <NA> A b TRUE TRUE
9 A <NA> A <NA> TRUE TRUE
此代码也无法提供所需的输出:
good_perms2 <- perms %>%
dplyr::mutate(LET_mismatch = isFALSE(LET_1 == LET_2)) %>%
dplyr::mutate(let_mismatch = isFALSE(let_1 == let_2))
> good_perms2
LET_1 let_1 LET_2 let_2 LET_mismatch let_mismatch
1 A a A a FALSE FALSE
2 A a A b FALSE FALSE
3 A a A <NA> FALSE FALSE
4 A b A a FALSE FALSE
5 A b A b FALSE FALSE
6 A b A <NA> FALSE FALSE
7 A <NA> A a FALSE FALSE
8 A <NA> A b FALSE FALSE
9 A <NA> A <NA> FALSE FALSE
如果使用下面的代码,则在定义值时会得到预期的结果,但会得到NA
而不是预期的结果:
FALSE
,其中一个值为NA
TRUE
,当两个值均为NA
good_perms2 <- perms %>%
dplyr::mutate(LET_mismatch = (LET_1 != LET_2)) %>%
dplyr::mutate(let_mismatch = (let_1 != let_2))
> good_perms2
LET_1 let_1 LET_2 let_2 LET_mismatch let_mismatch
1 A a A a FALSE FALSE
2 A a A b FALSE TRUE
3 A a A <NA> FALSE NA
4 A b A a FALSE TRUE
5 A b A b FALSE FALSE
6 A b A <NA> FALSE NA
7 A <NA> A a FALSE NA
8 A <NA> A b FALSE NA
9 A <NA> A <NA> FALSE NA
我意识到这里可能存在三个问题,但是第一个是我最困惑的问题:
dplyr::mutate
对!isTRUE
和TRUE
都将!isTRUE("a" == "a")
评估为!isTRUE("a" == "b")
?对于isFALSE
同样。NA == "a"
标识为FALSE
,将NA == NA
标识为TRUE
? NA
的问题可能需要单独解决,我现在主要关心的是为什么!isTRUE
的行为不符合dplyr::mutate
的预期。谢谢!
P.S。 This post谈到了这个问题,但是通过不同的方式解决了。
答案 0 :(得分:2)
也许用字符“ NA”替换NA
,运行代码,然后再用字符NA
替换字符“ NA”。
library(dplyr)
good_perms2 <- perms %>%
mutate_all(list(~replace(., is.na(.), "NA"))) %>%
mutate(LET_mismatch = (LET_1 != LET_2)) %>%
mutate(let_mismatch = (let_1 != let_2)) %>%
mutate_all(list(~replace(., . %in% "NA", NA_character_)))
good_perms2
# LET_1 let_1 LET_2 let_2 LET_mismatch let_mismatch
# 1 A a A a FALSE FALSE
# 2 A a A b FALSE TRUE
# 3 A a A <NA> FALSE TRUE
# 4 A b A a FALSE TRUE
# 5 A b A b FALSE FALSE
# 6 A b A <NA> FALSE TRUE
# 7 A <NA> A a FALSE TRUE
# 8 A <NA> A b FALSE TRUE
# 9 A <NA> A <NA> FALSE FALSE
答案 1 :(得分:1)
您遇到此问题是因为 isTRUE
和 isFALSE
不是矢量化函数。根据{{1}}:
isTRUE(x) 等同于 { is.logical(x) && length(x) == 1 && !is.na(x) && x }; isFALSE() 的定义类似。因此,由于 NA,if(isTRUE(cond)) 可能比 if(cond) 更可取。
了解我刚刚展示的内容后,让我们看看您的问题。
<块引用>?isTRUE
这里一切正常。现在,让我们对非标量对象进行同样的尝试。
x <- "a" == "a" # TRUE
y <- "a" == "b" # FALSE
!isTRUE(x)
#> [1] FALSE
!isTRUE(y)
#> [1] TRUE
!(is.logical(x) && length(x) == 1 && !is.na(x) && x)
#> [1] FALSE
!(is.logical(y) && length(y) == 1 && !is.na(y) && y)
#> [1] TRUE
如您所见,let_1 <- c("a", "a", "a", "b", "b", "b", NA, NA, NA)
let_2 <- c("a", "b", NA, "a", "b", NA, "a", "b", NA)
let_1 == let_2
#> [1] TRUE FALSE NA FALSE TRUE NA NA NA NA
!isTRUE(let_1 == let_2)
#> TRUE
x <- (let_1 == let_2)
!(is.logical(x) && length(x) == 1 && !is.na(x) && x)
#> TRUE
返回长度为 1 的对象。使用 isTRUE
时,函数会将值回收到所有元素,这就是所有 mutate
等于 let_mismatch
的原因。
@www 已经展示了解决这个问题的方法。更好的方法是使用矢量化 if-else,例如 TRUE
。
您可以使用函数来执行此操作,但这不是最好的方法(下面的示例)。如果您想直接分配逻辑表达式(也不是解决您的问题的最佳方法),您还可以创建一个处理表达式的函数。
dplyr::case_when()
这是使用您展示的示例的 library(dplyr)
foo <- function(x, y) {
case_when(
is.na(x) & !is.na(y) ~ FALSE,
is.na(x) & is.na(y) ~ TRUE)
}
foo(NA, "a")
#> [1] FALSE
foo(NA, NA)
#> [1] TRUE
解决方案。
case_when()
答案 2 :(得分:1)
添加rowwise()
good_perms1 <- perms %>% rowwise() %>%
dplyr::mutate(LET_mismatch = !isTRUE(LET_1 == LET_2)) %>%
dplyr::mutate(let_mismatch = !isTRUE(let_1 == let_2))