Logicals are a fundamental tool for using R in a sophisticated way. Logicals allow us to precisely select elements of an R object (e.g., a vector or dataframe) based upon criteria and to selectively perform operations.
R supports all of the typical mathematical comparison operators: Equal to:
1 == 2
## [1] FALSE
Note: Double equals ==
is a logical test. Single equals =
means right-to-left assignment.
Greater than:
1 > 2
## [1] FALSE
Greater than or equal to:
1 >= 2
## [1] FALSE
Less than:
1 < 2
## [1] TRUE
Less than or equal to:
1 <= 2
## [1] TRUE
Note: Less than or equal to <=
looks like <-
, which means right-to-left assignment.
Spacing between the numbers and operators is not important:
1 == 2
## [1] FALSE
1 == 2
## [1] FALSE
But, spacing between multiple operators is! The following:
# 1 > = 2
produces an error!
The results of these comparisons is a logical vector that has values TRUE, FALSE, or NA:
is.logical(TRUE) #' valid logical
## [1] TRUE
is.logical(FALSE) #' valid logical
## [1] TRUE
is.logical(NA) #' valid logical
## [1] TRUE
is.logical(45) #' invalid
## [1] FALSE
is.logical("hello") #' invalid
## [1] FALSE
Because logicals only take values of TRUE or FALSE, values of 1 or 0 can be coerced to logical:
as.logical(0)
## [1] FALSE
as.logical(1)
## [1] TRUE
as.logical(c(0, 0, 1, 0, NA))
## [1] FALSE FALSE TRUE FALSE NA
And, conversely, logicals can be coerced back to integer using mathematical operators:
TRUE + TRUE + FALSE
## [1] 2
FALSE - TRUE
## [1] -1
FALSE + 5
## [1] 5
Logical comparisons can also be applied to vectors:
a <- 1:10
a > 5
## [1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
This produces a logical vector. This is often useful for indexing:
a[a > 5]
## [1] 6 7 8 9 10
We can also apply multiple logical conditions using boolean operators (AND and OR):
a > 4 & a < 9
## [1] FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE FALSE FALSE
a > 7 | a == 2
## [1] FALSE TRUE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE
Complex conditions can also be combined with parentheses to build a logical:
(a > 5 & a < 8) | (a < 3)
## [1] TRUE TRUE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE
There is also a xor
function to enforce strict OR (but not AND) logic:
xor(TRUE, FALSE)
## [1] TRUE
xor(TRUE, TRUE)
## [1] FALSE
xor(FALSE, FALSE)
## [1] FALSE
This becomes helpful, for example, if we want to create a new vector based on values of an old vector:
b <- a
b[b > 5] <- 1
b
## [1] 1 2 3 4 5 1 1 1 1 1
It is also possible to convert a logical vector into a positional vector using which
:
a > 5
## [1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
which(a > 5)
## [1] 6 7 8 9 10
Of course, this is only helful in some contexts because:
a[a > 5]
## [1] 6 7 8 9 10
a[which(a > 5)]
## [1] 6 7 8 9 10
produce the same result.
We can also invert a logical (turn TRUE into FALSE, and vice versa) using the exclamaation point (!
):
!TRUE
## [1] FALSE
!FALSE
## [1] TRUE
b == 3
## [1] FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
!b == 3
## [1] TRUE TRUE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
We can also use an if-else construction to define a new vector conditional on an old vector:
For example, we could produce our b
vector from above using the ifelse
function:
ifelse(a > 5, 1, a)
## [1] 1 2 3 4 5 1 1 1 1 1
This tests each element of a
. If that elements meets the condition, it returns the next value (1), otherwise it returns the value of a
.
We could modify this slightly to instead return 2 rather than the original value when an element fails the condition:
ifelse(a > 5, 1, 2)
## [1] 2 2 2 2 2 1 1 1 1 1
This gives us an indicator vector.
An especially helpful logical comparator checks of a vector in another vector:
d <- 1:5
e <- 4:7
d %in% e
## [1] FALSE FALSE FALSE TRUE TRUE
e %in% d
## [1] TRUE TRUE FALSE FALSE
R has several other functions related to sets (e.g., union
, intersection
) but these produce numeric, not logical output.
Note: The ifelse
function demonstrates an R feature called “vectorization.”
This means that the function operates on each element in the vector rather than having to test each element separately.
Many R functions rely on vectorization, which makes them easy to write and fast for the computer to execute.