typeof(numVec)
[1] "double"
yarns <- rep(yarn, times = 4)
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.
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:
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. Try this:
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 output from running the previous code, 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.
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
:
<- c(3L, 17L, -22L, 45L)
intVec 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
:
<- c(3, 17, -22, 45)
numVec2 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:
<- c(
strVec "Brains", "are", "not", "the", "best",
"things", "in", "the", "world", "93.2"
) strVec
[1] "Brains" "are" "not" "the" "best" "things" "in" "the"
[9] "world" "93.2"
typeof(strVec)
[1] "character"
Notice that "93.2"
makes a string, not a number.
The last type of vectors to consider are the logical
vectors. Here is an example:
<- c(TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE)
logVec logVec
[1] TRUE FALSE TRUE TRUE FALSE FALSE FALSE
In order to represent a logical value you use:
TRUE
to represent truth;FALSE
to represent falsity.You need all-caps: if you try anything else—like the following—you get an error (try it):
What would happen if you tried to represent falsity with the string "false"
?
<- c(TRUE, "false")
newVector 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
,integer
,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:
<- c(3, 2.5, -7.32, 0)
numVec 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
.
You can combine vectors you have already created to make new, bigger ones:
<- c(5, 3, 10)
numVec1 <- c(1, 2, 3, 4, 5, 6)
numVec2 <- c(numVec1, numVec2)
numCombined numCombined
[1] 5 3 10 1 2 3 4 5 6
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.
Consider the following vector, which we may think of as recording the heights of people, in inches:
<- c(72, 70, 69, 58, NA, 45) heights
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).
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. You can see by running the code below:
NA
is of type logical
. See for yourself:
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”!
The elements of a vector can have names, if we like:
<- c(Bettina = 32, Chris = 64, Ramesh = 101)
ages 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:
names(heights) <- c("Scarecrow", "Tinman", "Lion", "Dorothy", "Toto", "Boq")
heights
Scarecrow Tinman Lion Dorothy Toto Boq
72 70 69 58 NA 45
R comes with two handy, predefined character vectors. Run this code:
We will make use of them from time to time.
The length()
function tells us how many elements a vector has:
length(heights)
[1] 6
Quite often we need to make lengthy vectors that follow simple patterns. R has a few functions to assist us in these tasks.
Consider the seq()
function:
The default value of the parameter by
is 1, so we could get the same thing with:
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):
Some more complex examples:
R will go up to the to
value, but not past it:
Negative steps are fine:
The colon operator :
is a convenient abbreviation for seq
:
If the from
number is greater than the to
number the step for the colon operator is -1:
With rep()
we may repeat a given vector as many times as we like:
We can apply rep()
to a vector of length greater than 1:
rep()
applies perfectly well to character-vectors:
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:
<- c(7, 3, 4)
vec 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:
<- seq(5, -3, -2)
vec rep(vec, each = 2, times = 2)
[1] 5 5 3 3 1 1 -1 -1 -3 -3 5 5 3 3 1 1 -1 -1 -3 -3
In order to create fifty 10’s followed by fifty 30’s followed by fifty 50’s I would write the following code. (Try it if you want to verify what it produces.)
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:
4] heights[
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:
<- c(1,5)
desired heights[desired]
Scarecrow Toto
72 NA
We could also ask for them directly:
c(1,5)] heights[
Scarecrow Toto
72 NA
Negative numbers are significant in subsetting:
-2] #select all but second element heights[
Scarecrow Lion Dorothy Toto Boq
72 69 58 NA 45
-c(1,3)] # all but first and third heights[
Tinman Dorothy Toto Boq
70 58 NA 45
If you specify a nonexistent index, you get NA
, the reasonable result:
7] heights[
<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:
1:3] heights[
Scarecrow Tinman Lion
72 70 69
The following gives the same as heights
:
1:length(heights)] 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. Try this:
If a vector has names we can refer to its elements using the subsetting operator and those names:
"Tinman"] heights[
Tinman
70
c("Scarecrow", "Boq")] heights[
Scarecrow Boq
72 45
Finally, we can use subsetting to modify parts of a vector. For example, Dorothy’s height is reported as:
"Dorothy"] heights[
Dorothy
58
If Dorothy grows two inches, then we can modify her height as follows:
"Dorothy"] <- 60 heights[
We can replace more than one element, of course. Thus:
c("Scarecrow", "Boq")] <- c(73, 46) heights[
The subset of indices may be as complex as you like. Try this:
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. Try the example below:
Run the following expression:
We constructed it with the “less-than” operator <
. You can think of it as saying 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
Let’s look further into Boolean expressions. Define the following two vectors:
<- c(10, 13, 17)
a <- c(8, 15, 12) b
Now let’s evaluate the expression a < b
:
< b a
[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.
Operation | What It Means |
---|---|
< | less than |
> | greater than |
<= | less than or equal to |
>= | greater than or equal to |
== | equal to |
& | and |
| | or |
! | not |
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. Try this:
The reasons for the evaluation above are as follows:
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 (run the code):
(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:
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”. Try this:
When TRUE (itself of
type 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 TRUE
and 1 are not identical, as you can verify by running the code below:
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 (try this):
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 (try this):
The final Boolean operator is !
, which works like “not” (try these):
Consider the vector
<- c(2, 6, 1, 7, 3) vec
Look at what happens when we evaluate the expression:
> 4 vec
[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:
<- 1:6
vec2 > c(3,1) vec2
[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:
<- 1:7
vec2 > c(3, 8) vec2
## 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:
suppressWarnings(vec2 > c(3, 8))
[1] FALSE FALSE FALSE FALSE TRUE FALSE
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:
<- c(TRUE, TRUE, FALSE, TRUE, FALSE, FALSE)
wanted heights[wanted]
Scarecrow Tinman Dorothy
73 70 60
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:
<- c(55, 64, 67, 70, 63, 72)
people <- (people >= 70)
tall 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:
>= 70] people[people
[1] 70 72
I like to pronounce the above as:
people
, wherepeople
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. Try 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. Try 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:
<- c(55, 64, 67, 70, 63, 72) people
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:
<- c(TRUE, TRUE, FALSE, FALSE, FALSE, TRUE) logical_vector
Now suppose we decide to add up those TRUE
s and FALSE
es. 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 many TRUE
s it contained.
Back to people
. people < 70
is a Boolean expression, so its result is a logical vecotr:
< 70 people
[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!
There are several functions on logical vectors that are worth keeping in your back pocket:
which()
any()
all()
which()
Applied to a logical vector, the which()
function returns the indices of the vector that have the value TRUE
. Try this:
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()
.
any()
and %in%
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. Try this:
The above question occurs so frequently that R provides the %in%
operator as a short-cut. Try these examples:
all()
Is everyone more than 71 inches tall?
all(heights > 71)
[1] FALSE
R provides all of the familiar arithmetical operations. Table 2.2 shows the basic operators.
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. Try these examples:
As an illustration, the final result above 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. try this:
Recycling applies in vector arithmetic (as in most of R). Try this:
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 (try it):
%/%
) and Remainder (%%
)R provides two special operations that are related to division:
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:
The %/%
operator finds the quotient for you. Try this:
The %%
operator in R finds the remainder. Try it:
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. Try this:
As an application, let’s find all the numbers in vec
that are multiples of 6. It’s a two-step process:
<- 5:20
vec <- vec %% 6 == 0
is_multiple_of_6 is_multiple_of_6
[1] FALSE TRUE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
[13] FALSE TRUE FALSE FALSE
vec
with logical sub-setting: vec[is_multiple_of_6]
[1] 6 12 18
Of course it is perfectly fine to do it all in one line:
%% 6 == 0] vec[vec
[1] 6 12 18
We would pronouce this as: “vec, where vec mod 6 is 0”.
Indeed, the mod-operator comes in handy quite often. Here are several more quick applications:
%% 2 == 0] # just the even numbers in vec vec[vec
[1] 6 8 10 12 14 16 18 20
%% 2 == 0] # just the odd numbers in vec vec[vec
[1] 6 8 10 12 14 16 18 20
%% 3 == 1] # numbers one more than a multiple of 3 vec[vec
[1] 7 10 13 16 19
sum()
and the Mean (mean()
)There are some functions on vectors that return only a vector of length 1. Among the examples we have met so far are:
length()
any()
all()
We have also met sum()
, which returns the sum of all of the elements of a vector that it is given:
<- 1:100
first_hundred_numbers sum(first_hundred_numbers)
[1] 5050
In statistics we are often interested in the mean of a list of numbers. The mean is defined as:
\[\frac{\text{sum of the numbers}}{\text{how many numbers there are}}\] You can find the mean of a numerical vector as follows:
<- c(-3, 4, 17, 23, 51)
vec <- sum(vec)/length(vec) meanVec
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
The max()
function delivers the maximum value of the elements of a numerical vector. Try this:
The min()
function delivers the minimum value of a numerical vector. Try this:
You can enter more than one vector into min()
or max()
: the function will combine the vectors and then do its job. Try this:
The pmax()
function compares corresponding elements of each input-vector and produces a vector of the maximum values. Try this:
There is a pmin()
function that computes pair-wise minima as well. Try this:
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:
In R, a legal name:
a
through z
and also A
through Z
;0
,1
, through 9
;.
and the underscore _
.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
Here are a few more useful math functions involving vectors.
Consider the this vector:
<- c(4, 5, -3, 2, 0, 8) sample_numbers
If you sum the elements of this vector up to each of its successive indices, you get:
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\).
You can use the round()
function to round off numbers to any desired number of decimal places.
<- sqrt(1:5)
roots # Too much information! roots
[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
The ceiling()
function returns the least integer that is greater than or equal to the given number:
<- c(-2.9, -1.1, 0.2, 1.35, 3, 4.01)
vec ceiling(vec)
[1] -2 -1 1 2 3 5
The floor()
function returns the greatest integer that is less than or equal to the given number:
floor(vec)
[1] -3 -2 0 1 3 4
Note that vectorization applies to all of these functions.
Read this section if you are want to learn as much about R as quickly as you can.
R has a special number called Inf
. It is bigger than any real number, even a very big one like &10^50$
10^50 < Inf
[1] TRUE
Accordingly, -Inf
is less than any real number:
-Inf < 334
[1] TRUE
-Inf < -10^50
[1] TRUE
R has opinions about arithmetic with infinities. They make sense if you have done a couple of semesters of calculus:
mean(c(5, -1000, Inf))
[1] Inf
Inf + 1
[1] Inf
Inf - 1000
[1] Inf
Inf - Inf
[1] NaN
NA
NA
in SubsettingThink back to the heights of our Oz characters:
<- c(72, 70, 69, 58, NA, 45)
heights names(heights) <- c("Scarecrow", "Tinman", "Lion", "Dorothy", "Toto", "Boq")
heights
Scarecrow Tinman Lion Dorothy Toto Boq
72 70 69 58 NA 45
You should be aware of the effect of NA
-values on subsetting.
<- (heights > 65)
tall 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.:
<- heights[-5] # remove Toto
knownHeights <- (knownHeights > 65)
tall length(knownHeights[tall])
[1] 3
For longer vectors the above approach won’t be practical. Instead we may use the is.na()
function.
is.na(heights)
Scarecrow Tinman Lion Dorothy Toto Boq
FALSE FALSE FALSE FALSE TRUE FALSE
Then we may select those elements that are not NA
:
<- heights[!is.na(heights)]
knownHeights knownHeights
Scarecrow Tinman Lion Dorothy Boq
72 70 69 58 45
length(knownHeights[knownHeights > 65])
[1] 3
Is everyone more than 40 inches tall?
all(heights > 40)
[1] NA
Everyone with a known height is taller than 40 inches, but because Toto’s height is NA
R can’t say whether all the heights are bigger than 40.
NA
s in Arithmetic, and na.rm
When an arithmetic function produces one number out of many numbers, it is very susceptible to the presence of NA
values.
For example, look at:
<- c(3, 7, -2)
some_numbers sum(some_numbers)
[1] 8
<- c(3, 7, -2, NA)
some_other_numbers sum(some_other_numbers)
[1] NA
mean(some_other_numbers)
[1] NA
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 NA
s, but you want to do arithemtic on the rest of the elements—the numbers that are not NA
. 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
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:
<- sqrt(c(-4, 2, 4)) vec
Warning in sqrt(c(-4, 2, 4)): NaNs produced
+ 3 vec
[1] NaN 4.414214 5.000000
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.
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:
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:
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:
<- 15
emeraldCity <- 4 emeraldcity
Suppose now that—wanting R to compute \(15^2\)—you run the following code:
^2 emeraldcity
[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.
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 -> emeraldCity
emeraldCity
[1] 4
Most people don’t use it.
A popular alternative to <-
as an assignment operator is the equals sign =
:
= 5
emeraldCity emeraldCity
[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:
<- b <- c <- 5 a
The above code has the same effect as:
<- 5
a <- 5
b <- 5 c
R allows you to write more than one expression on a single line, as long as you separate the expressions with semicolons:
<- b <- c <- 5
a 2+2; sum(1:5) a; b; c;
[1] 5
[1] 5
[1] 5
[1] 4
[1] 15
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:
<- 0
T <- 1 F
Later on, suppose you create what you think is a logical vector:
<- c(T, F, F, T) myVector
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:
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.
Suppose that you begin a new R session and that you run the following code:
<- c(
person "Abe", "Bettina", "Candace",
"Devadatta", "Esmeralda"
)<- c(2, 1, 0, 2, 3)
numberKids <- c(12, 16, 13, 14, 18)
yearsEducation <- c(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE) hasPets
person(yearsEducation > 15]
person(!haspets]
length(hasPets)
> 15] person[yearsEducation
haspets
is not defined, so R will issue a “can’t find” error when the code is executed. Probably you meant:!hasPets] person[
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
Identifiers that are set aside by R for specific programming purposes. They cannot be used as names of variables.
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.
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.
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.
An error in code that is syntactically correct and that can be executed by the computer but which produces unexpected results.
TRUE
s and FALSE
s;3L
or -2L
;3.1415
, and can also be expressed with scientific notation, like 5.1e06
(which stands for \(5 \times 10^6\), or 5100000
);"Dorothy"
and "3.14"
.c()
function concatenates (combines) vectors together.3.2
or TRUE
or "Dorothy"
.letters
gives the lowercase letters of the English alphabet, and LETTERS
gives you the uppercase letters.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.[
operator, e.g. letters[1:5]
gives you the first five lowercase letters.letters[letters <= "r]
.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).%%
that finds remainders.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.
The process of changing a vector from one type to another. Sometimes the process takes place automatically, as a convenience to the programmer.
The operation of selecting one or more elements from a vector.
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.
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.
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)
Using a combination of c()
, rep()
and seq()
and other operations, find concise one-line programs to produce each of the following vectors:
Using a combination of c()
, rep()
and seq()
and other operations, find concise one-line programs to produce each of the following vectors:
LETTERS
will be useful.)letters
will be useful, and you should review Practice 2.8.)For Exercises 4, 5, 6, and 7 it is a good idea to first review Section 2.5.
The following three vectors gives the names, heights and ages of five people, and also say whether or not each person likes Toto:
<- c("Akash", "Bee", "Celia", "Devadatta", "Enid")
person <- c(23, 21, 22, 25, 63)
age <- c(68, 67, 71, 70, 69)
height <- c(TRUE, TRUE, FALSE, FALSE, TRUE) likesToto
Use sub-setting with logical vectors to produce vectors of:
Exercises 5, 6 and 7 below use the vectors defined in Exercise 4.
Use sub-setting with logical vectors to produce vectors of:
Use the sum()
function along with logical vectors to find:
Hint: Review Section 2.5.1.
Read the previous problem, and then use sum()
along with logical vectors to find:
Hint: Review Section 2.5.1.
So-called after George Boole, a nineteenth century British logician.↩︎
A vector is said to be numerical if it is of type integer
or double
.↩︎
See help(make.names)
.↩︎
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.↩︎