Why does rlang::quo_is_missing(quo(NA)) evaluate to FALSE? - r

If I understand, rlang::quo_is_missing evaluates a quosure and checks whether it contains a missing value. If it does, it should return TRUE, FALSE if not. Yet, I've tried the following combinations and it always returns FALSE:
rlang::quo_is_missing(quo(NA))
rlang::quo_is_missing(quo(NA_character_))
rlang::quo_is_missing(quo(NA_integer_))
If I try non-NA values, it also returns FALSE, as expected:
rlang::quo_is_missing(quo("hello"))
Why is it returning FALSE when the value is obviously missing?

"Missing" is a special term that refers to values that are not present at all. NA is not the same as "missing" -- NA is itself a value. In base R you can compare the functions is.na() and missing() each of which do different things. quo_is_missing is like the missing() function, not is.na and returns true only when there is no value at all:
rlang::quo_is_missing(quo())
If you want to check for NA, you could write a helper
quo_is_na <- function(x) {
!rlang::quo_is_symbolic(x) &&
!rlang::quo_is_missing(x) &&
!rlang::quo_is_null(x) &&
is.na(rlang::quo_get_expr(x))
}
quo_is_na(quo())
# [1] FALSE
quo_is_na(quo(x+y))
# [1] FALSE
quo_is_na(quo(NULL))
# [1] FALSE
quo_is_na(quo(42))
# [1] FALSE
quo_is_na(quo(NA))
# [1] TRUE
quo_is_na(quo(NA_character_))
# [1] TRUE

Related

How to check if an element exist in a vector when the element may be NULL using base r functions

Assume we have a vector and want to check if a variable is part of that vector. This is going to be used in a function as a error handling and the input can also be NULL
values <- c("ALL", "NON")
parameter <- "ALL"
the easy solution is to use is.element() or %in% but the parameter can be NULL and in that case we get an error in if()
parameter <- NULL
is.element(parameter, values)
logical(0)
if(is.element(parameter, values)){stop("invalid parameter")}
Error in if (is.element(parameter, values)) { :
argument is of length zero
If you want to use %in% you can reverse the call and use any on top :
any(c("ALL", "NON") %in% NULL) # [1] FALSE
c("ALL", "NON") %in% NULL returns a logical of length 2 so we need anyto get the output.
Another possibility is to use purrr::has_element, which handles this case :
library(purrr)
has_element(c("ALL", "NON"), NULL) # [1] FALSE
It's also stricter :
has_element(1, "1") # [1] FALSE
is.elements(1, "1") # [1] TRUE
has_element(list(iris), list(as.list(iris))) # [1] FALSE
is.element(list(iris), list(as.list(iris))) # [1] TRUE
There are multiple ways to check. The exact function to check is is.null which returns TRUE if it is NULL
!is.null(parameter) && is.element(parameter, values)
Other option is length which returns 0 for NULL
length(parameter) > 0 && is.element(parameter, values)
NOTE: Both the methods evaluate the second argument only if the first condition is TRUE. So it will return FALSE whenever 'parameter' is NULL. We can evaluate this in the same argument order as in the OP's is.element as
1:3 %in% 2
#[1] FALSE TRUE FALSE
2 %in% 1:3
#[1] TRUE
returns different results.
NOTE: No packages used and here we assume that the OP pass vectors of same type

Using NOT (!) operator in R with numbers

I'm trying to understand the ! operator better in R, and I'm confused as to how it applies to numbers. What does the following code signify, and why are the two equality queries not the same?
> !5 == 7
[1] TRUE
> 5 == !7
[1] FALSE
> !5
[1] FALSE
Thanks!
First of all: the ! operator coerces non-logicals to logical, then reverses them. Anything other than 0 evaluates to a logical TRUE, then the ! operator flips it to FALSE
The rest has to do with order of operations.
!5 == 7
Evaluates to
!(5==7)
Which is equivalent to
!(FALSE)
Which returns TRUE
Whereas
5 == !7
Evaluates to
5 == FALSE
Which returns FALSE
The equivalent to 5 == !7 would be (!5) == 7 (Both return FALSE)
The ! coerces its argument to a logical, thus:
as.logical(-3L:3L)
# [1] TRUE TRUE TRUE FALSE TRUE TRUE TRUE
as.logical(seq(-2,2, by = 0.5))
# [1] TRUE TRUE TRUE TRUE FALSE TRUE TRUE TRUE TRUE
As you can see, 0 is FALSE, everything else is TRUE.
To get an even better sense of this, see that ! is - like everything in R - a function:
> `!`
function (x) .Primitive("!")
So, you're applying the ! function to numeric arguments, which are coerced to logical, as above.
When you compare a logical to a numeric value using ==, the numeric value is also coerced to logical.
In your first example (!5 == 7) is due to precedence ordering; == is higher precedence than !.

Why do logical operators negate their argument when there is only one argument in R?

When passing only a single vector to the logical and/or operator, the operator negates the argument:
> x = c(F,T,T)
> `&`(x)
[1] TRUE FALSE FALSE
> `|`(x)
[1] TRUE FALSE FALSE
To make the logical operator work as idempotent, one needs to pass a single element vector as the second argument:
> `&`(x,T)
[1] FALSE TRUE TRUE
> `|`(x,F)
[1] FALSE TRUE TRUE
Why do the logical operators negate their argument when there is only one argument passed?
This was modified in R 3.2.1 as a result of a bug report. As you've pointed out, the previous behavior made little sense:

Determine if a vector is ordered

I would like to determine if a vector is either always increasing or always decreasing in R.
Ideally, if I had these three vectors:
asc=c(1,2,3,4,5)
des=c(5,4,3,2,1)
non=c(1,3,5,4,2)
I would hope that the first two would return TRUE, and the last would return FALSE.
I tried a few approaches. First, I tried:
> is.ordered(asc)
[1] FALSE
> is.ordered(des)
[1] FALSE
> is.ordered(non)
[1] FALSE
And I also tried:
> order(non)
[1] 1 5 2 4 3
And hoped that I could simply compare this vector with 1,2,3,4,5 and 5,4,3,2,1, but even that returns a string of logicals, rather than a single true or false:
> order(non)==c(1,2,3,4,5)
[1] TRUE FALSE FALSE TRUE FALSE
Maybe is.unsorted is the function your looking for
> is.unsorted(asc)
[1] FALSE
> is.unsorted(rev(des)) # here you need 'rev'
[1] FALSE
> is.unsorted(non)
[1] TRUE
From the Description of is.unsorted you can find:
Test if an object is not sorted (in increasing order), without the cost of sorting it.
Here's one way using ?is.unsorted:
is.sorted <- function(x, ...) {
!is.unsorted(x, ...) | !is.unsorted(rev(x), ...)
}
Have a look at the additional arguments to is.unsorted, which can be passed here as well.
Here is one way without is.unsorted() to check if to vectors are sorted. This function will return true, if all elements in the vector given are sorted in an ascending manner or false if not:
is.sorted <- function(x) {
if(all(sort(x, decreasing = FALSE) == x)) {
return(TRUE)
} else {
return(FALSE)
}
}

Lazy evaluation of `which` function arguments?

If there are multiple boolean expressions as arguments to the which function, are they evaluated lazily?
For example:
which(test1 & test2)
If test1 returns false, then test2 is not evaluated as the compound expression will be false anyway.
With if there can be efficiency gains as a result of that behavior. It is documented to work that way, and I don't think it is due to lazy evaluation. Even if you "force()-ed" that expression it would still only evaluate a series of &'s until it had a single FALSE. See this help page:
?Logic
#XuWang probably deserved the credit for emphasizing the difference between "&" and "&&". The "&" operator works on vectors and returns vectors. The "&&" operator acts on scalars (actually vectors of length==1) and returns a vector of length== 1. When offered a vector or length >1 as either side of the arguments, it will work on only the information in the first value of each and emit a warning. It is only the "&&" version that does what is being called "lazy" evaluation. You can see that hte "&" operator is not acting in a "lazy fashion with a simepl test:
fn1 <- function(x) print(x)
fn2 <- function(x) print(x)
x1 <- sample(c(TRUE, FALSE), 10, replace=TRUE)
fn1(x1) & fn2(x1) # the first two indicate evaluation of both sides regardless of first value
# [1] FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE
# [1] FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE
# [1] FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE

Resources