Let’s begin with arrays. The next command creates a 3-dimensional array:
a <- array(data = 1:12, dim=c(3,2,2))
The numbers 1 through 12 are arranged in the following order: a111 = 1, a211, a311, a121, a221, a321, a112, a212, a312, a122, a222, a322 = 12.
The next command extracts an element of the array:
a[2,1,2]
## [1] 8
All elements in an array must be of the same data type. If you intended some of the elements of your array to be numbers and others to be characters, R will treat all elements as characters.
a2 <- array(data = 1:12, dim=c(3,2,2), dimnames = list(c("Row 1", "Row 2", "Row 3"), c("Col 1", "Col 2"), c("Table 1", "Table 2")))
This may help with subsetting:
a2["Row 1",, "Table 2"]
## Col 1 Col 2
## 7 10
a2["Row 1", "Col 1", "Table 2"]
## [1] 7
LETTERS[1:8]
## [1] "A" "B" "C" "D" "E" "F" "G" "H"
is.array(LETTERS[1:8])
## [1] FALSE
is.array(as.array(LETTERS[1:8]))
## [1] TRUE
is.array(a)
## [1] TRUE
Note that as.array(LETTERS[1:8]) converts a non-array object into an array object.
So far, I have not used arrays in any actual data work.
A matrix is simply a 2-dimensional array.
The next command chunk creates a matrix:
year <- c(2008,2009,2010,2011,2012,2013)
product1 <- c(0,3,6,9,7,8)
product2 <- c(1,2,3,5,9,6)
product3 <- c(2,4,4,2,3,2)
formales <- c(T, F, F, F, T, F)
naam <- c("Atif", "Benazir", "Caifi", "Dilrubba", "Enayat", "Fareed")
mymat1 <- cbind(year,product1,product2,product3)
mymat2 <- cbind(year,product1,product2,product3,formales)
mymat3 <- cbind(year,product1,product2,product3,formales,naam)
Now, if you use the class() command, you will see that product1, product2, product3 are numeric objects, formales is a logical object, and naam is a character object. If you run class()on mymat1, mymat2, and mymat3 you’ll see that they are all matrices. But if you run the summary() command, you will see that when combining objects of different data types into a matrix, R smooshes them into the same data type. mymat1 is a numeric matrix. But mymat3 is a character matrix, with all its constituent objects converted to the character type and then combined. mymat2 is a numeric matrix; the T’s and F’s in the logical object formales are converted to 1’s and 0’s and then combined with the other constituent objects of mymat2.
So, what do you do if you wish to combine objects of different data types into one object without reducing their separate data types to a lowest-common-denominator data type?
You make a data frame!
mydf3 <- data.frame(year,product1,product2,product3,formales,naam)
Compare summary(mymat3) and summary(mydf3) to see the difference. More on data frames in another article.
This creates a matrix with 10 rows and 9 columns.
x <- matrix(data = 1:90, nrow = 10)
nrow(x) # 10
## [1] 10
ncol(x) # 9
## [1] 9
class(x) # "matrix" "array"
## [1] "matrix" "array"
typeof(x) # "integer"
## [1] "integer"
Next, I create a matrix with 9 rows and 10 columns.
xtransposed <- matrix(data = 1:90, ncol = 10, byrow = TRUE)
diag()Here’s a 3X3 matrix with 1’s on the diagonal and 0’s elsewhere. In other words, this is a 3X3 identity matrix.
I <- diag(x = 1, nrow = 3, ncol = 3)
Omitting ncol = 3 does not change the result, but omitting nrow = 3 does!
We can give I a new diagonal:
diag(I) <- c(4,11,23) # Changes the diagonal terms in I from (1,1,1) to (4,11,23)
Here’s a 3-by-4 matrix with 1, 2, and 3 along the diagonal that starts from the first number, and with zeros elsewhere:
J <- diag(c(1,2,3), nrow = 3, ncol = 4)
x[2, 4] outputs the element of x that sits in the 2nd row and 4th column. class(x[2, 4]) is “integer”, indicating that we have an 1-element vector, not a matrix. x[c(2, 4), 4] outputs x[2, 4] and x[4, 4] as a 2-element vector, not a matrix. x[2, ] outputs the 2nd row as an integer vector. x[c(1,3), ] outputs the 1st and 3rd rows. x[, 3] outputs the 3rd column as an integer vector. diag(x) outputs the diagonal of a matrix x as a vector.
The next command extracts a 2X2 matrix from x:
xsmall <- x[c(2, 4), c(4, 7)]
class(xsmall) # "matrix" "array"
## [1] "matrix" "array"
Note that: xsmall[1,1]=x[2,4], xsmall[1,2]=x[2,7] xsmall[2,1]=x[4,4], xsmall[2,2]=x[4,7]
The next code chunk creates smaller matrices out of the matrix x.
y <- 1:6
z <- 1:7
x3 <- x[y, z] # A matrix of the first 6 rows and first 7 columns of x
x4 <- x[y[y > 2], z[z < 4]] # A matrix of rows 3-to-6 and columns 1-to-3 of x
x*pi # Multiplies every element of `x` by pi = 3.141593 ..
x^2 # Squares every number in `x`
a <- matrix(c(1,0,4,2,-1,1), nrow = 3) # A 3X2 matrix
b <- matrix(c(1,-1,2,1,1,0), nrow = 2) # A 2X3 matrix
a[1,]*b[,1] # Multiply the i^th^ element of row 1 of `a` and the i^th^ element of column 1 of `b`.
## [1] 1 -2
sum(a[1,]*b[,1]) # Add the results of the previous step. Inner product done!
## [1] -1
c = a %*% b # Here's `a` post-multiplied by `b`. This yields a 3 by 3 matrix.
d = b %*% a # This is `a` pre-multiplied by `b`. This yields a 2 by 2 matrix.
det(d) # Calculates the determinant of the square matrix d
## [1] -14
d[2,] <- 3*d[2,] # Multiplies the 2nd row of d by 3
det(d) # Show that the determinant has also been multiplied by 3
## [1] -42
c[,2] <- 0 # Now all elements of the 2^nd^ column of c are zeroes
det(c) # The determinant should now be zero
## [1] 0
LinDepMatrix <- matrix(c(1,2,3,6), nrow = 2) # Linearly dependent matrix
det(LinDepMatrix) # 0, as expected
## [1] 0
solve(d) # Calculates the inverse of the square matrix d
## [,1] [,2]
## [1,] 0.21428571 0.02380952
## [2,] -0.07142857 -0.11904762
library(MASS) # Loads the MASS package, for matrix operations
ginv(d) # Calculates the inverse of the square matrix d
## [,1] [,2]
## [1,] 0.21428571 0.02380952
## [2,] -0.07142857 -0.11904762
ginv(ginv(d)) # Should return the matrix d
## [,1] [,2]
## [1,] 5 1
## [2,] -3 -9
d %*% ginv(d) # Should give the identity matrix
## [,1] [,2]
## [1,] 1.000000e+00 -1.387779e-17
## [2,] 1.110223e-16 1.000000e+00
det(ginv(d))*det(d)
## [1] 1
This confirms that the determinant of a matrix is the reciprocal of the determinant of the inverse of that matrix.
eigen(d) # Calculates the eigenvalues and eigen vectors of d as a list
## eigen() decomposition
## $values
## [1] -8.78233 4.78233
##
## $vectors
## [,1] [,2]
## [1,] -0.07236644 0.9771198
## [2,] 0.99737811 -0.2126897
eigen(d)$values # Show the eigen values as a numeric vector
## [1] -8.78233 4.78233
eigen(d)$vectors # Show the eigen vectors as a matrix
## [,1] [,2]
## [1,] -0.07236644 0.9771198
## [2,] 0.99737811 -0.2126897
t(a) # Transpose of the matrix a
## [,1] [,2] [,3]
## [1,] 1 0 4
## [2,] 2 -1 1
a %*% t(a)
## [,1] [,2] [,3]
## [1,] 5 -2 6
## [2,] -2 1 -1
## [3,] 6 -1 17
This shows that the matrix product of a matrix and its transpose is a symmetric matrix.
solveHere we solve a set of two simultaneous linear equations: Equation 1: 3x + 4y = 12 Equation 2: x + 2y = 8
A <- matrix(c(3,1,4,2), nrow = 2) # Defines the matrix of coefficients of x and y
V <- matrix(c(12,8), nrow = 2) # Defines the Right-Hand-Side numbers
solve(A,V) # x = -4 and y = 6
## [,1]
## [1,] -4
## [2,] 6
That simple!
As we saw before, solve(A) outputs the inverse of the matrix A, when it is a numeric square matrix. That is, when V is omitted from solve(A,V), the identity matrix (of the relevant dimension) is substituted for V.
Matrix package is useful for matrix calculations.library(Matrix)
qr(d)$rank # Rank, as part of a list called the QR decomposition
## [1] 2
rankMatrix(a) # Rank
## [1] 2
## attr(,"method")
## [1] "tolNorm2"
## attr(,"useGrad")
## [1] FALSE
## attr(,"tol")
## [1] 6.661338e-16
Check that the next two lines give the same matrix:
t(a %*% b)
## [,1] [,2] [,3]
## [1,] -1 1 3
## [2,] 4 -1 9
## [3,] 1 0 4
t(b) %*% t(a)
## [,1] [,2] [,3]
## [1,] -1 1 3
## [2,] 4 -1 9
## [3,] 1 0 4
Test whether A is a lower-triangular matrix. The result will be a matrix with all elements TRUE if A is lower-triangular.
A == A*lower.tri(A, diag = TRUE)
## [,1] [,2]
## [1,] TRUE FALSE
## [2,] TRUE TRUE
Similarly, for upper-triangular matrices.
Create a symmetric matrix and check whether it is symmetric.
(sym <- matrix(c(1,8,8,1), nrow = 2)) # Define the matrix sym
## [,1] [,2]
## [1,] 1 8
## [2,] 8 1
isSymmetric(sym) # Will be TRUE if sym is symmetric
## [1] TRUE
matrxcalc package is useful for matrix calculations.library(matrixcalc)
## Warning: package 'matrixcalc' was built under R version 4.0.3
is.idempotent.matrix(sym) # TRUE if sym is an idempotent matrix
## [1] FALSE
is.non.singular.matrix(sym) # TRUE if sym is a non-singular matrix
## [1] TRUE
lower.triangle(c) # Zeroes out the upper-triangle elements of `c`
## [,1] [,2] [,3]
## [1,] -1 0 0
## [2,] 1 0 0
## [3,] 3 0 4