Now let’s look at some common operations related to vectors. We’ll cover arithmetic and logical operations, vector indexing, and some useful ways to create vectors. Then we’ll look at two extended examples of using these operations.
Remember that R is a functional language. Every operator, including +
in the following example, is actually a function.
> 2+3 [1] 5 > "+"(2,3) [1] 5
Recall further that scalars are actually one-element vectors. So, we can add vectors, and the +
operation will be applied element-wise.
> x <- c(1,2,4) > x + c(5,0,-1) [1] 6 2 3
If you are familiar with linear algebra, you may be surprised at what happens when we multiply two vectors.
> x * c(5,0,-1) [1] 5 0 −4
But remember, because of the way the *
function is applied, the multiplication is done element by element. The first element of the product (5) is the result of the first element of x
(1) being multiplied by the first element of c(5,0,1)
(5), and so on.
The same principle applies to other numeric operators. Here’s an example:
> x <- c(1,2,4) > x / c(5,4,−1) [1] 0.2 0.5 −4.0 > x %% c(5,4,−1) [1] 1 2 0
One of the most important and frequently used operations in R is that of indexing vectors, in which we form a subvector by picking elements of the given vector for specific indices. The format is vector1[vector2]
, with the result that we select those elements of vector1
whose indices are given in vector2
.
> y <- c(1.2,3.9,0.4,0.12) > y[c(1,3)] # extract elements 1 and 3 of y [1] 1.2 0.4 > y[2:3] [1] 3.9 0.4 > v <- 3:4 > y[v] [1] 0.40 0.12
Note that duplicates are allowed.
> x <- c(4,2,17,5) > y <- x[c(1,1,3)] > y [1] 4 4 17
Negative subscripts mean that we want to exclude the given elements in our output.
> z <- c(5,12,13) > z[-1] # exclude element 1 [1] 12 13 > z[-1:-2] # exclude elements 1 through 2 [1] 13
In such contexts, it is often useful to use the length()
function. For instance, suppose we wish to pick up all elements of a vector z
except for the last. The following code will do just that:
> z <- c(5,12,13) > z[1:(length(z)-1)] [1] 5 12
Or more simply:
> z[-length(z)] [1] 5 12
This is more general than using z[1:2]
. Our program may need to work for more than just vectors of length 2, and the second approach would give us that generality.
There are a few R operators that are especially useful for creating vectors. Let’s start with the colon operator :, which was introduced in Chapter 1. It produces a vector consisting of a range of numbers.
> 5:8 [1] 5 6 7 8 > 5:1 [1] 5 4 3 2 1
You may recall that it was used earlier in this chapter in a loop context, as follows:
for (i in 1:length(x)) {
Beware of operator precedence issues.
> i <- 2 > 1:i-1 # this means (1:i) - 1, not 1:(i-1) [1] 0 1 > 1:(i-1) [1] 1
In the expression 1:i-1
, the colon operator takes precedence over the subtraction. So, the expression 1:i
is evaluated first, returning 1:2. R then subtracts 1 from that expression. That means subtracting a one-element vector from a two-element one, which is done via recycling. The one-element vector (1) will be extended to (1,1) to be of compatible length with 1:2. Element-wise subtraction then yields the vector (0,1).
In the expression 1:(i-1)
, on the other hand, the parentheses have higher precedence than the colon. Thus, 1 is subtracted from i
, resulting in 1:1, as seen in the preceding example.
You can obtain complete details of operator precedence in R through the included help. Just type ?Syntax
at the command prompt.
A generalization of :
is the seq()
(or sequence) function, which generates a sequence in arithmetic progression. For instance, whereas 3:8 yields the vector (3,4,5,6,7,8), with the elements spaced one unit apart (4 – 3 = 1, 5 – 4 = 1, and so on), we can make them, say, three units apart, as follows:
> seq(from=12,to=30,by=3) [1] 12 15 18 21 24 27 30
The spacing can be a noninteger value, too, say 0.1.
> seq(from=1.1,to=2,length=10) [1] 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0
One handy use for seq()
is to deal with the empty-vector problem we mentioned earlier in Section 2.1.2. There, we were dealing with a loop that began with this:
for (i in 1:length(x))
If x
is empty, this loop should not have any iterations, but it actually has two, since 1:length(x)
evaluates to (1,0)
. We could fix this by writing the statement as follows:
for (i in seq(x))
To see why this works, let’s do a quick test of seq()
:
> x <- c(5,12,13) > x [1] 5 12 13 > seq(x) [1] 1 2 3 > x <- NULL > x NULL > seq(x) integer(0)
You can see that seq(x)
gives us the same result as 1:length(x)
if x
is not empty, but it correctly evaluates to NULL if x
is empty, resulting in zero iterations in the above loop.
The rep()
(or repeat) function allows us to conveniently put the same constant into long vectors. The call form is rep(x,times)
, which creates a vector of times*length(x)
elements—that is, times
copies of x
. Here is an example:
> x <- rep(8,4) > x [1] 8 8 8 8 > rep(c(5,12,13),3) [1] 5 12 13 5 12 13 5 12 13 > rep(1:3,2) [1] 1 2 3 1 2 3
There is also a named argument each
, with very different behavior, which interleaves the copies of x
.
> rep(c(5,12,13),each=2) [1] 5 5 12 12 13 13