Vector Operations

R vectors are dimensionless objects. Unlike the mathematical column vectors with which you may be familiar, R vectors don’t have rows or columns, but they do have length. We can check the length of a vector using the length() function.

y <- 1:4
length(y)
[1] 4

Arithmetic

Arithmetic in R works element-wise when applied to vectors. That is, the requested operation is performed between matched elements of the vectors. In this way, R vectors behave much like arrays do in most other programming languages.

(x <- rep(2, 4))
[1] 2 2 2 2
y + x
[1] 3 4 5 6
y - x
[1] -1  0  1  2
y / x
[1] 0.5 1.0 1.5 2.0
y * x
[1] 2 4 6 8

Recycling

If two vectors involved in an element-wise operation have different lengths, R will recycle the elements of the shorter vector until each element in the longer vector can be matched with an element from the shorter vector.

Practice

Can you see how R is applying recycling to calculate the elements of w?

  • Explain how the recycling algorithm works in this example.
z <- c(1, 2)
w <- y - z
w
[1] 0 0 2 2

In this example, y has four elements, but z has only two. So, R re-uses the two elements in z to define matches for all four elements in y and specify the four differences needed to define the elements of w.

  1. \(w_1 = y_1 - z_1\)
  2. \(w_2 = y_2 - z_2\) \(\leftarrow\) After calculating the second difference, we’ve run out of elements in z.
  3. \(w_3 = y_3 - z_1\) \(\leftarrow\) Beginning with the third difference, we start recycling the elements of z.
  4. \(w_4 = y_4 - z_2\)

If the length of the longer vector is divisible by the length of the shorter vector, R will recycle silently (i.e., without any messages or warnings). So, you need to vigilant and make sure you don’t accidentally trigger recycling when you don’t want it.

In cases where the length of the shorter vector doesn’t evenly divide the length of the longer vector, R will still use execute the requested operation and apply recycling, but it will also return a warning.

a <- 1:4
b <- 1:2
c <- 1:5

a + b # silent recycling
[1] 2 4 4 6
a + c # recycling with warning
Warning in a + c: longer object length is not a multiple of shorter object
length
[1] 2 4 6 8 6

Why Recycling is a Useful Feature

At this point, if you have any predilection toward strictly typed programming languages, all this recycling business might sound like insanity, but there’s a method to the madness. Let’s consider a few examples that demonstrate how recycling can actually be very helpful in day-to-day programmatic data analysis.

The following code looks like it should implement scalar multiplication, and outcome looks like the result of a scalar multiplication. But R doesn’t have scalars, and vector arithmetic in R doesn’t follow standard linear algebraic rules, so our intuitive reading of this expression can’t be correct.

3 * y
[1]  3  6  9 12

In fact, the preceding code is actually doing element-wise multiplication of a length-one vector, 3, and a length-four vector, y, and recycling the elements of the length-one vector to resolve the length difference. In this example, recycling provides two benefits:

  1. We get to write code that reads like the mathematical representation of the operation we’re trying to implement
  2. Our code produces the same result as the mathematical operation it appears to represent

The following three expressions follow the same pattern as the last example but provide more compelling evidence for the real-world usefulness of recycling. These expressions don’t represent valid operations under the conventional rules of linear algebra, and they wouldn’t be valid commands in a highly structure programming language like C. Yet, the intended operations are intuitively obvious, and R will use recycling to execute the operations we expect.

y - 1
[1] 0 1 2 3
y + 3
[1] 4 5 6 7
y / 3
[1] 0.3333333 0.6666667 1.0000000 1.3333333

Logical Tests

Logical comparisons between vectors test some logical relation between the vectors and return a logical vector that encodes the answer to whatever TRUE/FALSE question the logical relation tested. These comparisons follow the same rules as vector arithmetic:

  • Operators are applied element-wise
  • Recycling is used to resolve length differences
# Check which elements of y are equal to 4
y == 4
[1] FALSE FALSE FALSE  TRUE
# Check which elements of y are equal to 4 or 2
y == 4 | y == 2
[1] FALSE  TRUE FALSE  TRUE

We need to be especially mindful of the vector operation rules when applying logical comparisons to vectors. When defining logical tests, intuition can easily fail us. For example, it would be natural to read the following code as implementing the same test as the previous expression, but the two results are clearly different. Why?

y == c(4, 2)
[1] FALSE  TRUE FALSE FALSE

As you’ve probably surmised, recycling is causing the discrepancy. We can clarify the differences between the two tests by extending the shorter vectors to manually implement recycling.

y == c(4, 4, 4, 4) | y == c(2, 2, 2, 2)
[1] FALSE  TRUE FALSE  TRUE
y == c(4, 2, 4, 2)
[1] FALSE  TRUE FALSE FALSE

Now, the two tests don’t appear very similar, and their different outcomes aren’t particularly surprising.

Practice
  1. Create the object myVec by running the two lines of code below.
  2. Programmatically create a logical vector that indicates which elements of myVec are (strictly) less than 3.
set.seed(235711)
myVec <- sample(1:5)

y <- myVec < 3
Back to top