This Chapter gets you started officially with R. While the theme is vectors, the most important data structure in R, we’ll learn also about variables and variable names, vector types, reserved words, assignment and many of R’s basic operators.
2.1 What is a Vector?
If you have heard of vectors before in mathematics, you might think of a vector as something that has a magnitude and a direction, and that can be represented by a sequence of numbers. In its notion of a vector, R keeps the idea of a sequence but discards magnitude and direction. The notion of “numbers” isn’t even necessary.
For R, a vector is simply a sequence of elements. There are two general sort of vectors:
atomic vectors that come in one of six forms called vector types;
non-atomic vectors, called lists, whose elements can be any sort of R-object at all.
For now we’ll just study atomic vectors. Let’s make a few vectors, as examples.
We can make a vector of numbers using the c() function:
You can think of c as standing for “combine.” c() takes its arguments, all of which are separated by commas, and combines them to make a vector.
If you closely examine the above output, you’ll notice that R printed out all of the numerical values in the vector to three decimal places, which happened to be the largest number of decimal places we assigned to any of the numbers that made up numVec. You’ll also notice the numbers in brackets at the beginning of the lines. Each number represents the position within the vector occupied by the first element of the vector that is printed on the line. The position of an element in a vector is called its index. Reporting the indices of leading elements helps you locate particular elements in the output.
2.1.1 Types of Atomic Vectors
The numbers in numVec are what programmers call double-precision numbers. You can verify this for yourself with the typeof() function:
typeof(numVec)
[1] "double"
The typeof() function returns the type of any object in R. As far as vectors are concerned, there are six possible types, of which we will deal with only four:
double
integer
character
logical
Let’s look at examples of the other types. Here is a vector of type integer:
intVec <-c(3L, 17L, -22L, 45L)intVec
[1] 3 17 -22 45
The L after each number signifies to R that the number should be stored in memory as an integer, rather than in double-precision format. Officially, the type is integer:
typeof(intVec)
[1] "integer"
You should know that if you left off one or more of the L’s, then R would create a vector of type double:
numVec2 <-c(3, 17, -22, 45)typeof(numVec2)
[1] "double"
We won’t work much with integer-type vectors, but you’ll see them out in the wild.
We can also make vectors out of pieces of text called strings: these are called character vectors. As noted in the previous chapter, we use quotes to delimit strings:
You need all-caps: if you try anything else—like the following—you get an error:
badVec <-c(TRUE, False)
Error: object 'False' not found
2.1.2 Coercion
What would happen if you tried to represent falsity with the string "false"?
newVector <-c(TRUE, "false")newVector
[1] "TRUE" "false"
newVector is not a logical vector. Check it out:
typeof(newVector)
[1] "character"
In order to understand what just happened here, you must recall that all of the elements of an atomic vector have to be of the same type. If the c() function is presented with values of different types, then R follows a set of internal rules to coerce some of the values to a new type in such a way that all resulting values are of the same type. You don’t need to know all of the coercion rules, but it’s worth noting that
character beats double,
which in turn beats integer,
which in in turn beats logical.
The following examples show this:
typeof(c("one", 1, 1L, TRUE))
[1] "character"
typeof(c(1, 1L, TRUE))
[1] "double"
typeof(c(1L, TRUE))
[1] "integer"
Automatic coercion can be convenient in some circumstances, but in others it can give unexpected results. It’s best to keep track of what types you are dealing with and to exercise caution when combining values to make new vectors.
You can also coerce vectors “manually” with the functions:
as.numeric() ;
as.integer() ;
as.character() ;
as.logical() .
Here are some examples:
numVec <-c(3, 2.5, -7.32, 0)as.character(numVec)
[1] "3" "2.5" "-7.32" "0"
as.integer(numVec)
[1] 3 2 -7 0
as.logical(numVec)
[1] TRUE TRUE TRUE FALSE
Note that in coercion from numerical to logical, the number 0 becomes FALSE and all non-zero numbers become TRUE.
2.1.3 Combining Vectors
You can combine vectors you have already created to make new, bigger ones:
You can see here that vectors are different from sets: they are allowed to repeat the same value in different indices, as we see in the case of the 3’s above.
2.1.4 NA Values
Consider the following vector, which we may think of as recording the heights of people, in inches:
heights <-c(72, 70, 69, 58, NA, 45)
The NA in the fifth position of the vector is a special value that may be considered to mean “Not Assigned.” We can use it to say that a value was not recorded or has gone missing for some reason. R will often use it to say (more or less) that it cannot do what we asked. For example, consider:
as.numeric("four")
Warning: NAs introduced by coercion
[1] NA
R is not programmed to transform the string "four" to any particular number, so it coerces the string to NA (and issues a warning in case you did something you didn’t intend to do).
2.1.5 “Everything in R is a Vector”
Some folks say that everything in R is a vector. That’s a bit of an exaggeration but it’s remarkably close to the truth.
And yet it seems implausible. What about the elements of an atomic vector, for instance? A single element doesn’t look at all like a vector: it’s a value, not a sequence of values.
Or so we might think. But really, in R there are no “single values” that can exist by themselves. Consider, for instance, what we think of as the number 17:
17
[1] 17
See the [1] in front, in the output above? It indicates that the line begins with the first element of a vector. So 17 doesn’t exist on its own: it exists a vector of type double—a vector of length 1.
Even NA is, all along, a vector of length 1
NA
[1] NA
It is of type logical:
typeof(NA)
[1] "logical"
Note that even the type of NA evaluates, in R, to a vector: a character vector of length 1 whose only element is the string “logical”!
2.1.6 Named Vectors
The elements of a vector can have names, if we like:
ages <-c(Bettina =32, Chris =64, Ramesh =101)ages
Bettina Chris Ramesh
32 64 101
What is the type of this named vector? Let’s find out:
typeof(ages)
[1] "double"
Having names doesn’t keep the vector from being a vector of type double: it has to be double because its elements are of type double.
We can name the elements of a vector when we create it with c(), or we can name them later on. One way to do this is with the names() function:
Scarecrow Tinman Lion Dorothy Toto Boq
72 70 69 58 NA 45
2.1.7 Special Character Vectors
R comes with two handy, predefined character vectors:
letters
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
[20] "t" "u" "v" "w" "x" "y" "z"
LETTERS
[1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
[20] "T" "U" "V" "W" "X" "Y" "Z"
We will make use of them from time to time.
2.1.8 Length of Vectors
The length() function tells us how many elements a vector has:
length(heights)
[1] 6
2.1.9 Practice Exercises
Consider the following vector:
upperLower <-c(LETTERS, letters)
What should the length of upperLower be? Check you answer using the length() function.
True or False: c("a", 2, TRUE) yields a vector of length three consisting of the string "a", the number 2 and the logical value TRUE.
The function as.numeric() tries to coerce its input into numbers. How well can it pick out the “numbers” in strings. Try the following calls. When did as.numeric() find the numbers that was probably intended?
as.numeric("3.214")as.numeric("3L")as.numeric("fifty")as.numeric("10 + 3")as.numeric("3.25e-3") # scientific notation: 3.25 times 10^(-2)as.numeric("31,245")
2.1.10 Solutions to Practice Exercises
There are 26 letters, so the length of upperlower should be \(2 \times 26 = 52\). Let’s check:
length(upperLower)
[1] 52
False! The resulting vector will be atomic—all of its elements will be the same data type. The non-strings will be coerced to strings, yielding:
c("a", 2, TRUE)
[1] "a" "2" "TRUE"
as.numeric() isn’t very smart: it picked out the number in "3.214" and 3.25e-3, but in the other cases it returned NA.
2.2 Constructing Patterned Vectors
Quite often we need to make lengthy vectors that follow simple patterns. R has a few functions to assist us in these tasks.
2.2.1 Sequencing
Consider the seq() function:
seq(from =5, to =15, by =1)
[1] 5 6 7 8 9 10 11 12 13 14 15
The default value of the parameter by is 1, so we could get the same thing with:
seq(from =5, to =15)
[1] 5 6 7 8 9 10 11 12 13 14 15
Further reduction in typing may be achieved as long as we remember the order in which R expects the parameters (from before to, then by if supplied):
seq(5, 15)
[1] 5 6 7 8 9 10 11 12 13 14 15
Some more complex examples:
seq(3, 15, 2)
[1] 3 5 7 9 11 13 15
seq(0, 1, 0.1)
[1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
R will go up to the to value, but not past it:
seq(3, 16, 2)
[1] 3 5 7 9 11 13 15
Negative steps are fine:
seq(5, -4, -1)
[1] 5 4 3 2 1 0 -1 -2 -3 -4
The colon operator: is a convenient abbreviation for seq:
1:5# 1 is from, 5 is to
[1] 1 2 3 4 5
If the from number is greater than the to number the step for the colon operator is -1:
5:1
[1] 5 4 3 2 1
2.2.2 Repeating
With rep() we may repeat a given vector as many times as we like:
rep(3, times =5)
[1] 3 3 3 3 3
We can apply rep() to a vector of length greater than 1:
vec <-c(7, 3, 4)rep(vec, times =3)
[1] 7 3 4 7 3 4 7 3 4
rep() applies perfectly well to character-vectors:
rep("Toto", 4)
[1] "Toto" "Toto" "Toto" "Toto"
rep() also takes an each parameter that determines how many times each element of the given vector will be repeated before the times parameter is applied. This is best illustrated with an example:
vec <-c(7, 3, 4)rep(vec, each =2, times =3)
[1] 7 7 3 3 4 4 7 7 3 3 4 4 7 7 3 3 4 4
If we combine seq() and rep() we can create fairly complex patterns concisely:
It tells you that the first argument of rep() is the vector that you want to repeat, and that it’s called x. It goes on to say that times is:
“an integer-valued vector giving the (non-negative) number of times to repeat each element if of length length(x), or to repeat the whole vector if of length 1.”
Use this information to describe in words what will be the output of:
rep(seq(10, 100, by =10), times =1:10)
2.2.4 Solutions to Practice Exercises
Here’s how:
rep("Kansas", times =5)
Here’s how:
rep(c(TRUE, FALSE), times =4)
Here’s how:
seq(5, 26, by =3)
Here’s how:
seq(8, -32, by =-4)
Here’s how:
10:20
Here’s how:
10:-30
All you need is this:
1:length(myVec)
Here’s how:
rep(seq(2, 10), times =3)
Here’s how:
rep(seq(2, 10), each =3)
You’ll get one 10, two 20s, three 30s, …, all the way up to ten 100s.
Quite often we need to select one or more elements from a vector. The subsetting operator[ allows us to do this.
Recall the vector heights:
heights
Scarecrow Tinman Lion Dorothy Toto Boq
72 70 69 58 NA 45
If we want the fourth element, we ask for it with the subsetting operator like this:
heights[4]
Dorothy
58
If we want two or more elements, then we specify their indices in a vector. Thus, to get the first and fifth elements, we might do this:
desired <-c(1,5)heights[desired]
Scarecrow Toto
72 NA
We could also ask for them directly:
heights[c(1,5)]
Scarecrow Toto
72 NA
Negative numbers are significant in subsetting:
heights[-2] #select all but second element
Scarecrow Lion Dorothy Toto Boq
72 69 58 NA 45
heights[-c(1,3)] # all but first and third
Tinman Dorothy Toto Boq
70 58 NA 45
If you specify a nonexistent index, you get NA, the reasonable result:
heights[7]
<NA>
NA
Patterned vectors are quite useful for subsetting. If you want the first three elements of heights, you don’t have to type heights[c(1,2,3)]. Instead you can just say:
heights[1:3]
Scarecrow Tinman Lion
72 70 69
The following gives the same as heights:
heights[1:length(heights)]
Scarecrow Tinman Lion Dorothy Toto Boq
72 70 69 58 NA 45
If you desire to quickly provide names for a vector, subsetting can help:
If a vector has names we can refer to its elements using the subsetting operator and those names:
heights["Tinman"]
Tinman
70
heights[c("Scarecrow", "Boq")]
Scarecrow Boq
72 45
Finally, we can use subsetting to modify parts of a vector. For example, Dorothy’s height is reported as:
heights["Dorothy"]
Dorothy
58
If Dorothy grows two inches, then we can modify her height as follows:
heights["Dorothy"] <-60
We can replace more than one element, of course. Thus:
heights[c("Scarecrow", "Boq")] <-c(73, 46)
The subset of indices may be as complex as you like:
vec <-c(3,4,5,6,7,8)vec[seq(from =2, to =6, by =2)] <-c(100, 200, 300)vec
[1] 3 100 5 200 7 300
In the above example, seq(2,6,2) identified 2, 4 and 6 as the indices of elements of vec that were to be replaced by the corresponding elements of c(100, 200, 300).
We can even use subsetting to rearrange the elements of a vector. Consider the example below:
Select the third and sixth elements of practiceVec.
Select the first, second, third and fourth elements of practiceVec.
How would you select the last element of the vector mysteryVec if you did not know how many elements it had?
Select all but the fourth element of practiceVec.
Select all but the fourth and sixth elements of practiceVec.
Select the even-numbered elements of practiceVec.
Replace the third element of practiceVec with the number 5.
Replace the even-numbered elements of practiceVec with zeroes.
Replace the second, third and fifth elements of practiceVec with 3, 10, and 20 respectively.
Reverse the order of the elements of practiceVec.
2.3.2 Solutions to Practice Exercises
practiceVec[5]
practiceVec[c(3,6)]
practiceVec[1:4]
mysteryVec[length(mysteryVec)]
practiceVec[-4]
practiceVec[-c(4,5)]
practiceVec[seq(2, length(practiceVec), by = 2)]
practiceVec[3] <- 5
Here is one way:
evenIndices <-seq(2, length(practiceVec), by =2) zeroes <-rep(0, times =length(evenIndices))practiceVec[evenIndics] <- zeroes
Here’s a quicker way:
practiceVec[evenIndices] <-0
The latter approach involves “recycling” the zero. We’ll discuss recycling soon.
practiceVec[c(2, 3, 5)] <- c(3, 10, 20)
practiceVec[length(practiceVec):1]
2.4 More on Logical Vectors
Consider the following expression:
13<20
[1] TRUE
We constructed it with the “less-than” operator <. You can think of it as saying that 13 is less than 20, which is a true statement, and sure enough, R evaluates the expression 13 < 20 as TRUE.
When you think about it, we’ve seen lots of expressions so far. Here are just a few of them:
sqrt(64)
heights
heights[1:3]
13 < 20
When we type any one of them into the console, it evaluates to a particular value. In the examples above, the value was always a vector.
Expressions like 13 < 20 that evaluate to a logical vector are often called Boolean expressions.1
2.4.1 Boolean Operators
Let’s look further into Boolean expressions. Define the following two vectors:
a <-c(10, 13, 17)b <-c(8, 15, 12)
Now let’s evaluate the expression a < b:
a < b
[1] FALSE TRUE FALSE
The < operator, when applied to vectors, always works element-wise; that is, it is applied to corresponding elements of the vectors on either side of it. R’s evaluation of a < b involves evaluation of the following three expressions:
10 < 8 (evaluates to FALSE)
13 < 15(evaluates to TRUE)
17 < 12(evaluates to FALSE)
The result is a logical vector of length 3.
The < operator is an example of a Boolean operator in R. Table 2.1 shows the available Boolean operators.
Table 2.1: The Boolean Operators
Operation
What It Means
<
less than
>
greater than
<=
less than or equal to
>=
greater than or equal to
==
equal to
&
and
|
or
!
not
2.4.1.1 Inequalities
The “numerical-looking operators” (<, <=, >, >=) have their usual meanings when one is working with numerical vectors2 When applied to character vectors they evaluate according to an alphabetical order:
a<-c("Dorothy", "toto", "Boq")b <-c("tinman", "Toto", "2017")a < b
[1] TRUE TRUE FALSE
The reasons for the evaluation above are as follows:
D comes before t in the alphabet;
lowercase t comes before uppercase T, according to R;
characters for numbers come before letter-characters, according to R.
2.4.1.2 Equality
The equality (==) operator indicates whether the expressions being compared evaluate to the same value. Note that it’s made with two equal-signs, not one! It’s all about evaluation to the same value, not strict identity. The following examples will help to clarify this.
a <-c(Dorothy =1,Toto =2) # a named vectorb <-c(Glinda =1, Tinman =2)a == b
Dorothy Toto
TRUE TRUE
(Note that the resulting logical vector inherits the names of a, the vector on the left.).
But a and b aren’t identical. We can see this because R has the function identical() to test for identity:
identical(a, b)
[1] FALSE
Corresponding elements of a and b have the same values, but the two vectors don’t have the same set of names, so they aren’t considered identical.
Here’s another way to see that “evaluating to the same value” is not the same as “identity”:
TRUE==1
[1] TRUE
When TRUE (itself oftype logical) is being compared with something numerical (type integer or double) it is coerced into the numerical vector 1. (In the same situation FALSE would be coerced to 0.) But clearly TRUE and 1 are not identical:
identical(TRUE, 1)
[1] FALSE
2.4.1.3 And, Or, Not
We consider an “and” statement to be true when both of its component statements are true; otherwise it is counted as false. The & Boolean operator accords with our thinking:
a <-c(TRUE, TRUE, FALSE, FALSE)b <-c(TRUE, FALSE, TRUE, FALSE)a & b
[1] TRUE FALSE FALSE FALSE
In logic and mathematics, an “or” statement is considered to be true when at least one of its component statements are true. (This is sometimes called the “inclusive” use of the term “or.”) R accords with this line of thinking:
a <-c(TRUE, TRUE, FALSE, FALSE)b <-c(TRUE, FALSE, TRUE, FALSE)a | b
[1] TRUE TRUE TRUE FALSE
The final Boolean operator is !, which works like “not”:
a <-c(TRUE, FALSE)!a
[1] FALSE TRUE
e <-c(2, 5, 6)f <-c(3, 1, 2)e > f
[1] FALSE TRUE TRUE
!(e > f)
[1] TRUE FALSE FALSE
2.4.2 Vector Recycling
Consider the vector
vec <-c(2, 6, 1, 7, 3)
Look at what happens when we evaluate the expression:
vec >4
[1] FALSE TRUE FALSE TRUE FALSE
At first blush this doesn’t make any sense: vec has length 5, whereas 4 is a vector of length 1. How can the two of them be compared?
They cannot, in fact, be compared. Instead the shorter of the two vectors—the 4—is recycled into the c(4,4,4,4,4) a vector of length five, which may then be compared element-wise with vec. Recycling is a great convenience as it allows us to express an idea clearly and concisely.
Recycling is always performed on the shorter of two vectors. Consider the example below:
vec2 <-1:6vec2 >c(3,1)
[1] FALSE TRUE FALSE TRUE TRUE TRUE
Here, c(3,1) was recycled into c(3,1,3,1,3,1) prior to being compared with vec2.
What happens if the length of the longer vector is not a multiple of the shorter one? We should look into this:
vec2 <-1:7vec2 >c(3, 8)
## longer object length is not a multiple of shorter object length
## [1] FALSE FALSE FALSE FALSE TRUE FALSE TRUE
We get a warning, but R tries to do the job for us anyway, recycling the shorter vector to c(3,8,3,8,3,8,3) and then performing the comparison.
By the way, if you don’t want to see the warning you can put the expression into the suppressWarnings() function:
Think of the vectors as having corresponding elements. Thus, there is a person named Dorothy who is 12 years old and likes dogs, a person named Tin Man who is 0.04 years old and doesn’t like dogs, etc.
Write a Boolean expression that is TRUE when a person is less than 14 years old and FALSE otherwise.
Write a Boolean expression that is TRUE when a person is between 10 and 15 years old (not including 10 but not 15) and FALSE otherwise.
Write a Boolean expression that is TRUE when a person is more than 12 years old and likes dogs, and FALSE otherwise.
Write a Boolean expression that is TRUE when a person is more than 12 years old and does not like dogs, and FALSE otherwise.
Write a Boolean expression that is TRUE when a person is more than 12 years old and or likes dogs, and FALSE otherwise.
Write a Boolean expression that is TRUE when the person is Dorothy, and FALSE otherwise.
Write a Boolean expression that is TRUE when the person is Dorothy or Tin Man, and FALSE otherwise.
Write a Boolean expression that is TRUE when the person’s name comes after the letter “M” in the alphabet, and FALSE otherwise.
Write a Boolean expression that is FALSE when the person is Dorothy, and TRUE otherwise.
2.4.4 Solutions to Practice Exercises
Here’s the code:
age <14
[1] TRUE TRUE FALSE FALSE TRUE
Here’s the code:
age >=10& age <15
[1] TRUE FALSE FALSE FALSE FALSE
Here’s the code:
age >12& likesDogs
[1] FALSE FALSE TRUE FALSE FALSE
Here’s the code:
age >12&!likesDogs
[1] FALSE FALSE FALSE TRUE FALSE
Here’s the code:
age >12| likesDogs
[1] TRUE FALSE TRUE TRUE TRUE
Here’s the code:
person =="Dorothy"
[1] TRUE FALSE FALSE FALSE FALSE
Here’s the code:
person =="Dorothy"| person =="Tin Man"
[1] TRUE FALSE TRUE FALSE FALSE
Here’s the code:
person >"M"
[1] FALSE TRUE TRUE FALSE TRUE
Here’s the code:
person !="Dorothy"
[1] FALSE TRUE TRUE TRUE TRUE
2.5 Subsetting with Logical Vectors
The subsetting we have seen up to now involves specifying the indices of the elements we would like to select from the original vector. It is also possible to say, for each element, whether or not it is to be included in our selection. This is accomplished by means of logical vectors.
Recall our heights vector:
heights
Scarecrow Tinman Lion Dorothy Toto Boq
73 70 69 60 NA 46
Let’s say that we want the heights of Scarecrow, Tinman and Dorothy. We can use a logical vector to do this:
The TRUE’s at indices 1, 2, and 4 in wanted inform R that we want the heights vector at indices 1, 2 and 4. The FALSE’s say: “don’t include this element!”
Subsetting can be used powerfully along with logical vectors and Boolean operators.
For example, in order to select those persons whose heights exceed a certain amount, we might say something like this:
#heights of some people:people <-c(55, 64, 67, 70, 63, 72)tall <- (people >=70)tall
[1] FALSE FALSE FALSE TRUE FALSE TRUE
people[tall]
[1] 70 72
As you can see, the tall vector specifies which elements we would like to select from the people vector.
We need not define the tall vector along the way. It is quite common to see something like the following:
people[people >=70]
[1] 70 72
I like to pronounce the above as:
people, where people is at least 70
The word “where” in the above phrase corresponds to the subsetting operator.
Your subsetting logical vector need not have been constructed with the original vector in mind. Consider the following example:
Here the selection is done from the age vector, using a logical vector that was constructed from height—another vector altogether. It concisely expresses the idea:
the ages of people whose height is less than 70
There is no limit to the complexity of selection. Consider the following:
Logical subsetting provides a convenient way to count the elements of a vector that possess a given property. For example, think back to the vector people, which gave the heights of some people:
people <-c(55, 64, 67, 70, 63, 72)
In order find out how many people are less than 70 years old, we could say:
length(people[people <70])
[1] 4
But there is an easier way, made possible by how R coerces logical vectors into numerical ones. Consider the following logical vector:
Now suppose we decide to add up those TRUEs and FALSEes. It seems like a crazy idea, but R actually gives us an answer:
sum(logical_vector)
[1] 3
What happened, here? Well, R needs numbers as input for the sum() function. when it doesn’t get numbers, it does the best it can to coerce its input into numbers. It so happens that R is programmed to coerce TRUE to the number 1 and FALSE to the number 0. So the command sum(logical_vector) really amount to:
sum(c(1, 1, 0, 0, 0, 1))
[1] 3
The upshot is that if you ask R to sum a logical vector, the result is just a count of how manyTRUEs it contained.
Back to people. people < 70 is a Boolean expression, so its result is a logical vecotr:
people <70
[1] TRUE TRUE TRUE FALSE TRUE FALSE
Summing this vector counts how many times it was true that the height was less than 70:
sum(people <70)
[1] 4
That’s very convenient!
2.5.2 Which, Any, All
There are several functions on logical vectors that are worth keeping in your back pocket:
which()
any()
all()
2.5.2.1which()
Applied to a logical vector, the which() function returns the indices of the vector that have the value TRUE:
boolVec <-c(TRUE,TRUE,FALSE,TRUE)which(boolVec)
[1] 1 2 4
Thus if we want to know the indices of heights where the heights are at least 65, then we write:
which(heights >65)
Scarecrow Tinman Lion
1 2 3
(Recall that height was a named vector. The logical vector heights > 65 inherited these names and passed them on to the result of which().)
Note also that Toto’s NA height was ignored by which().
2.5.2.2any()
Is anyone more than 71 inches tall? any() will tell us:
heights
Scarecrow Tinman Lion Dorothy Toto Boq
73 70 69 60 NA 46
any(heights >71)
[1] TRUE
Yes: the Scarecrow is more than 71 inches tall.
We can use any() along with the equality Boolean operator == to determine whether or not a given value appears a a given vector:
2.6.1 Familiar Arithmetic: Addition, Subtraction, etc.
R provides all of the familiar arithmetical operations. Table 2.2 shows the basic operators.
Table 2.2: Familiar arithmetical operations on vectors.
Operation
What It Means
x + y
addition
x - y
subtraction
x * y
multiplication
x / y
division
x^y
exponentiation (raise x to the power y)
The operators are applied element-wise to vectors:
x <-c(10, 15, 20)y <-c(3, 4, 5)x + y
[1] 13 19 25
x - y
[1] 7 11 15
x * y
[1] 30 60 100
x / y
[1] 3.333333 3.750000 4.000000
x^y
[1] 1000 50625 3200000
As an illustration, the final result is:
\[10^3, 15^4, 20^5.\] The “mod” operator %% can be quite useful. Here is an example: even numbers have a remainder of 0 after division by 2, whereas odd numbers have a remainder of 1. Hence we may use %% to quickly locate the even numbers in a vector, as follows:
vec <-c(2, 7, 9, 12, 15, 24)vec[vec %%2==0]
[1] 2 12 24
Recycling applies in vector arithmetic (as in most of R):
vec <-c(2, 7, 9, 12, 15, 24)2* vec # the 2 will be recycled
[1] 4 14 18 24 30 48
vec +100# the 100 will be recycled
[1] 102 107 109 112 115 124
vec^3# the 3 will be recycled
[1] 8 343 729 1728 3375 13824
2.6.1.1 Vectorization
All of the above operations implement a “vector-in, vector-out” principle—referred to by R users as vectorization. Not only does vectorization permit us to express ideas concisely and in human-readable fashion, but the computations themselves tend to be performed very quickly.
The sqrt() function for taking square roots, which you met in the first Chapter, also implements vectorization. For example, if you need the square roots of all of the numbers in vec, then you can just write:
R provides two special operations that are related to division:
Basic arithmetical operations on vectors.
Operation
What It Means
x %/% y
integer division (quotient after dividing x by y)
x %% y
x mod y (remainder after dividing x by y)
Let’s review the ideas of quotient and remainder:
Suppose you divide 17 by 5.
The quotient is 3 …
… because \(3 \times 5 = 15 \leq 17\), but
\(4 \times 5 = 20 > 17\).
The remainder is 2 …
… because \(17 - 15 = 2\).
The %/% operator finds tha quotient for you:
17%/%5
[1] 3
The %% operator in R finds the remainder:
## write the dividend, then %%, then the divisor:17%%5
[1] 2
Note: because remainders are associated with an area of advanced mathematics called modular arithmetic, people often pronounce the %% operator as “mod”. Thus, 17 %% 5 would be pronounced as “seventeen mod 5”.
Both of these operators implement vectorization, so, for example, the %% finds the remainders for all elements of any dividend-vector:
vec <-5:20## vector of dividendsvec %%5## the remainders after dividing by 5
[1] 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0
As an application, let’s find all the numbers in vec that are multiples of 6. It’s a two-step process:
First, find out whether or not each number has a remainder of 0 when divided by 6:
The way we compute the mean in R looks a great deal like its mathematical definition.
You might be interested to know that there is a function in R dedicated to finding the mean. Unsurprisingly, it is called mean() :
mean(vec)
[1] 18.4
2.6.4 Maxima and Minima
The max() function delivers the maximum value of the elements of a numerical vector:
max(c(3, 7, 2))
[1] 7
The min() function delivers the minimum value of a numerical vector:
min(c(3, 7, 2))
[1] 2
You can enter more than one vector into min() or max(): the function will combine the vectors and then do its job:
a <-c(5, 6, 10)b <-c(2, 3, 12, 15, 1) # the max of both is 15max(a, b)
[1] 15
The pmax() function compares corresponding elements of each input-vector and produces a vector of the maximum values:
a <-c(3, 7, 10)b <-c(5, 2, 12)pmax(a, b)
[1] 5 7 12
There is a pmin() function that computes pair-wise minima as well:
pmin(a, b)
[1] 3 2 10
2.6.5 Practice Exercises
Consider the following vectors:
Write a command that produces the squares of the first 10 whole numbers.
Write a command that produces the square roots of: the numbers from 1 to 100 that are one more than a multiple of 3. (Hint: Recall that the function sqrt() will take the square roots of the elements of any numerical vector).
Write a command that raises 2 to the second power, 3 to third power, 4 to the fourth power, … , up to 100 to the hundredth power.
The following problems use the vectors from the previous set of practice Exercises:
Using the sum() function and the vector hasPets, write a command that says how many people have pets.
Using the sum() function and the vector hasPets, write a command that says how many people do not have pets.
Using the vectors above, find the name of the person who has the most education.
2.6.6 Solutions to the Practice Exercises
(1:10)^2
Here are a couple of ways:
sqrt(seq(1, 100, by =3))sqrt((1:100)[1:100%%3==1])
(2:100)^(2:100)
When given a logical vector, the sum() function converts TRUE to 1 and FALSE to 0, and then adds. Accordingly, you can count how many people have pets like this:
sum(hasPets)
[1] 3
Do this:
sum(!hasPets)
[1] 3
Try this:
person[yearsEducation ==max(yearsEducation)]
[1] "Esmeralda"
2.7 Legal Variable Names
Using the assignment operator we have created quite a few variables by now, and we appear to have named them whatever we want. In fact there are very few limitations on the name of a variable. According to R’s own documentation:3
“A syntactically valid name consists of letters, numbers and the dot or underline characters and starts with a letter or the dot not followed by a number.”
This means:
Note
In R, a legal name:
CAN contain letters, both lower and uppercase: a through z and also A through Z;
CAN contain the the digits 0,1, through 9;
CAN contain the dot . and the underscore _.
HOWEVER, it must start with either a letter or the dot, AND
if it starts with a dot then that dot cannot be immediately followed by a number.
This leaves a lot of room for creativity. All of the following names are possible for variables:
yellowBrickRoad
yellow_brick_road
yellow.brick.road
yell23
y2e3L45Lo3....rOAD
.yellow
The following, though, are not valid:
.2yellow (cannot start with dot and then number)
_yellow (cannot start with _)
5scones (cannot start with a number)
Most programmers try to devise names for variables that are descriptive in the sense that they suggest to a reader of the code the role that is played within it by the variable. In addition they try to stick to a consistent system for variable names that divide naturally into meaningful words.
One popular convention is known as CamelCase. In this convention each new word-like part of the variable names begins with a capital letter. (The initial letter, though, is often not capitalized.) Examples would be:
emeraldCity
isEven
Another popular convention—sometimes called “snake-case”—is to use lowercase and to separate words with underscores:
emerald_city
is_even
An older convention—one that was popular among some of the original developers of R—was to separate words with dots:
emerald.city
is.even
You see will often see this in R functions (examples oyu have met so far include as.numeric()as.character(). This convention is no longer recommended, however, because in programming languages other than R the dot is associated syntactically with the calling of a “method” on an “object.”4
2.7.1 Practice Exercises
Is ThreeLittlePigs a valid name for a variable? If not, why not?
Is 3LittlePigs a valid name for a variable? If not, why not?
Is LittlePigs3 a valid name for a variable? If not, why not?
Is Little-Pigs-3 a valid name for a variable? If not, why not?
Is Little_Pigs_3 a valid name for a variable? If not, why not?
Is three.little.pigs a valid name for a variable? If not, why not?
2.7.2 Solutions to the Practice Exercises
Yes.
No! Variables can’t start with a number.
Yes.
No! Hyphens aren’t allowed in variables.
Yes.
Yes.
2.8 More in Depth
2.8.1 More Math Functions
Here are a few more useful math functions involving vectors.
2.8.1.1 Cumulative Sums
Consider the this vector:
sample_numbers <-c(4, 5, -3, 2, 0, 8)
If you sum the elements of this vector up to each of its successive indices, you get:
just the first element: 4;
\(4+5=9\);
\(4+5+(-3)=6\);
\(4+5+(-3)+2=8\);
\(4+5+(-3)+2+0=8\) again;
\(4+5+(-3)+2+0+8=16\).
The function cumsum() will do the work for you:
cumsum(sample_numbers)
[1] 4 9 6 8 8 16
That is: for each \(i\) from 1 to the length of sample_numbers, the \(i\)-th element of cumsum(sample_numbers) is the sum of the elements of sample_numbers from index 1 through index \(i\).
2.8.1.2 Rounding
You can use the round() function to round off numbers to any desired number of decimal places.
roots <-sqrt(1:5)roots # Too much information!
[1] 1.000000 1.414214 1.732051 2.000000 2.236068
round(roots, digits =3) # nicer
[1] 1.000 1.414 1.732 2.000 2.236
2.8.1.3 Ceiling and Floor
The ceiling() function returns the least integer that is greater than or equal to the given number:
Scarecrow Tinman Lion Dorothy Toto Boq
72 70 69 58 NA 45
You should be aware of the effect of NA-values on subsetting.
tall <- (heights >65)tall
Scarecrow Tinman Lion Dorothy Toto Boq
TRUE TRUE TRUE FALSE NA FALSE
Since Toto’s height was missing, R can’t say whether or not he was more than 65 inches tall. Hence it assigns NA to the Toto-element of the tall vector.
When we subset using this vector we get an odd result:
heights[tall]
Scarecrow Tinman Lion <NA>
72 70 69 NA
Since R doesn’t know whether or not to select Toto, it records its indecision by including an NA in the result. That NA, however, is not the NA for Toto’s height in the vector heights, so it can’t inherit the “Toto” name. Since it has no name, R presents its name as <NA>.
If we try to count the number of tall persons, we get a misleading result:
length(heights[tall])
[1] 4
We would have preferred something like:
“Three, with another one undecided.”
Counting is one those situations in which we might wish to remove NA values at the start. If the vector is small we could remove them by hand, e.g.:
This it is reasonable that the sum and the mean should return NA: after all, if you don’t know what one of the numbers is, how can you find the sum of the numbers?
Similarly, the max() and the min() functions yield NA when one of the elements is NA:
max(3, 7, -2, NA)
[1] NA
min(3, 7, -2, NA)
[1] NA
You could say that NA is “contagious” in arithmetic functions.
Sometimes your vector will contain NAs, but you want to do arithemtic on the rest of the elements—the numbers that are notNA. You can do this with the na,rm parameter, whch all of the arithmetic functions know about. By defualt this parameter is set to FALSE (meaning: don’t remove the NA values) but you can set it to TRUE (meaning: remove all the NA values and then get on with the requested arithmetic). Here are some examples:
max(3, 7, -2, NA, na.rm =TRUE)
[1] 7
sum(some_other_numbers, na.rm =TRUE)
[1] 8
2.8.3.3 When Math Goes Wrong: NaN
The results of some arithmetical operations sometimes are not defined. (Examples: you can’t divide by 0; you can’t take the square root of a negative number.) R reports the results of such operations as NaN—“not a number.” R also issues a warning:
sqrt(c(-4, 2, 4))
Warning in sqrt(c(-4, 2, 4)): NaNs produced
[1] NaN 1.414214 2.000000
Keep in mind, though, that the result is a perfectly good vector as far as R is concerned. After the warning R will permit you to use it in further computations:
vec<-sqrt(c(-4, 2, 4))
Warning in sqrt(c(-4, 2, 4)): NaNs produced
vec +3
[1] NaN 4.414214 5.000000
2.8.4 Syntax
In the process of learning about R, you have been unconsciously imbibing some of its syntax. The syntax of a computer-programming is the complete set of rules that determine what combinations of symbols are considered to make a well-formed program in the language—something that R can interpret and attempt to execute.
2.8.5 Syntax Errors vs. Run-time Errors vs. Semantic Errors
For the most part you will learn the syntax informally. By now, for example, you have probably realized that when you call a function you have to supply a closing parenthesis to match the open parenthesis. Thus the following is completely fine:
sum(1:5)
[1] 15
On the other hand if you were to type sum(1:5 alone on a single line in a R script, R Studio’s code-checker would show a red warning-circle at that line. Hovering over the circle you would see the message:
unmatched opening bracket '('
If you were to attempt to run the command sum(1:5 from the script you would get the following error message:
## Error: Incomplete expression: sum(1:5
Such an error is called a syntax error.5 The R Studio IDE can detect most—but not all—syntax errors.
Syntax errors in computer programming are similar to grammatical errors in ordinary language, such as:
“Mice is scary.” (Number of the subject does not match the number of the verb.)
“Mice are.” (Incomplete expression.)
A run-time error is an error that occurs when the syntax is correct but R is unable to finish the execution of your code for some other reason. The following code, for example, is perfectly fine from a syntactical point of view:
sum("hello")
When run, however, it produces an error:
## Error in sum("hello") : invalid 'type' (character) of argument
Here is another example:
sum(emeraldCity)
Unless for some reason you have defined the variable emeraldCity, an attempt to run the above command will produce the following run-time error:
## Error: object 'emeraldCity' not found
Many run-time errors in computer programming resemble errors in ordinary language where the sentence is grammatically correct by does not mean anything, as in:
“Beelbubs are juicy.” (What’s a “beelbub”?)
There is a third type of error, known in the world of programming as a semantic error. The term “semantics” refers to the meaning of things. Computer code is said to contain a semantic error when it is syntactically correct and can be executed, but does not deliver the results one knows to expect.
As an example, suppose you have defined, at some point, two variables:
emeraldCity <-15emeraldcity <-4
Suppose now that—wanting R to compute \(15^2\)—you run the following code:
emeraldcity^2
[1] 16
You don’t get the results you wanted, because you accidentally asked for the square of the wrong number.
Semantic errors are usually the most difficult errors for programmers to detect and repair.
2.8.6 More About the Assignment Operator
We have been using the assignment operator <- to assign values to variables. You should be aware that there is another assignment operator that works the other way around:
4-> emeraldCityemeraldCity
[1] 4
Most people don’t use it.
A popular alternative to <- as an assignment operator is the equals sign =:
emeraldCity =5emeraldCity
[1] 5
I myself prefer to stay away from it, as it can be confused with other uses of =, such as the setting of values to parameters in functions:
rep("Dorothy", times =3)
[1] "Dorothy" "Dorothy" "Dorothy"
When you have to assign the same value to several values, R allows you to abbreviate a bit. Consider the following code:
a <- b <- c <-5
The above code has the same effect as:
a <-5b <-5c <-5
2.8.7 Multiple Expressions
R allows you to write more than one expression on a single line, as long as you separate the expressions with semicolons:
a <- b <- c <-5a; b; c; 2+2; sum(1:5)
[1] 5
[1] 5
[1] 5
[1] 4
[1] 15
2.8.8 More on Variable Names; Reserved Words
There is one further restriction on variable-names that we have not yet mentioned: you are not allowed to use any of R’s reserved words. These are:
if, else, while, repeat, function, for, in, next, break,TRUE, FALSE, NULL, inf, NaN, NA, NA_integer, NA_real, NA_complex, NA_character
You need not memorize the above list: You’ll gradually learn most of it, and words you don’t learn are words that you are unlikely to ever choose as a variable-name on your own. Besides, reserved words show in in blue in the R Studio editor, and if you manage to use one anyway then R will stop you outright with a clear error message:
break<-5
## Error in break <- 5 : invalid (NULL) left side of assignment
Notice that TRUE and FALSE are reserved words. R actually allows abbreviations: T for TRUE and F for FALSE. You can see this from the following expression:
c(T, F, F, T)
[1] TRUE FALSE FALSE TRUE
However, T and F are not reserved words: instead they are just variable-names, and R has bound them (in its base package) to TRUE and FALSE respectively. since they are just variable-names, They could be bound to other values.
This can lead to problems in code, if someone chooses to bind T or F to some value and you are not aware of thier choice.
For example, suppose that have two lines of code like this:
T <-0F <-1
Later on, suppose you create what you think is a logical vector:
myVector <-c(T, F, F, T)
But it’s not logical:
typeof(myVector)
[1] "double"
That’s because T ad F have been bound to numerical values. If you coerce myLogical to a logical vector, you get the exact opposite of what you would have expected:
as.logical(myVector)
[1] FALSE TRUE TRUE FALSE
The moral of the story is:
Warning
Don’t use T for TRUE or F for FALSE, even though R allows it.
One final remark: variables together with reserved words constitute the part of the R language called identifiers.
2.8.9 Practice Exercises
Suppose that you begin a new R session and that you run the following code:
What sort of error (syntax, runtime or semantic) is produced by the next piece of code, which is intended to produce the names of the people with more than 15 years of education? Why?
person(yearsEducation >15]
What sort of error (syntax, runtime or semantic) is produced by the next piece of code, which is intended to produce the names of the people who don’t have pets? Why?
person(!haspets]
What sort of error (syntax, runtime or semantic) is produced by the next piece of code, which is intended to find out how many people have pets? Why?
length(hasPets)
2.8.10 Solutions to the Practice Exercises
This will result in a syntax error. You need brackets to select and you’ve got a parenthesis n the left. The correct syntax would be:
person[yearsEducation >15]
This will result in a runtime error. The variable haspets is not defined, so R will issue a “can’t find” error when the code is executed. Probably you meant:
person[!hasPets]
This will result in a semantic error. You’ll get 5 (the number of elements in the vector hasPets) What you wnat can be accomplished by either one of the following:
sum(!hasPets) # nice and snappy!length(hasPets[!hasPets]) # kinda awkward, but it works
2.8.11 Optional Glossary (For This Section Only)
Reserved Words
Identifiers that are set aside by R for specific programming purposes. They cannot be used as names of variables.
Syntax
The complete set of rules for a computer language that determine what combinations of symbols are considered to make a well-formed program in the language.
Syntax Error
A sequence of symbols that contains a violation of one of the rules of syntax. R is unable to interpret and attempt to execute code that contains a syntax error.
Run-time Error
An error that occurs when the computer language’s interpreter attempts to execute code but is unable to do so. A typical cause of a run-time error is the situation when the code calls for the evaluation of a name that has not been bound to an object.
Semantic Error
An error in code that is syntactically correct and that can be executed by the computer but which produces unexpected results.
The Main Ideas of This Chapter
We meet four types of atomic vectors in this course: logical, integer, character and double.
logical: TRUEs and FALSEs;
integer: put an L after a whole number, like 3L or -2L;
double: these can have decimal points, like 3.1415, and can also be expressed with scientific notation, like 5.1e06 (which stands for \(5 \times 10^6\), or 5100000);
character: these are strings "Dorothy" and "3.14".
The c() function concatenates (combines) vectors together.
Everything in R is a vector, even things that look like lone values: 3.2 or TRUE or "Dorothy".
letters gives the lowercase letters of the English alphabet, and LETTERS gives you the uppercase letters.
You can use rep() and seq() and the colon operator : to construct various patterned vectors. Know the arguments for the rep() and seq() functions, and know how to use them in combination to make complex patterned vectors.
You can subset vectors with the [ operator, e.g. letters[1:5] gives you the first five lowercase letters.
Sub-setting with logical vectors: Boolean operators help you make Boolean expressions that evaluate to logical vectors. These can be used in sub-setting, as in letters[letters <= "r].
Review which(), any() and all(), but especially keep in mind the “in”-operator %in%, as in 3 %in% 5:400 (which is FALSE by the way).
R does the basic arithmetic you would expect. Especially keep in mind the mod-operator %% that finds remainders.
Know the rules for what makes a legal variable-name in R.
Links to Class Slides
Quarto Presentations that I sometimes use in class:
Any one of the six basic forms the elements in an atomic vector can take. The four types we will encounter the most are: double, integer, character and logical.
Coercion
The process of changing a vector from one type to another. Sometimes the process takes place automatically, as a convenience to the programmer.
Sub-setting
The operation of selecting one or more elements from a vector.
Recycling
An automatic process by which R, when given two vectors, repeats elements of the shorter vector until it is as long as the longer vector. Recycling enables the two resulting vectors to be combined element-wise in operations.
Vectorization
R’s ability to operate on each element of a vector, producing a new vector of the same length. Vectorized operations can be expressed concisely and performed very quickly.
Exercises
Exercise 1
Determine the type of each of the following vectors:
c(3.2, 2L, 4.7, TRUE)
c(as.integer(3.2), 2L, 5L, TRUE)
c(as.integer(3.2), 2L, "5L", TRUE)
Exercise 2
Using a combination of c(), rep() and seq() and other operations, find concise one-line programs to produce each of the following vectors:
all numbers from 4 to 307 that are one more than a multiple of 3;
the numbers 0.01, 0.02, 0.03, …, 0.98, 0.99.
twelve 2’s, followed by twelve 4’s followed by twelve 6’s, …, followed by twelve 10’s, finishing with twelve 12’s.
one 1, followed by two 2’s, followed by three 3’s, …, followed by nine 9’s, finishing with ten 10’s.
Exercise 3
Using a combination of c(), rep() and seq() and other operations, find concise one-line programs to produce each of the following vectors:
the numbers 15, 20, 25, …, 145, 150.
the numbers 1.1, 1.2, 1.3, …, 9.8, 9.9, 10.0.
ten A’s followed by ten B’s, …, followed by ten Y’s and finishing with ten Z’s. (Hint: the special vector LETTERS will be useful.)
one a, followed by two b’s, followed by three c’s, …, followed by twenty-five y’s, finishing with twenty-six z’s. (Hint: the special vector letters will be useful.)
Exercise 4
The following three vectors gives the names, heights and ages of five people, and also say whether or not each person likes Toto:
We will look briefly at R’s object-oriented capabilities in Chapter 15.↩︎
R is a bit more forgiving if you type sum(1:5 directly into the console and press Enter. Instead of throwing an error, R shows a + prompt, hoping for further input that would correctly complete the command. If you are ever in the situation where you do not know how to complete the command, you may simply press the Escape key (upper left-hand corner of your keyboard): R will then abort the command and return to a regular prompt.↩︎