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.