2.7 Logical Operators

In last section, you learned how to do vector subsetting, which yields a subvector of the original vector. Let’s take a numeric vector x <- 1:5 for example. In order to get a subvector of x with values bigger than 3, you can first create a logical vector xb3 <- x > 3, then use the xb3 as the index to do vector subsetting.

x <- 1:5
xb3 <- x > 3
set1 <- x[xb3]
set1
#> [1] 4 5

Here, the numeric vector set1 has values 4 5. You can get another subvector of x by running the following codes.

xs4 <- x <= 4
set2 <- x[xs4]

Sometimes, you may want to get a subvector with more than one conditions. For example, how can we find the subvector of x with values larger than 3 and less than or equal to 4? In this section, we will introduce several logical operators and use them to get subvectors. Here, we only introduce how to apply these operators on logical vectors.

Before we get started, let’s create another numeric vector y and compare it to 8 and 9 separately, then you will get two logical vectors with same values as xb3 and xs4.

y <- 6:10
yb8 <- y > 8           #xb3 is the same as yb8
ys9 <- y <= 9          #xs4 is the same as ys9

2.7.1 NOT operator by !

The first operator we want to introduce is !, often called the NOT operator. The NOT operator returns the opposite logical value of the input logical values. It is intuitive to understand since if something is NOT FALSE, then it is TRUE; and if it is NOT TRUE, then it has to be FALSE. It is summarized in the following table.

Operation Result
!TRUE FALSE
!FALSE TRUE

Let’s see what happens if you apply the NOT operator on a logical vector.

!c(FALSE, FALSE, FALSE, TRUE, TRUE)  #the opposite of a logical vector
#> [1]  TRUE  TRUE  TRUE FALSE FALSE

From the result, you get another logical vector with the same length as the original one. The value of each element in the new vector is the opposite of the corresponding value in the original vector.

Since xb3 is also a logical vector, you can try to apply ! on xb3, and use !xb3 (which is also a logical vector) to do vector subsetting. Guess what will you get?

!xb3
#> [1]  TRUE  TRUE  TRUE FALSE FALSE
set3 <- x[!xb3]
set3
#> [1] 1 2 3

Of course the resulting numeric vector set3 will have 1 2 3 as values! As a result, set1 and set3 are complement of each other from the whole vector x.

From Section 2.6, you have learned that if the logical vectors you use are identical, you will get the same result after doing vector subsetting. So if you use !yb8 to do vector subsetting, you will get a vector with the same result as that when you use set3.

x[!yb8]
#> [1] 1 2 3

2.7.2 AND operator by &

Secondly, we will introduce the AND operator &. Similar to making comparisons between two logical vectors, & performs comparisons elementwisely, which generates a vector with the same length if the input logical vectors are of the same length or with the same length as that of the longer vector. For each location of the resulting vector, the value will be TRUE if both values in the same location of the input two vectors are both TRUE, and will be FALSE otherwise. In particular, for each element, we have the following summary.

Operation Result
TRUE & TRUE TRUE
TRUE & FALSE FALSE
FALSE & TRUE FALSE
FALSE & FALSE FALSE

Let’s see an example of the AND operation between two logical vectors of the same length.

c(FALSE, FALSE, FALSE, TRUE, TRUE) & c(TRUE, TRUE, FALSE, TRUE, FALSE)
#> [1] FALSE FALSE FALSE  TRUE FALSE

As explained before, the AND operator works elementwisely and the intermediate step is as below.

c(FALSE & TRUE, FALSE & TRUE, FALSE & FALSE, TRUE & TRUE, TRUE & FALSE)
#> [1] FALSE FALSE FALSE  TRUE FALSE

As you can see from the result, only the fourth element is TRUE since the fourth element of both input logical vectors is TRUE.

Since the AND operator makes comparisons elementwisely, the recycling rule also works here. But normally we want one vector with length > 1 and another one with length 1.

c(FALSE, FALSE, FALSE, TRUE, TRUE) & FALSE
#> [1] FALSE FALSE FALSE FALSE FALSE

After learning about the AND operator, you can now get a subvector of x with value(s) > 3 and <= 4 easily. At the beginning of this section, you have created two logical vectors xb3 and xs4 by making comparisons, then let’s apply & on these vectors,

xb3 & xs4  
#> [1] FALSE FALSE FALSE  TRUE FALSE

From the result, you know that both xb3 and xs4 have value TRUE for the fourth element, which means the statement that the value is > 3 and <= 4 is TRUE for the fourth element in x. Then you can use xb3 & xs4 (a logical vector) to do vector subsetting.

x[xb3 & xs4]                                      
#> [1] 4

Here, you get a subvector of x with value 4.

Since xs4 and ys9 have same values, you will get the same result if you include ys9 to get a subvector. You can try the following codes by yourself.

x[xb3 & ys9]
x[yb8 & ys9]

Note that the logical vector used to do vector subsetting needs to be of the same length as the original vector. Since x and y have the same length, the logical vectors from above can be used to get subvectors of y as well. You will get the same result from the following four codes.

y[xb3 & xs4]
y[yb8 & ys9]
y[xb3 & ys9]
y[yb8 & xs4]

2.7.3 OR operator by |

The OR operator | works similarly to the AND operator &, but the difference is that | returns TRUE if there is at least one TRUE among the two elements at the same location in two vectors. Let’s go through some examples together.

Operation Result
TRUE | TRUE TRUE
TRUE | FALSE TRUE
FALSE | TRUE TRUE
FALSE | FALSE FALSE

Let’s try another example on length > 1 vectors and compare the result with that when we use the AND operator &.

c(FALSE, FALSE, FALSE, TRUE, TRUE) | c(TRUE, TRUE, FALSE, TRUE, FALSE)
#> [1]  TRUE  TRUE FALSE  TRUE  TRUE
c(FALSE, FALSE, FALSE, TRUE, TRUE) & c(TRUE, TRUE, FALSE, TRUE, FALSE)
#> [1] FALSE FALSE FALSE  TRUE FALSE

You also get a length-5 logical vector with an elementwise OR operation |, which is very different from the result with AND operation &.

Of course you can also use xb3 | xs4 to do vector subsetting, with it being another length-5 logical vector.

x[xb3 | xs4]
#> [1] 1 2 3 4 5

Wow! You get all five elements of x! That’s because the statement “the value is either > 3 or <= 4” is TRUE for all elements in x.

2.7.4 Exclusive OR by xor

Last but not least, we introduce the exclusive OR operator xor. From the name, it’s easy to know that xor is an extended form of |. Here are some examples,

Operation Result
xor(TRUE, TRUE) FALSE
xor(TRUE, FALSE) TRUE
xor(FALSE, TRUE) TRUE
xor(FALSE, FALSE) FALSE

Different from the OR operator, xor() returns TRUE when there is one and only one TRUE among values of these two logical vectors. If these two vectors have the same value, both TRUE or both FALSE, you will get the value FALSE.

For two length > 1 vectors, xor() again performs comparisons elementwisely. You can check the result by yourself!

xor(c(FALSE, FALSE, FALSE, TRUE, TRUE), c(TRUE, TRUE, TRUE, TRUE, FALSE))
#> [1]  TRUE  TRUE  TRUE FALSE  TRUE

Since you also get a logical vector after applying xor(), you can use it to do vector subsetting. Using different combinations to do vector subsetting is interesting. Try them!

x[xor(xb3, xs4)]
y[xor(!xb3, ys9)]
y[xor(yb8, !ys9)]

2.7.5 Summary of Logical Operators

Let’s summarize the logical operators between two vectors.

  • The NOT operator ! gives the opposite of each value.
  • The AND operator & returns TRUE if both are TRUE.
  • The OR operator | returns TRUE if at least one is TRUE.
  • The exclusive OR operator xor() returns TRUE if one and only one is TRUE.

2.7.6 Exercises

Consider the vector v1 <- seq(from = 1, to = 100, by = 3), and v2 <- sqrt(v1).

  1. Find the subvector of v1 with values bigger or equal to 30 and less than 60. And assign the subvector to name v1s.

  2. Find the subvector of v2 such that the corresponding value of v1 is less than 20 or larger than 50.

  3. Use an example to verify \(xor(a, b) = (!a & b) | (a & !b)\)