Matrices & Arrays

# Like vectors, matrices store a collection of scalar-elements of the same type
# (double, logicals or strings).  Unlike vectors, they do so along two (rather
# than one) dimension.


Creation

mA = matrix(NA, ncol = 3, nrow = 2)  # matrix of NAs (to be filled-in)

# create a matrix by filling-in elements of a vector:
vx = c(4.3, 5, 6, 3, 4.5, 9.2)
# by column:
mA <- matrix(vx, ncol = 3, nrow = 2)
# by row:
mA <- matrix(vx, ncol = 3, nrow = 2, byrow = TRUE)
# As you can verify, mA is of type 'double' (and 'numeric') as opposed to
# 'logical' or 'character' (string):


typeof(mA)
is.double(mA)
is.numeric(mA)
# It is also a matrix rather than a vector:

is.matrix(mA)
is.vector(mA)
# We can use the following command to vectorize the matrix mA (i.e. turn it
# into a vector by stacking its columns on top of each other) or to ensure that
# a $n \times 1$ matrix is treated as a vector by R:


as.vector(mA)
## [1] 4.3 3.0 5.0 4.5 6.0 9.2
mB <- matrix(1:4, ncol = 3, nrow = 4)
is.vector(mB)
## [1] FALSE
mB_ <- as.vector(mB)
is.vector(mB_)
## [1] TRUE
# Further ways to create a matrix:


diag(3)  # 3x3 identity matrix

diag(c(3, 2))  # 2x2 diagonal matrix with 3 and 2 on diagonal
# But: Note: when applied to an existing, symmetric matrix, diag() returns its
# diagonal:
mA = matrix(1:9, ncol = 3, nrow = 3)
diag(mA)


# Create a matrix by putting the same vector in each column:
vx = c(1, 5, 3, 6)
mA = replicate(5, vx)


# Create a matrix as the Cartesian product of two vectors:
vx = seq(0, 0.1, 0.02)
vy = seq(0.1, 0.3, 0.1)
mB = expand.grid(column1 = vx, column2 = vy)

# To display the matrix, you can use the commands 'show' and 'print', as
# before.  Also, in contrast to scalars and vectors, you can click on the
# matrix in the environment window, which opens up a separate panel in the
# script-window to show you the matrix.
# We can, in principle also create a matrix of logicals or strings:

matrix(c(TRUE, FALSE, FALSE, FALSE, TRUE, FALSE), ncol = 3, nrow = 2)
matrix(c("a", "b", "c", "d", "e", "f"), ncol = 3, nrow = 4)

replicate(3, c(TRUE, FALSE))
replicate(3, c("a", "b"))

# This is rarely done in practice.  However, matrices of logicals or strings
# can arise when applying a function to a matrix (of numbers).  For example,
# matrices of logicals arise when comparing elements of a matrix to some
# number.


Indexing

mA = matrix(1:9, ncol = 3, nrow = 3)

dim(mA)  #display number of rows and columns of matrix mA
nrow(mA)
ncol(mA)

colnames(mA) = c("a", "b", "c")  #label columns of matrix
rownames(mA) = c("r1", "r2", "r3")


# Again, three ways of indexing possible:

# 1. 'Direct'

mA[1, 2]  # access element (1,2)

mA[1, ]  # access first row, returns vector

mA[, 1]  # access first column, returns vector

mA[c(1, 2), c(1, 3)]  # access specified rows and columns

# 2. Using logicals:

mA[c(TRUE, FALSE, FALSE), c(FALSE, TRUE, FALSE)]

mA[c(TRUE, FALSE, FALSE), ]

# 3. Using row/column names:

mA["r1", "b"]
mA["r1", ]

# And, with two dimensions available, we can mix these indexing approaches:

mA[1, "b"]

mA[c(TRUE, FALSE, FALSE), "b"]

# ...


Basic Operations

mA = matrix(c(1, 3, 8, 8), ncol = 2, nrow = 2)
mB = matrix(c(1, 2, 7, 1), ncol = 2, nrow = 2)


mA * mB  # element-wise multiplication

mA %*% mB  # matrix multiplication

t(mA)  # transpose

solve(mA)  # inverse
solve(mA, tol = 1e-23)  # if almost singular
# often, need to convert : as.numeric()


library(Matrix)

rankMatrix(mA)  # compute rank

eigen(mA)  # compute eigenvalues and eigenvectors;
eigen(mA)$values
eigen(mA)$vectors
# Many of the functions applied above to numbers and vectors can also be
# applied to matrices: e.g.

sqrt(mA)  # compute square-root for each element of matrix

min(mA)  # compute minimum out of all elements of matrix

mean(mA)  # mean of all elements in matrix

mA >= 2  # element-wise comparison, returns matrix of logicals
# Some functions only make sense when their single input is a matrix (not a
# vector or a scalar):


cov(mA)  # covariance between columns
cor(mA)  # correlation between columns
# A particularly useful function for matrices is 'apply'.  It allows us e.g. to
# apply a function designed for a vector to each column or row of a matrix
# separately:

mA = matrix(c(1, 3, 2, 8), ncol = 2, nrow = 2)

# for each column (=2nd dimension) of mA, compute the mean of elements:
apply(mA, 2, mean)
# (i.e. apply to object mA, on dimension 2 (i.e. by columns), the function
# 'mean'

# One can also supply a self-written function to 'apply':
fDemean = function(vx) {
    vx - mean(vx)
}
apply(mA, 2, fDemean)
# or, in short:
apply(mA, 2, function(vx) {
    vx - mean(vx)
})


# This is useful even for functions applied to each element of a matrix:
apply(mA, c(1, 2), function(x) {
    min(2, x)
})  # since min(mA,x) doesn't give the hoped for result


Arrays

# Arrays generalize matrices, as they store a collection of scalar-elements of
# the same type (double, logicals or strings) along a general number of
# dimensions (more than 2).  They are useful for efficiently storing data that
# differ along several dimensions (e.g. countries, years, variables, ...) and
# accessing subsets of it.

# Create three-dimensional array of NAs:
aY = array(dim = c(2, 3, 2))

# Create three-dimensional array by using a vector to 'fill it up':
aX = c(1:12)
dim(aX) = c(2, 3, 2)
print(aX)
## , , 1
## 
##      [,1] [,2] [,3]
## [1,]    1    3    5
## [2,]    2    4    6
## 
## , , 2
## 
##      [,1] [,2] [,3]
## [1,]    7    9   11
## [2,]    8   10   12
# Indexing of arrays:

aX[1, 3, 2]
aX[, c(2:3), 1]

aX[1, c(2:3), ]  # returns matrix (2 x 2)
aX[1, c(2:3), , drop = FALSE]  # returns array (1 x 2 x 2)

# Can index similarly by using logical-vectors for each dimension, or by using
# the names of the elements in the different dimensions:


# Name dimensions of array:

dimnames(aX)[[1]] = c("r1", "r2")
dimnames(aX)[[2]] = c("a", "b", "c")
dimnames(aX)[[3]] = c("A", "B")
# Functions that operate on scalars can be used on arrays as well:

# Functions:

sqrt(aX)  # compute square-root of each element

# There are (to my knowledge) no built-in functions that require a single array
# as an argument.
# We can use the function 'apply' to apply a function along different
# dimensions of an array:

# for each row and column, compute sum along third dimension:
apply(aX, c(1, 2), sum)

# for each row, compute minimum of resulting matrix:
apply(aX, 1, min)