Goals

After working on this handout, you should: * be able to create, fill, and access tensors and lists in R

Types of objects for storing information

Up to this point, we have stored vaules in vectors (1 dimensional) and matrices (2 dimensional). These object types are very useful, but sometimes you will want objects with a greater number of dimensions or more flexibility in the structures and data types that comprise it. Tensors and lists fill these roles.

Tensors

Tensors (also known as arrays in R) are simply a generalization of matrices to an arbitrary number of dimensions (think of our floor-building-block model). You can think of vectors and matrices as 1- and 2 -dimensional tensors, but 3- and 4- (and beyond) dimensional tensors also exist. If we think of a 2-dimensional tensor as a square, you can imagine a 3-dimensional tensor as a cube. Further dimensions are hard to visualize, but nonetheless are useful for data analysis. For our purposes, just think of each dimension as another axis that can be indexed with coordinates. Let’s look at some examples.

#R calls tensors 'arrays'. Because of this, we use the array() command to create a tensor.
#Here, I'm making a 2-dimensional tensor, basically a matrix, filled with NA. Note how, 
#as we've seen elsewhere, R assumes the first dimension is rows, and the second is columns 
A <- array(NA, dim = c(5,7))
A
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,]   NA   NA   NA   NA   NA   NA   NA
## [2,]   NA   NA   NA   NA   NA   NA   NA
## [3,]   NA   NA   NA   NA   NA   NA   NA
## [4,]   NA   NA   NA   NA   NA   NA   NA
## [5,]   NA   NA   NA   NA   NA   NA   NA
#This array (tensor) behaves just like a matrix. Let's try something we've done to matrices to test this
A[1, 2] <- 3.4
A
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,]   NA  3.4   NA   NA   NA   NA   NA
## [2,]   NA   NA   NA   NA   NA   NA   NA
## [3,]   NA   NA   NA   NA   NA   NA   NA
## [4,]   NA   NA   NA   NA   NA   NA   NA
## [5,]   NA   NA   NA   NA   NA   NA   NA
#Now let's make a 3-dimensional array with dimension lengths of 3, 4, and 7. We'll fill this with the numbers 
#1 to 84
A <- array(1:84, dim = c(3, 7, 4))
#Note the order that R fills values. First along the first dimension, then the second, then the third
#Additionally, note that the number of elements in an array is the product of its dimension lenths.
A
## , , 1
## 
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,]    1    4    7   10   13   16   19
## [2,]    2    5    8   11   14   17   20
## [3,]    3    6    9   12   15   18   21
## 
## , , 2
## 
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,]   22   25   28   31   34   37   40
## [2,]   23   26   29   32   35   38   41
## [3,]   24   27   30   33   36   39   42
## 
## , , 3
## 
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,]   43   46   49   52   55   58   61
## [2,]   44   47   50   53   56   59   62
## [3,]   45   48   51   54   57   60   63
## 
## , , 4
## 
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,]   64   67   70   73   76   79   82
## [2,]   65   68   71   74   77   80   83
## [3,]   66   69   72   75   78   81   84
#We can take slices of the array the same way we do with a matrix. Let's take everything from the 
#second element of the third dimension (from the second 'floor' if you will)
A[, , 2]
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,]   22   25   28   31   34   37   40
## [2,]   23   26   29   32   35   38   41
## [3,]   24   27   30   33   36   39   42
#Now let's take a different slice, everything from the third element of the second dimension. Or,
#the third column from every 'floor'
A[, 3 ,]
##      [,1] [,2] [,3] [,4]
## [1,]    7   28   49   70
## [2,]    8   29   50   71
## [3,]    9   30   51   72
#finally, let's grab a vector that is the first element of the first and third dimensions
A[1, ,1]
## [1]  1  4  7 10 13 16 19
#We can also use indexing to fill a tensor. Here, I will generate four matrices 
#using a for loop. Each matrix will be assigned to the four elements along the third dimension of A. In other
#words, we will stack one floor on top of the next.
for (i in 1:4) {
## create matrix
  mat <- matrix(rnorm(21, mean = 2, sd = 0.1), nrow = 3, ncol = 7)
  A[, , i] <- mat
}

A
## , , 1
## 
##          [,1]     [,2]     [,3]     [,4]     [,5]     [,6]     [,7]
## [1,] 2.082508 1.775379 1.900184 1.978349 1.967867 1.974810 1.925818
## [2,] 1.875089 2.080525 1.931078 1.936744 2.001214 1.970139 1.935788
## [3,] 2.128412 2.217703 2.087220 1.949163 2.078632 1.986336 1.844137
## 
## , , 2
## 
##          [,1]     [,2]     [,3]     [,4]     [,5]     [,6]     [,7]
## [1,] 1.892664 1.774546 2.008168 2.073964 1.868171 2.017857 1.838353
## [2,] 1.978101 2.213554 1.777871 1.928310 2.069382 1.901619 1.871504
## [3,] 1.932124 1.765178 2.050473 1.922234 2.056581 1.948967 2.079233
## 
## , , 3
## 
##          [,1]     [,2]     [,3]     [,4]     [,5]     [,6]     [,7]
## [1,] 2.262176 2.013616 2.175552 1.988582 2.062292 2.166319 2.182465
## [2,] 1.970632 2.032926 2.012937 2.020803 1.892967 1.940264 2.053814
## [3,] 2.032141 2.166490 2.049079 1.869973 1.930267 2.114866 1.880161
## 
## , , 4
## 
##          [,1]     [,2]     [,3]     [,4]     [,5]     [,6]     [,7]
## [1,] 2.174173 1.892850 1.875712 2.083905 2.070029 2.198431 2.089437
## [2,] 1.895169 1.959895 1.926443 1.864837 1.964779 2.063132 2.030474
## [3,] 1.931812 1.929905 1.930950 2.129101 1.990599 2.088403 2.087005
#Just for fun, here's a 5-dimensional array with the numbers 1 to 3125. Each dimension is five 
#elements long. 
Z <- array(1:3125, dim = c(5, 5, 5, 5, 5))
#This is quite long, so I'll let you print this in your own console instead of showing it here. Type 
#Z into your console to see what it looks like. 

Lists

Lists are another useful structure in R. You can think of lists as flexible containers for other variable types. This can be useful for combining multiple data types associated with an object. Most kinds of data can be grouped together in a list, which can be rather useful. For example, simulating individual organisms. You may want to encode and store different types of data about that organism, such as age, some trait data, and its genome. Let’s see how we can do that using lists.

## create 3 objects containing different pieces of information
age <- 7
traits <- c(2.1, 42, 1.8)
genome <- c("A", "T", "T", "G", "C", "G", "A", "A")
## combine as a list note that we are assigning existing variables
## names in the list context
critter <- list(age = age, trt = traits, gen = genome)
critter
## $age
## [1] 7
## 
## $trt
## [1]  2.1 42.0  1.8
## 
## $gen
## [1] "A" "T" "T" "G" "C" "G" "A" "A"
#We can access individual elements by name or indexing 
critter$trt
## [1]  2.1 42.0  1.8
critter$age
## [1] 7
critter$gen[1]
## [1] "A"
#Access with single brackets returns a list that is a subset of the
#original list
critter[2:3]
## $trt
## [1]  2.1 42.0  1.8
## 
## $gen
## [1] "A" "T" "T" "G" "C" "G" "A" "A"
#Double brackets extracts a list element and returns it not as a list
critter[[2]]
## [1]  2.1 42.0  1.8
critter[[2]][3]
## [1] 1.8

You can also generate an empty list and fill in the components. These don’t need to be named, and you can add names after the fact. Let’s try it.

alist <- vector("list", 4)
alist
## [[1]]
## NULL
## 
## [[2]]
## NULL
## 
## [[3]]
## NULL
## 
## [[4]]
## NULL
#The first 3 elements in the list will be matrixes of random
#uniform numbers
for (i in 1:3) {
alist[[i]] <- matrix(runif(10, 0, 1), nrow = 5, ncol = 2)
}
#Then the last element will be text
alist[[4]] <- "This is a weird list."
alist
## [[1]]
##           [,1]       [,2]
## [1,] 0.7718437 0.12524516
## [2,] 0.7896573 0.32878587
## [3,] 0.2004596 0.76807005
## [4,] 0.3618162 0.61075843
## [5,] 0.7906570 0.05347214
## 
## [[2]]
##           [,1]      [,2]
## [1,] 0.2438261 0.4278841
## [2,] 0.5423544 0.4210741
## [3,] 0.5689953 0.2336868
## [4,] 0.9947121 0.9574889
## [5,] 0.3898530 0.9777987
## 
## [[3]]
##           [,1]      [,2]
## [1,] 0.5879822 0.6602587
## [2,] 0.6909880 0.4397373
## [3,] 0.2554381 0.1896617
## [4,] 0.8382739 0.7953803
## [5,] 0.9583266 0.7011732
## 
## [[4]]
## [1] "This is a weird list."
#Lets access the first matrix
alist[[1]]
##           [,1]       [,2]
## [1,] 0.7718437 0.12524516
## [2,] 0.7896573 0.32878587
## [3,] 0.2004596 0.76807005
## [4,] 0.3618162 0.61075843
## [5,] 0.7906570 0.05347214
#Now let's name the elements
names(alist) <- c("m1", "m2", "m3", "note")
#Now I can also use the $ notation to index the list
alist$note
## [1] "This is a weird list."

\[ \] \[ \] \[ \] \[ \]