Week 3: R objects, attributes and working with data.frames, vectors, matrices, and lists
Week 3: R objects, attributes and working with data.frames, vectors, matrices, and lists
No matter if you are an expert or a novice, one of the most frustrating aspects of R computing can be working with objects, specifically – different types of objects. Going back to week 1, we learned that R is an object oriented language and that we manipulate objects in R to analyze data, simulate processes, create graphics, and write files. Sounds all rather simple, right? Well, it is... and it isn’t. There are different types of objects in R and many operations (e.g., addition, subtraction, etc.) can only be performed on specific types of objects. Therefore, it is important to know the different types of objects, how to create a query in R to identify the type of object, and how to change (coerce) the type of object. In this lesson, we will learn these skills and others. The script for today's lesson can be found here.
CREATING AN R OBJECT
In week 1, we learned to create a simple object consisting on a single number using one of two assignment operators “=” and “<-". For example, if we enter the code below we create an object "Z" and assign it a value of 10.
# using the = operator
Z= 10
# or using the assignment “<-“ operator
Z <- 10
These single value objects are often referred to as scalars if they represent numeric values (i.e., a single number). To create an object consisting of several numbers (often called a vector), you need to use the collection operator “c(numbers in here separated by a comma)”. For example, let’s create a vector named “jims.vect” consisting of the numbers 1 through 5.
# create vector
jims.vect <- c(1,2,3,4,5)
# print out vector
jims.vect
As we will learn later, we can perform operations on entire vectors and individual elements or groups of elements of vectors. For example, we can create another vector that contains the values of jims.vect divided by ten using the following:
# divide all the values in the vector by 10
new.vect <- jims.vect/10
# print out vector
new.vect
Vectors consist of elements that are indexed by the order in which that occur. For example the third element of “new.vect” is 0.3. We can refer to the elements of a vector using a bracket “[]” with the number in the bracket referring to the element in the vector. For example, let’s print out the 4th element of new.vect:
# print out 4th element
new.vect[4]
## assign 4th element to another object, fourth
fourth = new.vect[4]
We can also refer to multiple elements in a vector. For example:
# print elements 3, 4, and 5 in new.vect
new.vect[3:5]
Notice above that we used a colon (:) to refer to a sequence of elements from 3 to 5. This sequence notation will come in handy and has other uses. For example, let create a vector “biggy” consisting of integers (whole numbers) from 10 to 20:
## create vector
biggy = c(10:20)
#print out vector
biggy
Notice that it uses the collection operator “c()” and the list separator “:”. There are all kinds of neat functions in R for creating vectors. Below are a few that we find useful with a description of each in the comment above.
# create a vector with a sequence of values from 1.25 to 8.75 by increments of 0.25
wow <- seq(1.25, 8.75, by = 0.25)
# create a vector with a sequence of 13 evenly spaced values between 9 and 14
double.wow <- seq(9, 14, length = 13)
# create a vector of 13 elements with the same value 41
double.dog.wow <- rep(41,13)
# create a vector consisting of two sequences of 1,2,3,4
triple.dog.wow <- rep(1:4, 2)
Be sure to try each of these methods and print out the results. There are other uses for these functions so be sure to use “help()” to see what is possible.
You may be wondering, what if I want to select non-consecutive elements of a vector? No problemo, we have a list of the value inside a the collection operator that is inside of the brackets. You’re probably asking: What the @#$%% are you guys talking about? Here-- let’s show you below. First, let’s create a vector consisting of values from 1 to 10 by increments of 0.5, then select the odd numbered elements.
# create a vector with a sequence of values from 1 to 10 by increments of 0.5
nuts <- seq(1,10, by = 0.5)
# select the odd numbered elements of nuts and put them into a vector wing.nuts
wing.nuts = nuts[c(1,3,4,5,7,9,11,13,15,17,19)]
Typing in all of those numbers in the list to get wing.nuts was a pain. Notice that the numbers consisted of integers that went from 1 to 19 by increments of two. Above, we learned that we could generate a sequence of numbers using “seq”. Hmm… wonder if we can combine these two ideas...let’s try:
# select the odd numbered elements of nuts using seq and put them into a vector wing.nuts
wing.nuts = nuts[seq(1,19,2)]
Wow that worked! There are all kinds of tricks like this that can be used in R and the only way to discover them is to try different things. Use your imagination and don’t be afraid, try something else. Seriously--- we're waiting here till you try to combine a couple of commands.The worst that could happen is the dreaded red error message. Trial and error is how most of us learn these tricks.
CREATING MATRICES
We just learned to create single value (scalars) objects and a vectors consisting of a single row or single column of values. Matrices consist of multiple values contained in multiple rows and columns. For example, the matrix below consists of 4 rows and 3 columns and can be referred to as a 4 by 3 matrix (or 4x3 matrix):
1 2 3
4 5 6
7 8 9
10 11 12
We can create this matrix in R using the “matrix” function (who’d a thunk it?). First, let’s create a vector with values from 1:12.
### create the vector the hard way
vect = c(1,2,3,4,5,6,7,8,9,10,11,12)
### create the vector the easy way
vect = c(1:12)
Now we can use the matrix function, but note that we need to specify the number of rows or columns using the “nrow” or “ncol” options, respectively. We may need to specify one more thing. Let’s see what happened without it.
### create the vector the easy way
vect = c(1:12)
## create the 4 by 3 matrix using the values in vect
jims.matrix = matrix(vect, nrow = 4)
## create the 4 by 3 matrix using the values in vect
jims.matrix = matrix(vect, ncol = 3)
The matrix jims.matrix should look like this when printed:
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
[4,] 4 8 12
Notice that the values are in order going down the first column 1-4. This is not quite what we have above where the values are in order across rows. This is because the default for the matrix function is to place values from the matrix by column. This mean that the first 4 elements on “vect” are the values in the first column, the second 4 values in “vect” are in the second column and so forth. We can get a matrix like the original one above specifying “byrow = TRUE” option in the matrix function as:
## create the 4 by 3 matrix using the values in vect
jims.matrix = matrix(vect, ncol = 3, byrow = TRUE)
## print out the matrix
jims.matrix
When you execute the above code you should get:
[,1] [,2] [,3]
[1,] 1 2 3
[2,] 4 5 6
[3,] 7 8 9
[4,] 10 11 12
This looks just like the above (original) matrix. Similar to vectors, we can refer to elements in a matrix using numbers inside if brackets corresponding to the row and column contain the element separated by a comma: matrix.name[row,column]. The first number always refer to the row copy and paste this code in R and see what happens:
### print out value in row 2, column 3 in jims.matrix
jims.matrix[2,3]
## print out the values in rows 2 and 3 in the first column
jims.matrix[2:3,1]
## print out the values in rows 1 and 3 in the second column
jims.matrix[c(1,3),2]
## print out all the rows for columns 1 and 3, notice blank for row
jims.matrix[,c(1,3)]
## print out all the columns for row 2, notice blank for column
jims.matrix[2,]
You should notice the use of the sequence operator “:” and collection operator “c()” to refer to specific rows and columns. You should also notice that you refer to all rows or all columns by leaving the value for the row or column blank. We can also change the values of specific elements of a matrix (or vector) using the above notation. For example,
## change the value in row 2, column 1 to -99
jims.matrix[2,1] <- -99
## print out matrix
jims.matrix
## change the values in rows 1 and 4, column 3 to missing, remember NA is missing
jims.matrix[c(1,4),3] <- NA
## print out matrix
jims.matrix
## change the values in row 4, column 2 to the sum of values in row 3, columns 1 and 2
jims.matrix[4,2] <- jims.matrix[3,1] + jims.matrix[3,2]
## print out matrix
jims.matrix
Go ahead and try different things, but be forewarned that any operation on a missing value (NA) will result in NA.
WORKING WITH CHARACTER VALUES AND STRINGS
So far, we have only assigned numeric values and missing values to R objects. Many times we may wish to assign characters (e.g., a, b, c) or a string of characters (dog, cat) to an R object. For example, we may want a species or site name. To distinguish numeric and character variables, we place the latter (character) inside single quotes. For example, let’s create an R object species and assign it a value 'dog'
## create species assign “dog”
species = ‘dog’
#print out
species
Similar to numeric values, we also can create a vector that contains several strings or character values using the collection operator. Note that we must use double quotes in place of single quotes to delineate the character strings.
## create species.vect and assign pet names
species.vect = c("dog","cat","hamster")
#print out
species.vect
(WARNING, WARNING: copying and pasting the above may result in errors because R or Rstudio may not recognize the quotes as quotes, so if an error occurs. Type in the code by hand)
There also are neat tricks for working with characters, similar to tricks with numeric values. For example, let's say I want to create a vector containing the letters a to g in order.
# create vector alphabet the hard way
alphabet <- c("a","b","c","d","e","f","g")
#print it out
alphabet
# create vector alphabet the easy way
alphabet <- letters[1:10]
#print it out
alphabet
We also can create a matrix that contains several characters/strings using the matrix function.
## create pet.vect and assign pet names
pet.vect = c("dog","cat","hamster","goldfish","mouse","bird")
## create a matrix
pet.matrix = matrix(pet.vect, ncol = 3, byrow = TRUE)
## print it out
pet.matrix
(WARNING, WARNING: copying and pasting the above may result in errors because R or Rstudio may not recognize the quotes as quotes, so if an error occurs. Type in the code by hand)
Pretty neat, eh? We can refer to specific elements in character/string vectors and arrays using the exact same notation that we used with numeric values. For example,
## print out the values in row 1 column 1 and 3
pet.matrix[1,c(1,3)]
## print out all the rows for columns 1
pet.matrix[,1]
Try some other combinations of rows and columns just to get the hang of it.
The preceding may have spawned an idea, maybe I can create a matrix with numeric values and characters. Let’s try that Ok, we want to create something that looks like this matrix:
a 1 5
b 2 6
c 3 7
d 4 8
To do this, first create a vector that contains all of these elements
# the hard way
trial<- c("a","b","c","d",1,2,3,4,5,6,7,8)
#print it out
trial
#easier way
trial<- c(letters[1:4],1:8)
#print it out
trial
Did you notice anything interesting when you printed out the vector? Maybe you noticed the fact that the numbers that printed out had double quotes around them like this:
[1] "a" "b" "c" "d" "1" "2" "3" "4" "5" "6" "7" "8"
This means that R is treating numeric variables just like characters. If fact, a vector or a matrix cannot contain mixtures of numeric and character variables. Just to emphasize:
A vector or a matrix cannot contain mixtures of numeric and character variables in R.
R automatically treats all of the elements as character variables. What does that mean???? Before we investigate that, let’s finish what we started and create the matrix with the “trial” vector. We will use the matrix function to create the matrix. We can see above that we want 3 columns so “ncol = 3” we also see that we want the first 4 elements of “trial” to be the first column of the matrix, so we want the function to place elements in the matrix by column and “byrow = FALSE”:
## create mixed up matrix
trial.n.error <-matrix(trial,ncol = 3, byrow = FALSE)
# print it out
trial.n.error
and you should obtain
[,1] [,2] [,3]
[1,] "a" "1" "5"
[2,] "b" "2" "6"
[3,] "c" "3" "7"
[4,] "d" "4" "8"
Just like the before, R put double quotes around the numbers. That is, it considers the numbers in columns 2 and 3 to be characters. Ok, now we are ready to address: what does this mean???? We learned above that we can perform operations on specific elements of a vector and matrix. Let’s see what happened when we attempt to add columns 2 and 3:
## add columns 2 and 3 on trial.n.error matrix
trial.n.error[,2] + trial.n.error[,3]
after submitting this code to R you should have gotten
Error in trial.n.error[, 2] + trial.n.error[, 3] :
non-numeric argument to binary operator
This is because R thinks the numbers are letters. We can determine the type and properties of objects using several R functions. Among the most useful functions is typeof. Go ahead and find out what the function does using help, i.e., help(typeof). You should see its syntax and various uses. Let find out what type object trial.n.error is.
### what is this
typeof(trial.n.error)
##we can do the same for individual columns or rows
typeof(trial.n.error[,2])
## what class of object is trial.n.error
class(trial.n.error)
As you should see, the printout in the console indicates that the objects is “character” and the class function tells us that it is a matrix. Try using the typeof and class functions on some of the other objects. For Rstudio users, you should be able to see that the characteristics or each object in the workspace in the “workspace” window.
We can use other functions, in particular the “is.” functions, to determine the type of object here are two “is.” functions below:
## is trial.n.error a numeric matrix
is.numeric(trial.n.error)
## is trial.n.error a character matrix
is.character(trial.n.error)
We can change the characteristics of an object (this is caller coercion of an object) using the “as.” functions. To illustrate, create an object “fix” that contains columns 2 and 3 from “trial.n.error”. Then let’s check out the characteristics of the object.
# create new object
fix<- trial.n.error[,2:3]
# what is this type of object
class(fix)
# is fix a matrix, notice another is function-- of course!
is.matrix(fix)
## what are the type of variables in fix
typeof(fix)
## what are the attributes of fix, yes another function
attributes(fix)
The R console should have indicated that fix was a matrix, (is.matrix = TRUE), containing character values, and the dimensions of the matrix, $dim, is 4 by 2. Notice that the characteristics of the fix matrix are the same as the trial.n.error matrix, with the exception of a missing column. This means that the new objected inherited the characteristics of its parent object. This idea of inheritance is a very important concept. Any object that you create from another object will have the SAME characteristics of the first object. Allllrighty, let's change fix to become a numeric matrix:
# coerce fix to become numeric
fixed <- as.numeric(fix)
#print it out
fixed
# what is this type of object-- numeric
class(fixed)
# is fixed a matrix, -- oh no!
is.matrix(fixed)
## what are the type of variables in fixed-- yes numeric!
is.numeric(fixed)
What happened? Fixed contain numeric values but is no longer a matrix. It is now a vector. How do we get a matrix back?... maybe the matrix function?:
fixed <- as.numeric(fix)
#print it out
fixed <- matrix(fixed,ncol = 2, byrow = FALSE)
fixed
# what is this type of object-- matrix
class(fixed)
Ok, that worked. You also could have created a couple of different ways. Can you think of any? Go ahead any try one or two. We can now add the two columns. In fact we can create a new object as the sum of columns 1 and 2
#add column 1 and 2 of fixed
fixed[,1] + fixed[,2]
#create a third column in fixed by adding column 1 and 2 of fixed
new.val <- fixed[,1] + fixed[,2]
#print it out
new.val
Hy guys, what happened is we coerce letters to make them numeric values? Well, lets try it and find out using the character matrix trial.n.error:
# create vector curious by coercing trial.n.error
curious<-as.numeric(trial.n.error)
## print it out
curious
I created numeric values in a vector, but it also assigned the missing values (NA) to the instances where it tried to coerce an actual character. How do we find out is an object contains missing values? Hmmmm… if we want to find out if an object is a character we use “in.character”, if we want to find out is an object is numeric we use “is.numeric”, what if we want for found out is something is NA (missing). If you’re thinking “is.na” then you are correct. Let’s try that:
## are there missing values in curious
is.na(curious)
You should get:
[1] TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
Notice that the value is TRUE when the element in curious is NA and FALSE when it isn't.
The “is.na” function is very very useful and will come in handy. To illustrate, let’s say that you wanted to replace the missing values with zero or some other default value, say -666, you would use the following:
#replace missing elements in curious with zero
curious[is.na(curious)] <- 0
#print it out
curious
Notice the syntax, R replaces the elements in the vector where is.na(curious) is TRUE. Hmmm maybe this means that any element that meets some criteria can be changed? Let’s try. How about changing all zeros to -666 in other words change all the values where curious == 0 is TRUE.
#replace zero elements in curious with -666
curious[curious== 0] <- -666
#print it out
curious
Finally in this subsection we will address a question in the back of your mind. Can we coerce a numeric variable to become a character variable, why yes! And you can probably guess what function we will be using: “as.character”.
## create numeric vector num.vect with values 1:15
num.vect = c(1:15)
## print it out
num.vect
# create character vector char.vect by coercing num.vect
char.vect= as.character(num.vect)
## print it out
char.vect
Yes, coercion can be a good thing.
DATA FRAMES
It may be a bit confusing that matrices cannot contain both character and numeric values. From week 1, we read in text and excel files and they contained mixtures of character and numeric variables. This is because we used data frames, which can contain both numeric and character values. The only caveat is that the variables in a column must be the same type. For example, a column can consist of either all character/string variables OR numeric values NOT BOTH. So how do we create a data frame? One way is to read in an external file like we did in weeks 1 and 2. Another way is coercing an existing object into a data frame. If you are thinking “I can use as.data.frame” you deserve a gold star… or maybe a beer. Let’s try this with the trial.n.error matrix.
## print out matrix as a reminder
trial.n.error
#create my.dater data frame by coercing trial.n.error
my.dater<- as.data.frame(trial.n.error)
# print it out
my.dater
#what classs of object
class(my.dater)
# is my.dater a data frame-- yes!
is.data.frame(my.dater)
# is my.dater a matrix -- no!
is.matrix(my.dater)
So now you have a data frame with default column names V1, V2, and V3. We can change the names of the columns to something more meaningful using the colnames function.
#change column names in my.dater to pixie, dixie, and bud
colnames(my.dater) = c("pixie", "dixie", "bud")
#print it out
my.dater
Recall in week 1 that we can refer to individual columns in a data frame using the $ syntax for example:
#print out the second column dixie in my.data
my.dater$dixie
#we can also refer to individual columns in a data frame using brackets
#print out the second column dixie in my.data
my.dater[,2]
So, you can specify elements in a data frame using column names and using a row and column index inside a bracket [], just like a matrix. You should also have noticed something in the console output.
> my.dater[,2]
[1] 1 2 3 4
Levels: 1 2 3 4
The “Levels” thing at the bottom. What the heck is that? It indicates that the R considers the variable type in the dixie column to be a factor, which is a class variable used in an analysis. For example, you might use a factor variable in an ANOVA to represent study site when you test for differences among study sites. We discuss more use of factors later, for now it’s important to note that a factor is basically treated like a character variable in that they are not numbers and cannot be treated as numbers. For example, you cannot add them. To convince yourself, add the columns dixie and bud:
#try to add dixie and bud columns in my.dater
my.dater$dixie + my.dater$bud
you should get
[1] NA NA NA NA
Warning message:
In Ops.factor(my.dater$dixie, my.dater$bud) : + not meaningful for factors
We can determine if a variable is numeric or factor using the class command or an “is.” function.
# whatclass is dixie
class(my.dater$dixie)
#is dixie numeric- no!
is.numeric(my.dater$dixie)
#is dixie a factor- yes!
is.factor(my.dater$dixie)
Commonly Encountered Problems (CEP): Note that R sometimes changes numeric variables into factors when you read in a dataset. You will be unable to perform numeric operations on these factor variables and you will be quite frustrated! Bottom line: check the data in each column and make sure it is the correct type.
The only way we can change these to numbers is to coerce them. Let’s try first using what you may expect to use “as.numeric”
# first print out bud
my.dater$bud
# try to coerce bud to a numeric value
as.numeric(my.dater$bud)
Notice that it changed the values 5,6,7,8 to 1,2,3,4. This is a bad thing ---> so here’s is the bottom line:
IMPORTANT POINT: when coercing factors to numeric values-- you must first coerce them to character variables, then coerce the character variables to numeric variables
The correct way to change numbers is to coerce them, so first “as.character” then “as.numeric”
# print out bud
my.dater$bud
# now coerce as character then to coerce bud to a numeric value
as.numeric(as.character(my.dater$bud))
Now let’s fix both dixie and bud columns, be sure to check the class.
# now coerce as character then to coerce bud to a numeric value
my.dater$bud <- as.numeric(as.character(my.dater$bud))
# whats the class-- numeric!
class(my.dater$bud)
# is it numeric--yes!
is.numeric(my.dater$bud)
# now coerce as character then to coerce dixie to a numeric value
my.dater$dixie <- as.numeric(as.character(my.dater$dixie))
# whats the class--numeric!
class(my.dater$dixie)
# is it numeric--yes!
is.numeric(my.dater$dixie)
Wasn’t that bloody good fun?
LISTS
Lists are the final types of R objects that you will commonly encounter. They also are the most frustrating and (at least for beginners) difficult to work. Lists can contain objects of various types, numeric, character and lists are the default output that is created when you conduct a statistical analysis, such as linear regression. We will discuss using lists resulting from statistical analysis later in the course. For now, let’s go over some of the basics with lists. To create a list we use the.. (wait for it)… list function. Let’s create several different types of objects and combine them in a list.
# create a numeric vector with a sequence of values from 1.25 to 8.75 by increments of 0.25 and print
num.vct <- seq(1.25, 8.75, by = 0.25)
num.vct
# create 3 by 4 numeric matrix with a sequence of values from 1 to 6.5 by 0.5 and print
num.mtrx <- matrix(seq(1, 6.5, by = 0.5), ncol = 4, byrow = FALSE)
num.mtrx
# create a character vector with a through z and print
char.vct <- letters[1:10]
char.vct
# create 2 by 2 numeric with peoples names and print
char.mtrx <- matrix(c("bill", "mary","joe","brenda"), ncol = 2, byrow = FALSE)
char.mtrx
## create a list that contains all of these objectives
big.list <- list(num.vct,char.mtrx,num.mtrx,char.vct)
## create a name for each object within the big.list and print
names(big.list) <- c("vect_numbrs", "names", "numb_matrx","letters")
big.list
Let’s find out what type of object we just created.
## what class is this object
class(big.list)
# what type of object
typeof(big.list)
## new very important function for lists
str(big.list)
The last of these functions str() is particularly important and provides information on the objects within the list and the names of the objects, so let’s examine the output:
List of 4
$ vect_numbrs: num [1:31] 1.25 1.5 1.75 2 2.25 2.5 2.75 3 3.25 3.5 ...
$ names : chr [1:2, 1:2] "bill" "mary" "joe" "brenda"
$ numb_matrx : num [1:3, 1:4] 1 1.5 2 2.5 3 3.5 4 4.5 5 5.5 ...
$ letters : chr [1:10] "a" "b" "c" "d" ...
The above was output to the console resulting from str(big.list). It tells us that big.list contains 4 objects (List of 4), and each object has a name (names are after $). The first ($ vect_numbrs) consists of numbers in a vector with 31 elements (num [1:31]), the second ($ names) consists of a 2 by 2 matrix of characters variables (chr [1:2, 1:2]), the third object ($ numb_matrx) is a 3 by 4 matrix of numeric variables (num [1:3, 1:4]), and the 4th object ($ letters) is a character vector with 10 elements (chr [1:10]).
We can access the elements of a list using syntax similar to that is used to access elements within a data frame, using $ and brackets [].
# to access the ‘names’ object in big.list
big.list$names
# to access the names object the 2nd one in big.list
big.list[2]
Hmm looks too easy, right? Better check on the characteristics or each of these objects.
# what is the class of object created using $ in big.list
class(big.list$names)
# what is the class of object created using [] syntax in big.list
class(big.list[2])
Woa! The first method ($) produces a matrix and the second a list. This is important because you can’t really do much with a list in terms of common operations (e.g., addition, multiplication, etc.) but you can with matrices and vectors. To illustrate, try multiplying the first object in the list, vect_numbrs, by 5.
#multiply the elements in the first object within the list by 5
big.list$vect_numbrs*5
#multiply the elements in the first object within the list by 5
big.list[1]*5
You should have noticed that the first methods resulted in products, whereas the second method produces the error message:
Error in big.list[1] * 5 : non-numeric argument to binary operator
The importance of accessing objects in a list will be very apparent when we start using the output from statistical analysis, such as plotting residuals.
Week 3 Assignment
Due 1 week from today by 5pm Pacific. This R script creates a list "HW.list" that contains 3 objects: players, stock.portfolio, vehicle, and junk. Complete the following:
1) Create a data frame using players
2) Name the first column in the data frame "person" and the second column "salary"
3) Create a new column in that data frame named "total.income" by adding salary to stock.portfolio
4) Write the data frame created in steps 1-3 to a comma separated file
5) Create a 4 by 2 matrix using vehicle
Please save all of the code you used in a single script and submit the script and the comma separated file in an email attachment to both instructors.
BONUS MATERIAL (embrace yo' inner geek)
Working with matrices in R
Matrices are the backbone of most statistical analyses and are an essential tool in the quantitative ecologists toolbox. Here we cover a few neat tricks with matrices that may come in handy later.
### First create a 4 row by 5 column matrix
MTX <- matrix(c(1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4), ncol = 5, byrow = T)
# Print it out
MTX
## Transpose the matrix
# notice rows are columns and columns are rows
t(MTX)
# create a scalar
A <- 0.5
# multiply the matrix by a scalar
MTX*A
# multiplicity matrix by another scalar
MTX*10
## create a vector
V = c(10,1,0.1,0.01)
## multiply the matrix by a vector
## WARNING THIS IS NOT MATRIX MULTIPLICATION
MTX*V
# To illustrate matrix multiplication we first create an identity
# matrix. This is a matrix with 1's along a diagonal from the top
# left to bottom right
IDENT = matrix(c(1,0,0,0,1,0,0,0,1), ncol= 3)
IDENT
# Identity matrices are super useful in quantitative applications so
# you've probably used them and didn't know, Ok, next we create
# a vector that has the number of elements equal to the number of
# columns in the matrix
V.new = c(1,2,3)
# matrix multiplication is specified using %*%
# so we have
IDENT %*% V.new
## it should have returned a column vector 1,2,3
# what happens if we just use the regular multiplication operator, *
IDENT * V.new
2*10 + 5*15 = 95
3*10 + 6*15 = 120
4*10 + 7*15 = 145
results in the the vector:
## check with a little R code
c = matrix(c(2,3,4,5,6,7), ncol = 2)
z = c(10,15)
c %*% z
whereas regular multiplication with matrices in R works this way
## check with a little R code
c * z
Why should we care? Well, matrices and matrix multiplication is one of the essential tools for the quantitative ecologists- in particular we use them to model populations with Leslie / Lefkovitch matrices Consider a simple population with age/ stage classes:
# per capita fecundity juvenile
Fj = 1.1
# per capita fecundity adult
Fa = 2.1
#survival age 0
S0 = 0.25
#survival juvenile
Sj = 0.35
#survival adult
Sa = 0.75
We create the population transition matrix
trans.mtrx = matrix(c(0,Fj,Fa,S0,0,0,0,Sj,Sa), ncol = 3, byrow = T)
#print it out
trans.mtrx
We create the population vector
#Number of animals in each age class
N0 = 100
Nj = 50
Na = 200
Nt = c(N0,Nj,Na)
## whats the population estimate for next year?
trans.mtrx %*% Nt
R has several built-in functions for manupulating and evaluating matrices for population models we can perform an eigen analysis using eigen() function the first element in the list created by the function is the eigenvalue also known as lambda, the population growth rate lambda:
#eigen analysis of population transition matrix
eigen(trans.mtrx)$values[1]
## stable age distribution
eigen(trans.mtrx)$vectors[,1]/sum(eigen(trans.mtrx)$vectors[,1])
One more built-in function to show you grabs the diagonal elements of a matrix diag, it could come in handy later, see week 8 and using the variance covariance matrix from a linear model
# we'll use the identity matrix
IDENT
# grab the diagonal elements
diag(IDENT)